import {
assertNoUserErrors,
clampInt,
moneyFromSet,
nodesFromConnection,
pageInfoFromConnection,
shopifyGraphql,
trimString,
} from './lib/client.ts'
import { MONEY_SET_FIELDS, PAGE_INFO_FIELDS } from './lib/fragments.ts'
import { toGid } from './lib/shop.ts'
import type { Money, ShopifyClientOptions, ShopifyPageInfo } from './types.ts'
export type DraftOrderSummary = {
id: string
name: string | null
status: string | null
email: string | null
createdAt: string | null
updatedAt: string | null
totalPrice: Money | null
invoiceUrl: string | null
}
function mapDraft(node: Record<string, unknown>): DraftOrderSummary {
return {
id: String(node.id || ''),
name: typeof node.name === 'string' ? node.name : null,
status: typeof node.status === 'string' ? node.status : null,
email: typeof node.email === 'string' ? node.email : null,
createdAt: typeof node.createdAt === 'string' ? node.createdAt : null,
updatedAt: typeof node.updatedAt === 'string' ? node.updatedAt : null,
totalPrice: moneyFromSet(node.totalPriceSet),
invoiceUrl: typeof node.invoiceUrl === 'string' ? node.invoiceUrl : null,
}
}
/**
* List draft orders.
*
* @example
* import { listDraftOrders } from 'kody:@kody/shopify/draft-orders'
* const { items } = await listDraftOrders({ shop: 'acme', query: 'status:open' })
*/
export async function listDraftOrders(
params: ShopifyClientOptions & {
query?: string
first?: number
after?: string
} = {},
) {
const first = clampInt(params.first, 1, 50, 20)
const data = await shopifyGraphql<{ draftOrders: unknown }>({
shop: params.shop,
apiVersion: params.apiVersion,
query: `query ListDraftOrders($first: Int!, $after: String, $query: String) {
draftOrders(first: $first, after: $after, query: $query) {
${PAGE_INFO_FIELDS}
nodes {
id
name
status
email
createdAt
updatedAt
invoiceUrl
totalPriceSet { ${MONEY_SET_FIELDS} }
}
}
}`,
variables: {
first,
after: trimString(params.after) || null,
query: trimString(params.query) || null,
},
})
return {
items: nodesFromConnection<Record<string, unknown>>(data.draftOrders).map(
mapDraft,
),
pageInfo: pageInfoFromConnection(data.draftOrders) as ShopifyPageInfo,
}
}
/**
* Get one draft order by GID or numeric id.
*/
export async function getDraftOrder(
params: ShopifyClientOptions & { id: string },
): Promise<DraftOrderSummary> {
const id = toGid('DraftOrder', params.id)
const data = await shopifyGraphql<{
draftOrder: Record<string, unknown> | null
}>({
shop: params.shop,
apiVersion: params.apiVersion,
query: `query GetDraftOrder($id: ID!) {
draftOrder(id: $id) {
id
name
status
email
createdAt
updatedAt
invoiceUrl
totalPriceSet { ${MONEY_SET_FIELDS} }
}
}`,
variables: { id },
})
if (!data.draftOrder) throw new Error(`Draft order not found: ${id}`)
return mapDraft(data.draftOrder)
}
export type CreateDraftOrderParams = ShopifyClientOptions & {
email?: string
note?: string
lineItems: Array<{
title?: string
originalUnitPrice?: string
quantity: number
variantId?: string
}>
dryRun?: boolean
}
/**
* Create a draft order. Defaults to dry-run; pass `dryRun: false` to write.
*/
export async function createDraftOrder(params: CreateDraftOrderParams) {
if (!params.lineItems?.length) {
throw new Error('createDraftOrder requires at least one line item.')
}
const input = {
email: trimString(params.email) || undefined,
note: trimString(params.note) || undefined,
lineItems: params.lineItems.map((item) => ({
title: trimString(item.title) || undefined,
originalUnitPrice: trimString(item.originalUnitPrice) || undefined,
quantity: item.quantity,
variantId: trimString(item.variantId)
? toGid('ProductVariant', item.variantId)
: undefined,
})),
}
if (params.dryRun !== false) {
return {
dryRun: true as const,
wouldCall: { mutation: 'draftOrderCreate', input },
}
}
const data = await shopifyGraphql<{ draftOrderCreate: unknown }>({
shop: params.shop,
apiVersion: params.apiVersion,
query: `mutation CreateDraftOrder($input: DraftOrderInput!) {
draftOrderCreate(input: $input) {
draftOrder {
id
name
status
email
createdAt
updatedAt
invoiceUrl
totalPriceSet { ${MONEY_SET_FIELDS} }
}
userErrors { field message }
}
}`,
variables: { input },
})
assertNoUserErrors(data.draftOrderCreate, 'draftOrderCreate')
const created = (
data.draftOrderCreate as { draftOrder?: Record<string, unknown> }
).draftOrder
if (!created) throw new Error('draftOrderCreate returned no draft order.')
return mapDraft(created)
}
/** Default export: list draft orders. */
export default listDraftOrders