Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion components/js-api-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,11 +304,13 @@ const hydrated = await cart.hydrate({
items: [{ sku: 'SKU-1', quantity: 1 }],
});

// Add/remove items and place the order
// Add/remove items, abandon or place and fulfill the cart and assign the orderId
await cart.addSkuItem(hydrated.id, { sku: 'SKU-2', quantity: 2 });
await cart.setCustomer(hydrated.id, { identifier: 'customer-123', email: 'john@doe.com' });
await cart.setMeta(hydrated.id, { merge: true, meta: [{ key: 'source', value: 'web' }] });
await cart.abandon(hydrated.id);
await cart.place(hydrated.id);
await cart.fulfill(hydrated.id, orderId);
```

## Signature verification (async)
Expand Down
2 changes: 1 addition & 1 deletion components/js-api-client/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@crystallize/js-api-client",
"license": "MIT",
"version": "5.1.0",
"version": "5.2.0",
"type": "module",
"author": "Crystallize <hello@crystallize.com> (https://crystallize.com)",
"contributors": [
Expand Down
46 changes: 46 additions & 0 deletions components/js-api-client/src/core/shop/create-cart-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ import { transformCartCustomerInput, transformCartInput } from './helpers.js';
type WithId<R> = R & { id: string };

export const createCartManager = (apiClient: ClientInterface) => {
const fetch = async <OnCart, OC = unknown>(id: string, onCart?: OC) => {
const query = {
cart: {
__args: {
id,
},
id: true,
...onCart,
},
};
const response = await apiClient.shopCartApi<{ cart: WithId<OnCart> }>(jsonToGraphQLQuery({ query }));
return response.cart;
};

const place = async <OnCart, OC = unknown>(id: string, onCart?: OC) => {
const mutation = {
place: {
Expand All @@ -28,6 +42,35 @@ export const createCartManager = (apiClient: ClientInterface) => {
return response.place;
};

const abandon = async <OnCart, OC = unknown>(id: string, onCart?: OC) => {
const mutation = {
abandon: {
__args: {
id,
},
id: true,
...onCart,
},
};
const response = await apiClient.shopCartApi<{ abandon: WithId<OnCart> }>(jsonToGraphQLQuery({ mutation }));
return response.abandon;
};

const fulfill = async <OnCart, OC = unknown>(id: string, orderId: string, onCart?: OC) => {
const mutation = {
fulfill: {
__args: {
id,
orderId,
},
id: true,
...onCart,
},
};
const response = await apiClient.shopCartApi<{ fulfill: WithId<OnCart> }>(jsonToGraphQLQuery({ mutation }));
return response.fulfill;
};

const addSkuItem = async <OnCart, OC = unknown>(id: string, intent: CartSkuItemInput, onCart?: OC) => {
const input = CartSkuItemInputSchema.parse(intent);
const mutation = {
Expand Down Expand Up @@ -124,6 +167,9 @@ export const createCartManager = (apiClient: ClientInterface) => {
return {
hydrate,
place,
fetch,
fulfill,
abandon,
addSkuItem,
removeItem,
setMeta,
Expand Down
87 changes: 62 additions & 25 deletions components/js-api-client/tests/shop/cart.test.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,50 @@
import { test, expect, describe, beforeAll } from 'vitest';
import { ClientInterface, createCartManager, createOrderFetcher, createOrderManager } from '../../src';
import { ClientInterface, createCartManager, createOrderManager } from '../../src';
import { createApiClient } from '../util';
import { AddressType, defaultCartContext } from '@crystallize/schema/shop';
import { defaultCartContext, CustomerInput, Cart } from '@crystallize/schema/shop';

describe('Cart Tests', () => {
let CrystallizeClient: ClientInterface;
let cartId: string;
let customerIdentifier = 'test-customer';
const items = [
{
sku: 'bishop-marble-normal',
name: 'Bamboo Chair',
quantity: 2,
},
];
const customer: CustomerInput = {
isGuest: true,
addresses: [
{
type: 'billing',
firstName: 'Test',
lastName: 'Customer',
},
],
identifier: 'test-customer',
firstName: 'Test',
lastName: 'Customer',
type: 'individual',
};

beforeAll(() => {
CrystallizeClient = createApiClient();
});

test('Hydrate a Cart', async () => {
test('Hydrate, place and fulfill a cart', async () => {
const orderManager = createOrderManager(CrystallizeClient);
const cartManager = createCartManager(CrystallizeClient);

const cart = await cartManager.hydrate<{
customer: {
lastName: string;
};
}>(
{
context: defaultCartContext,
customer: {
isGuest: true,
addresses: [
{
type: 'billing',
firstName: 'Test',
lastName: 'Customer',
},
],
identifier: 'test-customer',
firstName: 'Test',
lastName: 'Customer',
type: 'individual',
},
items: [
{
sku: 'bishop-marble-normal',
quantity: 2,
},
],
customer,
items,
},
{
customer: {
Expand All @@ -52,5 +57,37 @@ describe('Cart Tests', () => {
expect(cart).toHaveProperty('customer');
expect(cart.customer).toHaveProperty('lastName');
expect(cart.customer.lastName).toBe('Customer');
});

await cartManager.place(cart.id);

const order = await orderManager.register({
cart: items,
customer: {
identifier: customerIdentifier,
type: 'individual',
},
});

await cartManager.fulfill(cart.id, order.id);

const fetchedCart = await cartManager.fetch<Cart>(cart.id, {
state: true,
});

expect(fetchedCart.state).toBe('ordered');
}, 10000);

test('Hydrate and abandon a cart', async () => {
const cartManager = createCartManager(CrystallizeClient);

const cart = await cartManager.hydrate({
context: defaultCartContext,
customer,
items,
});

expect(cart).toHaveProperty('id');

await cartManager.abandon(cart.id);
}, 10000);
});