diff --git a/components/js-api-client/README.md b/components/js-api-client/README.md index b6c31413..8f0fcc68 100644 --- a/components/js-api-client/README.md +++ b/components/js-api-client/README.md @@ -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) diff --git a/components/js-api-client/package.json b/components/js-api-client/package.json index ca5fd9c0..2659a3fa 100644 --- a/components/js-api-client/package.json +++ b/components/js-api-client/package.json @@ -1,7 +1,7 @@ { "name": "@crystallize/js-api-client", "license": "MIT", - "version": "5.1.0", + "version": "5.2.0", "type": "module", "author": "Crystallize (https://crystallize.com)", "contributors": [ diff --git a/components/js-api-client/src/core/shop/create-cart-manager.ts b/components/js-api-client/src/core/shop/create-cart-manager.ts index 523ed8c2..bfabc40e 100644 --- a/components/js-api-client/src/core/shop/create-cart-manager.ts +++ b/components/js-api-client/src/core/shop/create-cart-manager.ts @@ -14,6 +14,20 @@ import { transformCartCustomerInput, transformCartInput } from './helpers.js'; type WithId = R & { id: string }; export const createCartManager = (apiClient: ClientInterface) => { + const fetch = async (id: string, onCart?: OC) => { + const query = { + cart: { + __args: { + id, + }, + id: true, + ...onCart, + }, + }; + const response = await apiClient.shopCartApi<{ cart: WithId }>(jsonToGraphQLQuery({ query })); + return response.cart; + }; + const place = async (id: string, onCart?: OC) => { const mutation = { place: { @@ -28,6 +42,35 @@ export const createCartManager = (apiClient: ClientInterface) => { return response.place; }; + const abandon = async (id: string, onCart?: OC) => { + const mutation = { + abandon: { + __args: { + id, + }, + id: true, + ...onCart, + }, + }; + const response = await apiClient.shopCartApi<{ abandon: WithId }>(jsonToGraphQLQuery({ mutation })); + return response.abandon; + }; + + const fulfill = async (id: string, orderId: string, onCart?: OC) => { + const mutation = { + fulfill: { + __args: { + id, + orderId, + }, + id: true, + ...onCart, + }, + }; + const response = await apiClient.shopCartApi<{ fulfill: WithId }>(jsonToGraphQLQuery({ mutation })); + return response.fulfill; + }; + const addSkuItem = async (id: string, intent: CartSkuItemInput, onCart?: OC) => { const input = CartSkuItemInputSchema.parse(intent); const mutation = { @@ -124,6 +167,9 @@ export const createCartManager = (apiClient: ClientInterface) => { return { hydrate, place, + fetch, + fulfill, + abandon, addSkuItem, removeItem, setMeta, diff --git a/components/js-api-client/tests/shop/cart.test.ts b/components/js-api-client/tests/shop/cart.test.ts index 50f70d0d..1dc7587e 100644 --- a/components/js-api-client/tests/shop/cart.test.ts +++ b/components/js-api-client/tests/shop/cart.test.ts @@ -1,18 +1,41 @@ 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; @@ -20,26 +43,8 @@ describe('Cart Tests', () => { }>( { 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: { @@ -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.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); });