ActiveOrderStrategy
ActiveOrderStrategy
This strategy is used to determine the active Order for all order-related operations in
the Shop API. By default, all the Shop API operations that relate to the active Order (e.g.
activeOrder
, addItemToOrder
, applyCouponCode
etc.) will implicitly create a new Order
and set it on the current Session, and then read the session to obtain the active Order.
This behaviour is defined by the DefaultActiveOrderStrategy.
The InputType
generic argument should correspond to the input type defined by the
defineInputType()
method.
When defineInputType()
is used, then the following Shop API operations will receive an additional
activeOrderInput
argument allowing the active order input to be specified:
activeOrder
eligibleShippingMethods
eligiblePaymentMethods
nextOrderStates
addItemToOrder
adjustOrderLine
removeOrderLine
removeAllOrderLines
applyCouponCode
removeCouponCode
addPaymentToOrder
setCustomerForOrder
setOrderShippingAddress
setOrderBillingAddress
setOrderShippingMethod
setOrderCustomFields
transitionOrderToState
Example
mutation AddItemToOrder {
addItemToOrder(
productVariantId: 42,
quantity: 1,
activeOrderInput: { token: "123456" }
) {
...on Order {
id
# ...etc
}
}
}
Example
import { ID } from '@vendure/common/lib/shared-types';
import {
ActiveOrderStrategy,
CustomerService,
idsAreEqual,
Injector,
Order,
OrderService,
RequestContext,
TransactionalConnection,
} from '@vendure/core';
import gql from 'graphql-tag';
// This strategy assumes a "orderToken" custom field is defined on the Order
// entity, and uses that token to perform a lookup to determine the active Order.
//
// Additionally, it does _not_ define a `createActiveOrder()` method, which
// means that a custom mutation would be required to create the initial Order in
// the first place and set the "orderToken" custom field.
class TokenActiveOrderStrategy implements ActiveOrderStrategy<{ token: string }> {
readonly name = 'orderToken';
private connection: TransactionalConnection;
private orderService: OrderService;
init(injector: Injector) {
this.connection = injector.get(TransactionalConnection);
this.orderService = injector.get(OrderService);
}
defineInputType = () => gql`
input OrderTokenActiveOrderInput {
token: String
}
`;
async determineActiveOrder(ctx: RequestContext, input: { token: string }) {
const qb = this.connection
.getRepository(ctx, Order)
.createQueryBuilder('order')
.leftJoinAndSelect('order.customer', 'customer')
.leftJoinAndSelect('customer.user', 'user')
.where('order.customFields.orderToken = :orderToken', { orderToken: input.token });
const order = await qb.getOne();
if (!order) {
return;
}
// Ensure the active user is the owner of this Order
const orderUserId = order.customer && order.customer.user && order.customer.user.id;
if (order.customer && idsAreEqual(orderUserId, ctx.activeUserId)) {
return order;
}
}
}
// in vendure-config.ts
export const config = {
// ...
orderOptions: {
activeOrderStrategy: new TokenActiveOrderStrategy(),
},
}
interface ActiveOrderStrategy<InputType extends Record<string, any> | void = void> extends InjectableStrategy {
readonly name: string;
defineInputType?: () => DocumentNode;
createActiveOrder?: (ctx: RequestContext, input: InputType) => Promise<Order>;
determineActiveOrder(ctx: RequestContext, input: InputType): Promise<Order | undefined>;
}
- Extends:
InjectableStrategy
name
string
The name of the strategy, e.g. "orderByToken", which will also be used as the field name in the ActiveOrderInput type.
defineInputType
() => DocumentNode
Defines the type of the GraphQL Input object expected by the activeOrderInput
input argument.
Example
For example, given the following:
defineInputType() {
return gql`
input OrderTokenInput {
token: String!
}
`;
}
assuming the strategy name is "orderByToken", then the resulting call to activeOrder
(or any of the other
affected Shop API operations) would look like:
activeOrder(activeOrderInput: {
orderByToken: {
token: "foo"
}
}) {
# ...
}
Note: if more than one graphql input
type is being defined (as in a nested input type), then
the first input will be assumed to be the top-level input.
createActiveOrder
(ctx: RequestContext, input: InputType) => Promise<Order>
Certain mutations such as addItemToOrder
can automatically create a new Order if one does not exist.
In these cases, this method will be called to create the new Order.
If automatic creation of an Order does not make sense in your strategy, then leave this method undefined. You'll then need to take care of creating an order manually by defining a custom mutation.
determineActiveOrder
(ctx: RequestContext, input: InputType) => Promise<Order | undefined>
This method is used to determine the active Order based on the current RequestContext in addition to any
input values provided, as defined by the defineInputType
method of this strategy.
Note that this method is invoked frequently, so you should aim to keep it efficient. The returned Order, for example, does not need to have its various relations joined.