@sixtyninecommerce/admin 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +359 -2
- package/dist/apps/assertion.d.ts +30 -3
- package/dist/apps/assertion.js +40 -3
- package/dist/apps/config.d.ts +39 -0
- package/dist/apps/config.js +22 -0
- package/dist/apps/credential.d.ts +5 -0
- package/dist/apps/credential.js +55 -4
- package/dist/apps/index.d.ts +11 -0
- package/dist/client.d.ts +54 -1
- package/dist/client.js +34 -1
- package/dist/custom-objects/index.d.ts +66 -0
- package/dist/custom-objects/index.js +48 -0
- package/dist/customers/index.d.ts +225 -0
- package/dist/customers/index.js +279 -0
- package/dist/documents/index.d.ts +432 -0
- package/dist/documents/index.js +289 -0
- package/dist/index.d.ts +34 -2
- package/dist/index.js +16 -1
- package/dist/jobs/index.d.ts +143 -0
- package/dist/jobs/index.js +132 -0
- package/dist/metafields/index.d.ts +291 -0
- package/dist/metafields/index.js +141 -0
- package/dist/orders/index.d.ts +106 -0
- package/dist/orders/index.js +115 -0
- package/dist/orders/surcharges.d.ts +121 -0
- package/dist/orders/surcharges.js +110 -0
- package/dist/payments/index.d.ts +244 -0
- package/dist/payments/index.js +103 -0
- package/dist/payments/providers.d.ts +281 -0
- package/dist/payments/providers.js +205 -0
- package/dist/platform/provision-store.d.ts +9 -26
- package/dist/platform/provision-store.js +8 -66
- package/dist/tax/index.d.ts +128 -0
- package/dist/tax/index.js +101 -0
- package/dist/tenant/capabilities.d.ts +15 -0
- package/dist/types.d.ts +20 -3
- package/dist/uploads/index.d.ts +146 -0
- package/dist/uploads/index.js +72 -0
- package/package.json +3 -3
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { errorFromErrorResult, isErrorResult, pageFromVendureList, readOffsetCursor, } from '@sixtyninecommerce/core';
|
|
2
|
+
/**
|
|
3
|
+
* Customers — the commerce master record (headless spec §6.5).
|
|
4
|
+
*
|
|
5
|
+
* Commerce owns customer identity and addresses; a vertical stores a
|
|
6
|
+
* `platformCustomerId` and asks here. The two things worth knowing before
|
|
7
|
+
* reading further are both traps this module exists to close:
|
|
8
|
+
*
|
|
9
|
+
* 1. **`create` wraps `createChannelCustomer`, never core `createCustomer`.**
|
|
10
|
+
* Vendure's own mutation silently SHARES a customer across channels when the
|
|
11
|
+
* email already exists anywhere on the platform (ADR-016): it finds that
|
|
12
|
+
* customer and pushes the new channel onto them. On a multi-tenant platform
|
|
13
|
+
* that is a cross-tenant data leak wearing the clothes of a convenience.
|
|
14
|
+
* There is no way to reach the leaking path through this SDK.
|
|
15
|
+
* 2. **`erase` is a scrub, not a delete.** It removes the person and keeps the
|
|
16
|
+
* record, the orders and the documents. It is the only destructive customer
|
|
17
|
+
* operation the platform has.
|
|
18
|
+
*/
|
|
19
|
+
export const CUSTOMER_FIELDS = /* GraphQL */ `
|
|
20
|
+
id
|
|
21
|
+
createdAt
|
|
22
|
+
updatedAt
|
|
23
|
+
title
|
|
24
|
+
firstName
|
|
25
|
+
lastName
|
|
26
|
+
emailAddress
|
|
27
|
+
phoneNumber
|
|
28
|
+
groups {
|
|
29
|
+
id
|
|
30
|
+
name
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
export const CUSTOMER_ADDRESS_FIELDS = /* GraphQL */ `
|
|
34
|
+
id
|
|
35
|
+
fullName
|
|
36
|
+
company
|
|
37
|
+
streetLine1
|
|
38
|
+
streetLine2
|
|
39
|
+
city
|
|
40
|
+
province
|
|
41
|
+
postalCode
|
|
42
|
+
country {
|
|
43
|
+
code
|
|
44
|
+
name
|
|
45
|
+
}
|
|
46
|
+
phoneNumber
|
|
47
|
+
defaultShippingAddress
|
|
48
|
+
defaultBillingAddress
|
|
49
|
+
`;
|
|
50
|
+
export const LIST_QUERY = /* GraphQL */ `
|
|
51
|
+
query Customers($options: CustomerListOptions) {
|
|
52
|
+
customers(options: $options) {
|
|
53
|
+
items { ${CUSTOMER_FIELDS} }
|
|
54
|
+
totalItems
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
`;
|
|
58
|
+
export const GET_QUERY = /* GraphQL */ `
|
|
59
|
+
query CustomerById($id: ID!) {
|
|
60
|
+
customer(id: $id) { ${CUSTOMER_FIELDS} }
|
|
61
|
+
}
|
|
62
|
+
`;
|
|
63
|
+
export const ADDRESSES_QUERY = /* GraphQL */ `
|
|
64
|
+
query CustomerAddresses($id: ID!) {
|
|
65
|
+
customer(id: $id) {
|
|
66
|
+
id
|
|
67
|
+
addresses { ${CUSTOMER_ADDRESS_FIELDS} }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
/**
|
|
72
|
+
* The CHANNEL-SCOPED creation mutation. See the module docblock — core
|
|
73
|
+
* `createCustomer` is not an option, and this SDK exposes no path to it.
|
|
74
|
+
*/
|
|
75
|
+
export const CREATE_MUTATION = /* GraphQL */ `
|
|
76
|
+
mutation CreateChannelCustomer($input: CreateCustomerInput!, $password: String) {
|
|
77
|
+
createChannelCustomer(input: $input, password: $password) {
|
|
78
|
+
id
|
|
79
|
+
emailAddress
|
|
80
|
+
createdNew
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
84
|
+
export const UPDATE_MUTATION = /* GraphQL */ `
|
|
85
|
+
mutation UpdateCustomer($input: UpdateCustomerInput!) {
|
|
86
|
+
updateCustomer(input: $input) {
|
|
87
|
+
... on Customer { ${CUSTOMER_FIELDS} }
|
|
88
|
+
... on ErrorResult { errorCode message }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
`;
|
|
92
|
+
export const INVITE_MUTATION = /* GraphQL */ `
|
|
93
|
+
mutation InviteCustomer($customerId: ID!, $mode: CustomerInviteMode) {
|
|
94
|
+
inviteCustomer(customerId: $customerId, mode: $mode) {
|
|
95
|
+
customerId
|
|
96
|
+
emailAddress
|
|
97
|
+
mode
|
|
98
|
+
sentAt
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
`;
|
|
102
|
+
export const RESEND_INVITE_MUTATION = /* GraphQL */ `
|
|
103
|
+
mutation ResendCustomerInvitation($customerId: ID!, $mode: CustomerInviteMode) {
|
|
104
|
+
resendCustomerInvitation(customerId: $customerId, mode: $mode) {
|
|
105
|
+
customerId
|
|
106
|
+
emailAddress
|
|
107
|
+
mode
|
|
108
|
+
sentAt
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
`;
|
|
112
|
+
export const ERASE_MUTATION = /* GraphQL */ `
|
|
113
|
+
mutation EraseCustomer(
|
|
114
|
+
$customerId: ID!
|
|
115
|
+
$reason: String!
|
|
116
|
+
$force: Boolean
|
|
117
|
+
$idempotencyKey: String
|
|
118
|
+
) {
|
|
119
|
+
eraseCustomer(
|
|
120
|
+
customerId: $customerId
|
|
121
|
+
reason: $reason
|
|
122
|
+
force: $force
|
|
123
|
+
idempotencyKey: $idempotencyKey
|
|
124
|
+
) {
|
|
125
|
+
customerId
|
|
126
|
+
erasedAt
|
|
127
|
+
ordersRetained
|
|
128
|
+
documentsRetained
|
|
129
|
+
forced
|
|
130
|
+
reason
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
`;
|
|
134
|
+
export const ERASURE_QUERY = /* GraphQL */ `
|
|
135
|
+
query CustomerErasure($customerId: ID!) {
|
|
136
|
+
customerErasure(customerId: $customerId) {
|
|
137
|
+
customerId
|
|
138
|
+
erasedAt
|
|
139
|
+
ordersRetained
|
|
140
|
+
documentsRetained
|
|
141
|
+
forced
|
|
142
|
+
reason
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
`;
|
|
146
|
+
export const ADD_TO_GROUP_MUTATION = /* GraphQL */ `
|
|
147
|
+
mutation AddCustomersToGroup($customerGroupId: ID!, $customerIds: [ID!]!) {
|
|
148
|
+
addCustomersToGroup(customerGroupId: $customerGroupId, customerIds: $customerIds) {
|
|
149
|
+
id
|
|
150
|
+
name
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
`;
|
|
154
|
+
export const REMOVE_FROM_GROUP_MUTATION = /* GraphQL */ `
|
|
155
|
+
mutation RemoveCustomersFromGroup($customerGroupId: ID!, $customerIds: [ID!]!) {
|
|
156
|
+
removeCustomersFromGroup(customerGroupId: $customerGroupId, customerIds: $customerIds) {
|
|
157
|
+
id
|
|
158
|
+
name
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
`;
|
|
162
|
+
export const CREATE_ADDRESS_MUTATION = /* GraphQL */ `
|
|
163
|
+
mutation CreateCustomerAddress($customerId: ID!, $input: CreateAddressInput!) {
|
|
164
|
+
createCustomerAddress(customerId: $customerId, input: $input) {
|
|
165
|
+
${CUSTOMER_ADDRESS_FIELDS}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
`;
|
|
169
|
+
export const UPDATE_ADDRESS_MUTATION = /* GraphQL */ `
|
|
170
|
+
mutation UpdateCustomerAddress($input: UpdateAddressInput!) {
|
|
171
|
+
updateCustomerAddress(input: $input) { ${CUSTOMER_ADDRESS_FIELDS} }
|
|
172
|
+
}
|
|
173
|
+
`;
|
|
174
|
+
export const DELETE_ADDRESS_MUTATION = /* GraphQL */ `
|
|
175
|
+
mutation DeleteCustomerAddress($id: ID!) {
|
|
176
|
+
deleteCustomerAddress(id: $id) { success }
|
|
177
|
+
}
|
|
178
|
+
`;
|
|
179
|
+
const DEFAULT_PAGE_SIZE = 25;
|
|
180
|
+
export function createCustomersModule(transport) {
|
|
181
|
+
const listPage = async (skip, take, options) => {
|
|
182
|
+
const data = await transport.graphql({ api: 'admin', query: LIST_QUERY, variables: { options: { skip, take } } }, options);
|
|
183
|
+
return pageFromVendureList(data.customers, skip, take, (cursor) => listPage(readOffsetCursor(cursor), take, options));
|
|
184
|
+
};
|
|
185
|
+
return {
|
|
186
|
+
list(options) {
|
|
187
|
+
// The Admin API is offset-paged; the offset hides inside the cursor so a
|
|
188
|
+
// consumer only ever sees `Page<T>` (spec §9.3).
|
|
189
|
+
const take = options?.limit ?? DEFAULT_PAGE_SIZE;
|
|
190
|
+
const skip = options?.after === undefined ? 0 : readOffsetCursor(options.after);
|
|
191
|
+
return listPage(skip, take, options);
|
|
192
|
+
},
|
|
193
|
+
async get(id, options) {
|
|
194
|
+
const data = await transport.graphql({ api: 'admin', query: GET_QUERY, variables: { id } }, options);
|
|
195
|
+
return data.customer;
|
|
196
|
+
},
|
|
197
|
+
async create(input, options) {
|
|
198
|
+
const data = await transport.graphql({
|
|
199
|
+
api: 'admin',
|
|
200
|
+
query: CREATE_MUTATION,
|
|
201
|
+
variables: { input, password: options?.password ?? null },
|
|
202
|
+
}, options);
|
|
203
|
+
return data.createChannelCustomer;
|
|
204
|
+
},
|
|
205
|
+
async update(id, input, options) {
|
|
206
|
+
const data = await transport.graphql({ api: 'admin', query: UPDATE_MUTATION, variables: { input: { id, ...input } } }, options);
|
|
207
|
+
// `updateCustomer` is a UNION — `Customer | EmailAddressConflictError` —
|
|
208
|
+
// delivered at HTTP 200, so the transport never throws for it. Returned
|
|
209
|
+
// unchecked, a rename onto an address already taken on the channel
|
|
210
|
+
// resolved "successfully" with an error object, and the caller saw a
|
|
211
|
+
// `Customer` whose `id` was undefined.
|
|
212
|
+
if (isErrorResult(data.updateCustomer))
|
|
213
|
+
throw errorFromErrorResult(data.updateCustomer);
|
|
214
|
+
return data.updateCustomer;
|
|
215
|
+
},
|
|
216
|
+
async invite(id, options) {
|
|
217
|
+
// `resend` picks the SEPARATE mutation rather than a flag on one: the two
|
|
218
|
+
// behave identically on the server and exist apart only so a dashboard
|
|
219
|
+
// can label the repeat honestly. A still-valid set-password token is
|
|
220
|
+
// reused rather than rotated, so a link already in the customer's inbox
|
|
221
|
+
// keeps working.
|
|
222
|
+
const query = options?.resend === true ? RESEND_INVITE_MUTATION : INVITE_MUTATION;
|
|
223
|
+
const data = await transport.graphql({ api: 'admin', query, variables: { customerId: id, mode: options?.mode ?? null } }, options);
|
|
224
|
+
return (data.inviteCustomer ?? data.resendCustomerInvitation);
|
|
225
|
+
},
|
|
226
|
+
async erase(id, options) {
|
|
227
|
+
const data = await transport.graphql({
|
|
228
|
+
api: 'admin',
|
|
229
|
+
query: ERASE_MUTATION,
|
|
230
|
+
variables: {
|
|
231
|
+
customerId: id,
|
|
232
|
+
reason: options.reason,
|
|
233
|
+
force: options.force ?? null,
|
|
234
|
+
idempotencyKey: options.idempotencyKey ?? null,
|
|
235
|
+
},
|
|
236
|
+
}, options);
|
|
237
|
+
return data.eraseCustomer;
|
|
238
|
+
},
|
|
239
|
+
async erasure(id, options) {
|
|
240
|
+
const data = await transport.graphql({ api: 'admin', query: ERASURE_QUERY, variables: { customerId: id } }, options);
|
|
241
|
+
return data.customerErasure;
|
|
242
|
+
},
|
|
243
|
+
groups: {
|
|
244
|
+
async add(groupId, customerIds, options) {
|
|
245
|
+
const data = await transport.graphql({
|
|
246
|
+
api: 'admin',
|
|
247
|
+
query: ADD_TO_GROUP_MUTATION,
|
|
248
|
+
variables: { customerGroupId: groupId, customerIds },
|
|
249
|
+
}, options);
|
|
250
|
+
return data.addCustomersToGroup;
|
|
251
|
+
},
|
|
252
|
+
async remove(groupId, customerIds, options) {
|
|
253
|
+
const data = await transport.graphql({
|
|
254
|
+
api: 'admin',
|
|
255
|
+
query: REMOVE_FROM_GROUP_MUTATION,
|
|
256
|
+
variables: { customerGroupId: groupId, customerIds },
|
|
257
|
+
}, options);
|
|
258
|
+
return data.removeCustomersFromGroup;
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
addresses: {
|
|
262
|
+
async list(customerId, options) {
|
|
263
|
+
const data = await transport.graphql({ api: 'admin', query: ADDRESSES_QUERY, variables: { id: customerId } }, options);
|
|
264
|
+
return data.customer?.addresses ?? [];
|
|
265
|
+
},
|
|
266
|
+
async create(customerId, input, options) {
|
|
267
|
+
const data = await transport.graphql({ api: 'admin', query: CREATE_ADDRESS_MUTATION, variables: { customerId, input } }, options);
|
|
268
|
+
return data.createCustomerAddress;
|
|
269
|
+
},
|
|
270
|
+
async update(input, options) {
|
|
271
|
+
const data = await transport.graphql({ api: 'admin', query: UPDATE_ADDRESS_MUTATION, variables: { input } }, options);
|
|
272
|
+
return data.updateCustomerAddress;
|
|
273
|
+
},
|
|
274
|
+
async delete(id, options) {
|
|
275
|
+
await transport.graphql({ api: 'admin', query: DELETE_ADDRESS_MUTATION, variables: { id } }, options);
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
}
|