@sixtyninecommerce/admin 0.8.0 → 0.10.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 +2739 -17
- package/dist/api-tokens/index.d.ts +15 -5
- package/dist/api-tokens/index.js +1 -0
- package/dist/apps/event-subscription.d.ts +59 -0
- package/dist/apps/event-subscription.js +32 -0
- package/dist/apps/hook-secret.d.ts +22 -0
- package/dist/apps/hook-secret.js +36 -0
- package/dist/apps/index.d.ts +54 -3
- package/dist/apps/index.js +17 -0
- package/dist/audit/index.d.ts +7 -0
- package/dist/auth/queries.d.ts +5 -1
- package/dist/auth/queries.js +6 -0
- package/dist/bulk/index.d.ts +323 -0
- package/dist/bulk/index.js +162 -0
- package/dist/catalog/assets.d.ts +147 -0
- package/dist/catalog/assets.js +164 -0
- package/dist/catalog/collections.d.ts +109 -0
- package/dist/catalog/collections.js +91 -0
- package/dist/catalog/facets.d.ts +103 -0
- package/dist/catalog/facets.js +100 -0
- package/dist/catalog/index.d.ts +115 -0
- package/dist/catalog/index.js +21 -0
- package/dist/catalog/options.d.ts +89 -0
- package/dist/catalog/options.js +88 -0
- package/dist/catalog/products-bulk.d.ts +21 -0
- package/dist/catalog/products-bulk.js +58 -0
- package/dist/catalog/products.d.ts +650 -0
- package/dist/catalog/products.js +458 -0
- package/dist/catalog/properties.d.ts +234 -0
- package/dist/catalog/properties.js +158 -0
- package/dist/catalog/types.d.ts +174 -0
- package/dist/catalog/types.js +141 -0
- package/dist/catalog/variants.d.ts +324 -0
- package/dist/catalog/variants.js +288 -0
- package/dist/catalog/visibility.d.ts +57 -0
- package/dist/catalog/visibility.js +59 -0
- package/dist/client.d.ts +192 -3
- package/dist/client.js +87 -12
- package/dist/compliance/index.d.ts +88 -0
- package/dist/compliance/index.js +88 -0
- package/dist/customer-data-redaction.d.ts +14 -0
- package/dist/customer-data-redaction.js +22 -0
- package/dist/customers/index.d.ts +428 -17
- package/dist/customers/index.js +322 -15
- package/dist/documents/index.d.ts +513 -16
- package/dist/documents/index.js +229 -3
- package/dist/events/index.d.ts +259 -0
- package/dist/events/index.js +147 -0
- package/dist/feeds/ai.d.ts +46 -0
- package/dist/feeds/ai.js +43 -0
- package/dist/feeds/categories.d.ts +50 -0
- package/dist/feeds/categories.js +56 -0
- package/dist/feeds/configs.d.ts +54 -0
- package/dist/feeds/configs.js +85 -0
- package/dist/feeds/identifier-fields.d.ts +25 -0
- package/dist/feeds/identifier-fields.js +29 -0
- package/dist/feeds/index.d.ts +54 -0
- package/dist/feeds/index.js +46 -0
- package/dist/feeds/preserve.d.ts +38 -0
- package/dist/feeds/preserve.js +74 -0
- package/dist/feeds/rules.d.ts +42 -0
- package/dist/feeds/rules.js +56 -0
- package/dist/feeds/types.d.ts +654 -0
- package/dist/feeds/types.js +135 -0
- package/dist/fulfillments/index.d.ts +437 -0
- package/dist/fulfillments/index.js +534 -0
- package/dist/heads/index.d.ts +60 -0
- package/dist/heads/index.js +45 -0
- package/dist/hooks/assignments.d.ts +90 -0
- package/dist/hooks/assignments.js +53 -0
- package/dist/hooks/index.d.ts +97 -0
- package/dist/hooks/index.js +76 -0
- package/dist/index.d.ts +95 -9
- package/dist/index.js +21 -1
- package/dist/integrations/index.d.ts +14 -0
- package/dist/integrations/index.js +9 -0
- package/dist/integrations/pipelines.d.ts +113 -0
- package/dist/integrations/pipelines.js +62 -0
- package/dist/integrations/types.d.ts +176 -0
- package/dist/integrations/types.js +8 -0
- package/dist/jobs/bulk-action-run.d.ts +74 -0
- package/dist/jobs/bulk-action-run.js +69 -0
- package/dist/locations/index.d.ts +6 -2
- package/dist/meta/index.d.ts +32 -0
- package/dist/orders/bulk.d.ts +75 -0
- package/dist/orders/bulk.js +150 -0
- package/dist/orders/commit-edit.d.ts +123 -0
- package/dist/orders/commit-edit.js +142 -0
- package/dist/orders/deposit-policy.d.ts +103 -0
- package/dist/orders/deposit-policy.js +202 -0
- package/dist/orders/get.d.ts +722 -0
- package/dist/orders/get.js +530 -0
- package/dist/orders/index.d.ts +186 -21
- package/dist/orders/index.js +57 -0
- package/dist/orders/line-price-preview.d.ts +297 -0
- package/dist/orders/line-price-preview.js +50 -0
- package/dist/orders/manual-discounts.d.ts +141 -0
- package/dist/orders/manual-discounts.js +97 -0
- package/dist/orders/manual-pricing.d.ts +192 -0
- package/dist/orders/manual-pricing.js +72 -0
- package/dist/orders/order-entry.d.ts +566 -0
- package/dist/orders/order-entry.js +129 -0
- package/dist/orders/parcel-charge.d.ts +45 -0
- package/dist/orders/parcel-charge.js +55 -0
- package/dist/orders/plan-edit.d.ts +489 -0
- package/dist/orders/plan-edit.js +93 -0
- package/dist/orders/status.d.ts +235 -0
- package/dist/orders/status.js +96 -0
- package/dist/orders/surcharges.d.ts +75 -2
- package/dist/orders/surcharges.js +110 -4
- package/dist/payments/account-credit.d.ts +171 -0
- package/dist/payments/account-credit.js +107 -0
- package/dist/payments/index.d.ts +241 -9
- package/dist/payments/index.js +92 -0
- package/dist/payments/methods.d.ts +37 -0
- package/dist/payments/methods.js +33 -0
- package/dist/platform/catalog.d.ts +148 -0
- package/dist/platform/catalog.js +74 -0
- package/dist/platform/entitlements.d.ts +111 -0
- package/dist/platform/entitlements.js +86 -0
- package/dist/platform/legal-entities.d.ts +139 -0
- package/dist/platform/legal-entities.js +126 -0
- package/dist/platform/legal-entity-numbering.d.ts +103 -0
- package/dist/platform/legal-entity-numbering.js +101 -0
- package/dist/platform/organization-membership.d.ts +80 -0
- package/dist/platform/organization-membership.js +71 -0
- package/dist/platform/organization-settings.d.ts +33 -0
- package/dist/platform/organization-settings.js +42 -0
- package/dist/platform/organization-sharing.d.ts +172 -0
- package/dist/platform/organization-sharing.js +120 -0
- package/dist/platform/organizations.d.ts +205 -0
- package/dist/platform/organizations.js +180 -0
- package/dist/platform/provision-store.d.ts +88 -8
- package/dist/platform/provision-store.js +12 -0
- package/dist/platform/reports.d.ts +46 -0
- package/dist/platform/reports.js +31 -0
- package/dist/platform/server-metrics.d.ts +58 -0
- package/dist/platform/server-metrics.js +11 -0
- package/dist/pos/index.d.ts +567 -0
- package/dist/pos/index.js +205 -0
- package/dist/pricing/index.d.ts +408 -0
- package/dist/pricing/index.js +353 -0
- package/dist/pricing/vouchers.d.ts +103 -0
- package/dist/pricing/vouchers.js +69 -0
- package/dist/provenance/index.d.ts +93 -0
- package/dist/provenance/index.js +53 -0
- package/dist/reports/index.d.ts +199 -0
- package/dist/reports/index.js +154 -0
- package/dist/returns/index.d.ts +225 -0
- package/dist/returns/index.js +160 -0
- package/dist/search/index.d.ts +99 -0
- package/dist/search/index.js +62 -0
- package/dist/shipping/parcel-count-suggestion.d.ts +52 -0
- package/dist/shipping/parcel-count-suggestion.js +73 -0
- package/dist/shipping/parcel-rule.d.ts +48 -0
- package/dist/shipping/parcel-rule.js +91 -0
- package/dist/stock/index.d.ts +166 -0
- package/dist/stock/index.js +115 -0
- package/dist/stock/transfers.d.ts +520 -0
- package/dist/stock/transfers.js +437 -0
- package/dist/stored-value/index.d.ts +154 -0
- package/dist/stored-value/index.js +130 -0
- package/dist/tenant/capabilities.d.ts +10 -0
- package/dist/tenant/entitlements.d.ts +35 -0
- package/dist/tenant/entitlements.js +15 -0
- package/dist/tenant/field-groups.d.ts +64 -0
- package/dist/tenant/field-groups.js +34 -0
- package/dist/tenant/legal-entity.d.ts +32 -0
- package/dist/tenant/legal-entity.js +41 -0
- package/dist/tenant/notifications.d.ts +148 -0
- package/dist/tenant/notifications.js +79 -0
- package/dist/tenant/retention.d.ts +10 -4
- package/dist/tenant/settings-sources.d.ts +103 -0
- package/dist/tenant/settings-sources.js +92 -0
- package/dist/types.d.ts +86 -11
- package/dist/uploads/index.d.ts +11 -7
- package/dist/users/index.d.ts +104 -0
- package/dist/users/index.js +80 -0
- package/package.json +3 -2
package/dist/payments/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createAccountCreditModule } from './account-credit.js';
|
|
2
|
+
import { createPaymentMethodsModule } from './methods.js';
|
|
1
3
|
import { createProvidersModule } from './providers.js';
|
|
2
4
|
const PAID_ORDER_FIELDS = /* GraphQL */ `
|
|
3
5
|
id
|
|
@@ -64,9 +66,66 @@ export const REFUND_ORDER_PAYMENT_MUTATION = /* GraphQL */ `
|
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
`;
|
|
69
|
+
export const UNRECONCILED_REFUNDS_QUERY = /* GraphQL */ `
|
|
70
|
+
query UnreconciledRefunds($orderId: ID!) {
|
|
71
|
+
orderRefundPosition(orderId: $orderId) {
|
|
72
|
+
unreconciled {
|
|
73
|
+
refundId
|
|
74
|
+
paymentId
|
|
75
|
+
amount
|
|
76
|
+
createdAt
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
81
|
+
export const RECONCILE_REFUND_OUTCOME_MUTATION = /* GraphQL */ `
|
|
82
|
+
mutation ReconcileRefundOutcome($input: ReconcileRefundOutcomeInput!, $idempotencyKey: String) {
|
|
83
|
+
reconcileRefundOutcome(input: $input, idempotencyKey: $idempotencyKey) {
|
|
84
|
+
id
|
|
85
|
+
total
|
|
86
|
+
state
|
|
87
|
+
method
|
|
88
|
+
transactionId
|
|
89
|
+
reason
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
`;
|
|
93
|
+
export const REPORT_APP_PAYMENT_MUTATION = /* GraphQL */ `
|
|
94
|
+
mutation ReportAppPayment(
|
|
95
|
+
$paymentId: ID!
|
|
96
|
+
$input: ReportAppPaymentInput!
|
|
97
|
+
$idempotencyKey: String!
|
|
98
|
+
) {
|
|
99
|
+
reportAppPayment(paymentId: $paymentId, input: $input, idempotencyKey: $idempotencyKey) {
|
|
100
|
+
id
|
|
101
|
+
state
|
|
102
|
+
amount
|
|
103
|
+
method
|
|
104
|
+
transactionId
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
`;
|
|
108
|
+
export const APP_PAYMENTS_QUERY = /* GraphQL */ `
|
|
109
|
+
query AppPayments($orderId: ID!) {
|
|
110
|
+
appPayments(orderId: $orderId) {
|
|
111
|
+
id
|
|
112
|
+
orderId
|
|
113
|
+
orderCode
|
|
114
|
+
state
|
|
115
|
+
amount
|
|
116
|
+
currencyCode
|
|
117
|
+
providerReference
|
|
118
|
+
methodCode
|
|
119
|
+
outcomeUnknown
|
|
120
|
+
createdAt
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
`;
|
|
67
124
|
export function createPaymentsModule(transport) {
|
|
68
125
|
return {
|
|
69
126
|
providers: createProvidersModule(transport),
|
|
127
|
+
accountCredit: createAccountCreditModule(transport),
|
|
128
|
+
methods: createPaymentMethodsModule(transport),
|
|
70
129
|
async record(orderId, input, options) {
|
|
71
130
|
const data = await transport.graphql({
|
|
72
131
|
api: 'admin',
|
|
@@ -99,5 +158,38 @@ export function createPaymentsModule(transport) {
|
|
|
99
158
|
const data = await transport.graphql({ api: 'admin', query: ORDER_REFUND_POSITION_QUERY, variables: { orderId } }, options);
|
|
100
159
|
return data.orderRefundPosition;
|
|
101
160
|
},
|
|
161
|
+
// Its own query rather than a field on `refundPosition`'s: an SDK released
|
|
162
|
+
// ahead of a server without `unreconciled` must not break that read.
|
|
163
|
+
async unreconciledRefunds(orderId, options) {
|
|
164
|
+
const data = await transport.graphql({ api: 'admin', query: UNRECONCILED_REFUNDS_QUERY, variables: { orderId } }, options);
|
|
165
|
+
return data.orderRefundPosition.unreconciled;
|
|
166
|
+
},
|
|
167
|
+
async reconcileRefund(input, options) {
|
|
168
|
+
const data = await transport.graphql({
|
|
169
|
+
api: 'admin',
|
|
170
|
+
query: RECONCILE_REFUND_OUTCOME_MUTATION,
|
|
171
|
+
// A variable, not a header: the Admin API takes the key as an argument.
|
|
172
|
+
variables: { input, idempotencyKey: options?.idempotencyKey },
|
|
173
|
+
}, options);
|
|
174
|
+
return data.reconcileRefundOutcome;
|
|
175
|
+
},
|
|
176
|
+
async reportAppPayment(paymentId, input, options) {
|
|
177
|
+
// Refused here rather than sent: the argument is non-null on the wire, and
|
|
178
|
+
// a report without a key could never be safely repeated.
|
|
179
|
+
if (typeof options?.idempotencyKey !== 'string' || options.idempotencyKey.trim() === '') {
|
|
180
|
+
throw new TypeError('reportAppPayment needs options.idempotencyKey: one key per provider event.');
|
|
181
|
+
}
|
|
182
|
+
const data = await transport.graphql({
|
|
183
|
+
api: 'admin',
|
|
184
|
+
query: REPORT_APP_PAYMENT_MUTATION,
|
|
185
|
+
// A variable, not a header: the Admin API takes the key as an argument.
|
|
186
|
+
variables: { paymentId, input, idempotencyKey: options.idempotencyKey },
|
|
187
|
+
}, options);
|
|
188
|
+
return data.reportAppPayment;
|
|
189
|
+
},
|
|
190
|
+
async appPayments(orderId, options) {
|
|
191
|
+
const data = await transport.graphql({ api: 'admin', query: APP_PAYMENTS_QUERY, variables: { orderId } }, options);
|
|
192
|
+
return data.appPayments;
|
|
193
|
+
},
|
|
102
194
|
};
|
|
103
195
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Page, Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { ListOptions } from '../types.js';
|
|
3
|
+
/** One payment method of the store, as staff choose one. */
|
|
4
|
+
export interface PaymentMethod {
|
|
5
|
+
id: string;
|
|
6
|
+
/**
|
|
7
|
+
* The method's code: what `payments.record` takes as `method`, and what a
|
|
8
|
+
* deposit policy names in `storefront.methods`.
|
|
9
|
+
*/
|
|
10
|
+
code: string;
|
|
11
|
+
name: string;
|
|
12
|
+
/** A disabled method takes no new payments; it still names the ones it took. */
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* The code of the handler that takes the money (`manual-payment`, `mollie`, a
|
|
16
|
+
* payment app's handler, …): what kind of method this is.
|
|
17
|
+
*/
|
|
18
|
+
handlerCode: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The store's payment methods (`admin.payments.methods`): what staff record a
|
|
22
|
+
* payment with, and what a deposit policy names.
|
|
23
|
+
*
|
|
24
|
+
* Needs `ReadSettings` or `ReadPaymentMethod`. Scoped to the channel the client
|
|
25
|
+
* is on: another store's methods are not listed.
|
|
26
|
+
*/
|
|
27
|
+
export interface PaymentMethodsModule {
|
|
28
|
+
/**
|
|
29
|
+
* Every method of the store, enabled or not, in the engine's order. Paged
|
|
30
|
+
* (`options.limit`, default 100, at most 1,000 or `INVALID_PAGE_SIZE`; and
|
|
31
|
+
* `options.after` from a previous page); `for await (const method of page)`
|
|
32
|
+
* walks them all.
|
|
33
|
+
*/
|
|
34
|
+
list(options?: ListOptions): Promise<Page<PaymentMethod>>;
|
|
35
|
+
}
|
|
36
|
+
export declare const PAYMENT_METHODS_QUERY = "\n query StorePaymentMethods($options: PaymentMethodListOptions) {\n paymentMethods(options: $options) {\n items {\n id\n code\n name\n enabled\n handler {\n code\n }\n }\n totalItems\n }\n }\n";
|
|
37
|
+
export declare function createPaymentMethodsModule(transport: Transport): PaymentMethodsModule;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { vendureListPage } from '../catalog/types.js';
|
|
2
|
+
/** A store has a handful; one page holds them. */
|
|
3
|
+
const DEFAULT_PAGE_SIZE = 100;
|
|
4
|
+
export const PAYMENT_METHODS_QUERY = /* GraphQL */ `
|
|
5
|
+
query StorePaymentMethods($options: PaymentMethodListOptions) {
|
|
6
|
+
paymentMethods(options: $options) {
|
|
7
|
+
items {
|
|
8
|
+
id
|
|
9
|
+
code
|
|
10
|
+
name
|
|
11
|
+
enabled
|
|
12
|
+
handler {
|
|
13
|
+
code
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
totalItems
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
20
|
+
const toMethod = (method) => ({
|
|
21
|
+
id: method.id,
|
|
22
|
+
code: method.code,
|
|
23
|
+
name: method.name,
|
|
24
|
+
enabled: method.enabled,
|
|
25
|
+
handlerCode: method.handler.code,
|
|
26
|
+
});
|
|
27
|
+
export function createPaymentMethodsModule(transport) {
|
|
28
|
+
return {
|
|
29
|
+
list(options) {
|
|
30
|
+
return vendureListPage(transport, PAYMENT_METHODS_QUERY, 'paymentMethods', (skip, take) => ({ options: { skip, take } }), { ...options, limit: options?.limit ?? DEFAULT_PAGE_SIZE }, toMethod);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { AdminClientConfig } from '../client.js';
|
|
3
|
+
import type { Call } from '../types.js';
|
|
4
|
+
/** Which kind of catalogue entity a repair row is about. */
|
|
5
|
+
export type TranslationRepairEntityKind = 'PRODUCT' | 'VARIANT';
|
|
6
|
+
/**
|
|
7
|
+
* Why the repair left a candidate alone. A string on the wire, not an enum, so
|
|
8
|
+
* a reason added later never breaks a caller:
|
|
9
|
+
*
|
|
10
|
+
* - `SHARED_CHANNEL_LANGUAGES_DIFFER`: another store holds the entity and offers
|
|
11
|
+
* other languages, so a row foreign here may be that store's own.
|
|
12
|
+
* - `CARRIER_PRODUCT`: the platform's hidden external-line carrier.
|
|
13
|
+
* - `DEFAULT_ROW_GONE`: its default-language row disappeared before it could be
|
|
14
|
+
* repaired.
|
|
15
|
+
*/
|
|
16
|
+
export type TranslationRepairSkipReason = 'SHARED_CHANNEL_LANGUAGES_DIFFER' | 'CARRIER_PRODUCT' | 'DEFAULT_ROW_GONE' | (string & {});
|
|
17
|
+
export interface RepairTranslationLanguagesInput {
|
|
18
|
+
/** The store to repair. The root channel and organization hubs are refused. */
|
|
19
|
+
channelId: string;
|
|
20
|
+
/**
|
|
21
|
+
* `true` reports what would be removed and changes nothing; `false` removes
|
|
22
|
+
* it. Both take the same path on the server, so a dry run is an exact preview
|
|
23
|
+
* of the apply that follows it (as long as the catalogue does not change in
|
|
24
|
+
* between).
|
|
25
|
+
*/
|
|
26
|
+
dryRun: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Products and variants examined per page. The server defaults to 100 and
|
|
29
|
+
* treats anything above 500 as 500; below 1 is refused
|
|
30
|
+
* (`TRANSLATION_REPAIR_LIMIT_INVALID`).
|
|
31
|
+
*/
|
|
32
|
+
limit?: number;
|
|
33
|
+
/**
|
|
34
|
+
* The `nextCursor` of the previous page of the SAME run: same store, same
|
|
35
|
+
* `dryRun`. Omitted or null starts from the beginning. A cursor issued for
|
|
36
|
+
* another store or the other mode is refused (`TRANSLATION_REPAIR_CURSOR_INVALID`),
|
|
37
|
+
* so a dry run's cursor can never make an apply skip the pages before it.
|
|
38
|
+
*/
|
|
39
|
+
cursor?: string | null;
|
|
40
|
+
/**
|
|
41
|
+
* On the first page (no cursor) only: also list the store's single-variant
|
|
42
|
+
* products whose variant name differs from the product name.
|
|
43
|
+
*/
|
|
44
|
+
includeNameReport?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A translation row the repair removes, or in a dry run would remove. The whole
|
|
48
|
+
* row, so a dry run's answer is the undo record of the apply.
|
|
49
|
+
*/
|
|
50
|
+
export interface TranslationRepairRemovedRow {
|
|
51
|
+
translationId: string;
|
|
52
|
+
languageCode: string;
|
|
53
|
+
name: string;
|
|
54
|
+
/** Products only; null for a variant. */
|
|
55
|
+
slug: string | null;
|
|
56
|
+
/** Products only; null for a variant. */
|
|
57
|
+
description: string | null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A product or variant with a row in the store's default language and one or
|
|
61
|
+
* more rows in languages the store does not offer.
|
|
62
|
+
*/
|
|
63
|
+
export interface TranslationRepairEntity {
|
|
64
|
+
kind: TranslationRepairEntityKind;
|
|
65
|
+
id: string;
|
|
66
|
+
/** The product itself for a `PRODUCT`, the variant's product for a `VARIANT`. */
|
|
67
|
+
productId: string;
|
|
68
|
+
/** Variants only; null for a product. */
|
|
69
|
+
sku: string | null;
|
|
70
|
+
/** The language of the row the merchant sees, which stays. */
|
|
71
|
+
keptLanguageCode: string;
|
|
72
|
+
/** The name the merchant sees, which stays. */
|
|
73
|
+
keptName: string;
|
|
74
|
+
removed: TranslationRepairRemovedRow[];
|
|
75
|
+
}
|
|
76
|
+
/** A candidate the repair leaves alone, and why. */
|
|
77
|
+
export interface TranslationRepairSkipped {
|
|
78
|
+
kind: TranslationRepairEntityKind;
|
|
79
|
+
id: string;
|
|
80
|
+
productId: string;
|
|
81
|
+
sku: string | null;
|
|
82
|
+
/** The name the merchant sees. */
|
|
83
|
+
name: string;
|
|
84
|
+
reason: TranslationRepairSkipReason;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A single-variant product whose variant shows another name than the product.
|
|
88
|
+
* Report only: the repair never renames.
|
|
89
|
+
*/
|
|
90
|
+
export interface TranslationRepairNameMismatch {
|
|
91
|
+
productId: string;
|
|
92
|
+
variantId: string;
|
|
93
|
+
sku: string;
|
|
94
|
+
productName: string;
|
|
95
|
+
variantName: string;
|
|
96
|
+
}
|
|
97
|
+
/** One page of a repair run, exactly as the engine answers it. */
|
|
98
|
+
export interface TranslationRepairPage {
|
|
99
|
+
/** Fresh for every call; the audit rows of an apply page carry it. */
|
|
100
|
+
runId: string;
|
|
101
|
+
dryRun: boolean;
|
|
102
|
+
channelId: string;
|
|
103
|
+
channelCode: string;
|
|
104
|
+
defaultLanguageCode: string;
|
|
105
|
+
/** The default language first, then the store's other available languages. */
|
|
106
|
+
offeredLanguageCodes: string[];
|
|
107
|
+
repaired: TranslationRepairEntity[];
|
|
108
|
+
skipped: TranslationRepairSkipped[];
|
|
109
|
+
/**
|
|
110
|
+
* Products and variants whose rows are ALL in languages the store does not
|
|
111
|
+
* offer. Never touched (removing them would remove their only name). Counted
|
|
112
|
+
* on the first page only; 0 on later pages.
|
|
113
|
+
*/
|
|
114
|
+
foreignOnlyCount: number;
|
|
115
|
+
/** First page with `includeNameReport` only, for the whole store; empty otherwise. */
|
|
116
|
+
nameMismatches: TranslationRepairNameMismatch[];
|
|
117
|
+
/** Pass back as `cursor` to continue this run; null when every candidate was examined. */
|
|
118
|
+
nextCursor: string | null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* `admin.platform.catalog` — SuperAdmin catalogue maintenance across stores.
|
|
122
|
+
* An API token or an app credential is refused BEFORE anything is sent
|
|
123
|
+
* (`PLATFORM_REQUIRES_SESSION`); the server refuses one anyway.
|
|
124
|
+
*/
|
|
125
|
+
export interface PlatformCatalogModule {
|
|
126
|
+
/**
|
|
127
|
+
* One page of the translation-language repair for one store: removes the
|
|
128
|
+
* store's product and variant translation rows in languages it does not
|
|
129
|
+
* offer, ONLY from entities that also have a row in the store's default
|
|
130
|
+
* language. It never writes a name and never touches an entity whose rows
|
|
131
|
+
* are all foreign (`foreignOnlyCount`).
|
|
132
|
+
*
|
|
133
|
+
* Run it with `dryRun: true` first and keep the answer: its removed rows are
|
|
134
|
+
* the undo record. Follow `nextCursor` until it is null to cover the store;
|
|
135
|
+
* each page is its own transaction, so an apply that stops half-way leaves
|
|
136
|
+
* the pages before it applied and is safe to run again from the start.
|
|
137
|
+
*
|
|
138
|
+
* Refusals: `TRANSLATION_REPAIR_CHANNEL_NOT_FOUND` (404 `NotFoundError`),
|
|
139
|
+
* `TRANSLATION_REPAIR_NOT_A_STORE` (422 `BusinessRuleError`: the root channel
|
|
140
|
+
* or an organization hub), `TRANSLATION_REPAIR_CURSOR_INVALID` and
|
|
141
|
+
* `TRANSLATION_REPAIR_LIMIT_INVALID` (400 `ValidationError`), and
|
|
142
|
+
* `CHANNEL_ACCESS_DENIED` / `FORBIDDEN` (403 `PermissionDeniedError`) for a
|
|
143
|
+
* caller who is not a SuperAdmin.
|
|
144
|
+
*/
|
|
145
|
+
repairTranslationLanguages(input: RepairTranslationLanguagesInput, call?: Call): Promise<TranslationRepairPage>;
|
|
146
|
+
}
|
|
147
|
+
export declare const REPAIR_TRANSLATION_LANGUAGES_MUTATION = "\n mutation PlatformRepairProductTranslationLanguages($input: ProductTranslationRepairInput!) {\n repairProductTranslationLanguages(input: $input) {\n runId\n dryRun\n channelId\n channelCode\n defaultLanguageCode\n offeredLanguageCodes\n repaired {\n kind\n id\n productId\n sku\n keptLanguageCode\n keptName\n removed {\n translationId\n languageCode\n name\n slug\n description\n }\n }\n skipped {\n kind\n id\n productId\n sku\n name\n reason\n }\n foreignOnlyCount\n nameMismatches {\n productId\n variantId\n sku\n productName\n variantName\n }\n nextCursor\n }\n }\n";
|
|
148
|
+
export declare function createPlatformCatalogModule(transport: Transport, config: AdminClientConfig): PlatformCatalogModule;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { requireHumanSession } from './guard.js';
|
|
2
|
+
export const REPAIR_TRANSLATION_LANGUAGES_MUTATION = /* GraphQL */ `
|
|
3
|
+
mutation PlatformRepairProductTranslationLanguages($input: ProductTranslationRepairInput!) {
|
|
4
|
+
repairProductTranslationLanguages(input: $input) {
|
|
5
|
+
runId
|
|
6
|
+
dryRun
|
|
7
|
+
channelId
|
|
8
|
+
channelCode
|
|
9
|
+
defaultLanguageCode
|
|
10
|
+
offeredLanguageCodes
|
|
11
|
+
repaired {
|
|
12
|
+
kind
|
|
13
|
+
id
|
|
14
|
+
productId
|
|
15
|
+
sku
|
|
16
|
+
keptLanguageCode
|
|
17
|
+
keptName
|
|
18
|
+
removed {
|
|
19
|
+
translationId
|
|
20
|
+
languageCode
|
|
21
|
+
name
|
|
22
|
+
slug
|
|
23
|
+
description
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
skipped {
|
|
27
|
+
kind
|
|
28
|
+
id
|
|
29
|
+
productId
|
|
30
|
+
sku
|
|
31
|
+
name
|
|
32
|
+
reason
|
|
33
|
+
}
|
|
34
|
+
foreignOnlyCount
|
|
35
|
+
nameMismatches {
|
|
36
|
+
productId
|
|
37
|
+
variantId
|
|
38
|
+
sku
|
|
39
|
+
productName
|
|
40
|
+
variantName
|
|
41
|
+
}
|
|
42
|
+
nextCursor
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
export function createPlatformCatalogModule(transport, config) {
|
|
47
|
+
return {
|
|
48
|
+
async repairTranslationLanguages(input, call) {
|
|
49
|
+
requireHumanSession(config);
|
|
50
|
+
// Only what the caller named: an omitted `limit` must reach the server as
|
|
51
|
+
// absent, so the SERVER's default applies rather than one copied here.
|
|
52
|
+
const variables = {
|
|
53
|
+
channelId: input.channelId,
|
|
54
|
+
dryRun: input.dryRun,
|
|
55
|
+
};
|
|
56
|
+
if (input.limit !== undefined)
|
|
57
|
+
variables.limit = input.limit;
|
|
58
|
+
if (input.cursor !== undefined && input.cursor !== null)
|
|
59
|
+
variables.cursor = input.cursor;
|
|
60
|
+
if (input.includeNameReport !== undefined) {
|
|
61
|
+
variables.includeNameReport = input.includeNameReport;
|
|
62
|
+
}
|
|
63
|
+
const data = await transport.graphql({
|
|
64
|
+
api: 'admin',
|
|
65
|
+
query: REPAIR_TRANSLATION_LANGUAGES_MUTATION,
|
|
66
|
+
variables: { input: variables },
|
|
67
|
+
},
|
|
68
|
+
// The store is named in the input, not by the tenant: a SuperAdmin
|
|
69
|
+
// repairs any store from wherever the client happens to be scoped.
|
|
70
|
+
{ ...call, allowUnscoped: true });
|
|
71
|
+
return data.repairProductTranslationLanguages;
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { AdminClientConfig } from '../client.js';
|
|
3
|
+
import type { Call } from '../types.js';
|
|
4
|
+
/** Where a store's plugin grant comes from. */
|
|
5
|
+
export type PlatformEntitlementSource = 'channel' | 'organization';
|
|
6
|
+
/**
|
|
7
|
+
* One plugin grant of a named store (or of an organization's workspace), as the
|
|
8
|
+
* platform's control plane reads it.
|
|
9
|
+
*/
|
|
10
|
+
export interface PlatformPluginEntitlement {
|
|
11
|
+
/**
|
|
12
|
+
* The grant row's id. An `organization` row carries the ORGANIZATION grant's
|
|
13
|
+
* id, shared by every channel that grant reaches, so pair it with
|
|
14
|
+
* `channelId` when you need a key that is unique across a `listAll`.
|
|
15
|
+
*/
|
|
16
|
+
id: string;
|
|
17
|
+
/** The store, or the organization's workspace channel, the grant applies to. */
|
|
18
|
+
channelId: string;
|
|
19
|
+
/** The plugin's catalog key (`pos`, `afas`, `mollie`, …), exactly as the engine sends it. */
|
|
20
|
+
pluginKey: string;
|
|
21
|
+
/** ISO-8601 UTC. Connecting a plugin that is already connected refreshes it. */
|
|
22
|
+
connectedAt: string;
|
|
23
|
+
connectedByUserId: string | null;
|
|
24
|
+
notes: string | null;
|
|
25
|
+
/** ISO-8601 UTC. */
|
|
26
|
+
createdAt: string;
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
/**
|
|
29
|
+
* `channel` for the store's own grant, `organization` for one its
|
|
30
|
+
* organization holds for every member store and its workspace. When both
|
|
31
|
+
* exist the store's own row wins, so a store lists a key once. An
|
|
32
|
+
* `organization` grant is changed on the organization
|
|
33
|
+
* (`platform.organizations.entitlements.set`), never through
|
|
34
|
+
* {@link PlatformEntitlementsModule.set}. A string on the wire: a newer
|
|
35
|
+
* engine may answer a source this SDK does not name yet.
|
|
36
|
+
*/
|
|
37
|
+
source: PlatformEntitlementSource | (string & {});
|
|
38
|
+
}
|
|
39
|
+
export interface SetPlatformEntitlementOptions {
|
|
40
|
+
/** `true` connects the plugin for the store; `false` removes the store's own grant. */
|
|
41
|
+
connected: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Operator notes, kept with the store's own grant. Omitted keeps the notes
|
|
44
|
+
* already stored; `''` clears them. Ignored on a disconnect, which deletes
|
|
45
|
+
* the row. At most 512 characters: the engine stores no more, and a longer
|
|
46
|
+
* value fails the write.
|
|
47
|
+
*/
|
|
48
|
+
notes?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* `admin.platform.entitlements` — which external plugins each store may use,
|
|
52
|
+
* read and switched by the platform's operators (spec §7, §9.2 `platform`).
|
|
53
|
+
*
|
|
54
|
+
* SuperAdmin with a HUMAN session only: an API token or an app credential is
|
|
55
|
+
* refused with `PLATFORM_REQUIRES_SESSION` (403) before anything is sent, and a
|
|
56
|
+
* session without SuperAdmin is refused `FORBIDDEN` (403) by the engine. The
|
|
57
|
+
* calls are platform-wide, so the client needs no tenant.
|
|
58
|
+
*
|
|
59
|
+
* Core plugins (search, email) are on for every store and never have a row.
|
|
60
|
+
* The engine reads these rows on every operation; the plugins service's own
|
|
61
|
+
* gates follow a change within its 60-second entitlement cache.
|
|
62
|
+
*/
|
|
63
|
+
export interface PlatformEntitlementsModule {
|
|
64
|
+
/**
|
|
65
|
+
* The plugins one store may use: its own grants and the ones its
|
|
66
|
+
* organization holds (`source`), by plugin key. The store's own row wins
|
|
67
|
+
* where both exist. An organization's workspace channel lists its
|
|
68
|
+
* organization's grants the same way. The engine does not check that the
|
|
69
|
+
* channel exists: an unknown id is not refused, it answers the rows stored
|
|
70
|
+
* under it (normally none).
|
|
71
|
+
*/
|
|
72
|
+
list(channelId: string, call?: Call): Promise<PlatformPluginEntitlement[]>;
|
|
73
|
+
/**
|
|
74
|
+
* Every grant across the platform. An organization grant appears once per
|
|
75
|
+
* member store and once for the organization's workspace
|
|
76
|
+
* (`source: 'organization'`, each under that channel's id), except where the
|
|
77
|
+
* channel holds the plugin itself.
|
|
78
|
+
*/
|
|
79
|
+
listAll(call?: Call): Promise<PlatformPluginEntitlement[]>;
|
|
80
|
+
/**
|
|
81
|
+
* Connect (`connected: true`) or disconnect (`connected: false`) an external
|
|
82
|
+
* plugin for one store.
|
|
83
|
+
*
|
|
84
|
+
* - Connecting answers the store's own row; connecting again refreshes
|
|
85
|
+
* `connectedAt` and `connectedByUserId` (and the notes, when given).
|
|
86
|
+
* - Disconnecting deletes the store's OWN row and answers `null`. A plugin
|
|
87
|
+
* the store's organization also grants stays connected through it: read
|
|
88
|
+
* {@link list} again to see it as `source: 'organization'`.
|
|
89
|
+
*
|
|
90
|
+
* Refusals: `MANAGED_BY_ORGANIZATION` (422 `BusinessRuleError`: the store
|
|
91
|
+
* holds the plugin only through its organization; revoke it there),
|
|
92
|
+
* `ORGANIZATION_WORKSPACE_NOT_A_STORE` (422: an organization's workspace
|
|
93
|
+
* cannot be connected on its own; grant the plugin to the organization),
|
|
94
|
+
* `USER_INPUT_ERROR` (400 `ValidationError`: a key that is not an external
|
|
95
|
+
* plugin of the catalog), `FORBIDDEN` (403). The engine does not check
|
|
96
|
+
* that `channelId` names a store, so pass an id read from the platform's
|
|
97
|
+
* channel list.
|
|
98
|
+
*
|
|
99
|
+
* Not deduplicated by an idempotency key (an Admin API mutation). A second
|
|
100
|
+
* connect refreshes the same row. A second disconnect answers `null` again,
|
|
101
|
+
* unless the organization also grants the plugin: the first removed the
|
|
102
|
+
* store's own grant, so the second is refused `MANAGED_BY_ORGANIZATION`.
|
|
103
|
+
* After a disconnect whose answer was lost, read {@link list} instead of
|
|
104
|
+
* repeating it.
|
|
105
|
+
*/
|
|
106
|
+
set(channelId: string, pluginKey: string, options: SetPlatformEntitlementOptions, call?: Call): Promise<PlatformPluginEntitlement | null>;
|
|
107
|
+
}
|
|
108
|
+
export declare const TENANT_PLUGIN_ENTITLEMENTS_QUERY = "\n query PlatformTenantPluginEntitlements($channelId: ID!) {\n tenantPluginEntitlements(channelId: $channelId) { \n id\n channelId\n pluginKey\n connectedAt\n connectedByUserId\n notes\n createdAt\n updatedAt\n source\n }\n }\n";
|
|
109
|
+
export declare const ALL_PLUGIN_ENTITLEMENTS_QUERY = "\n query PlatformAllPluginEntitlements {\n allPluginEntitlements { \n id\n channelId\n pluginKey\n connectedAt\n connectedByUserId\n notes\n createdAt\n updatedAt\n source\n }\n }\n";
|
|
110
|
+
export declare const SET_TENANT_PLUGIN_ENTITLEMENT_MUTATION = "\n mutation PlatformSetTenantPluginEntitlement(\n $channelId: ID!\n $pluginKey: String!\n $connected: Boolean!\n $notes: String\n ) {\n setTenantPluginEntitlement(\n channelId: $channelId\n pluginKey: $pluginKey\n connected: $connected\n notes: $notes\n ) { \n id\n channelId\n pluginKey\n connectedAt\n connectedByUserId\n notes\n createdAt\n updatedAt\n source\n }\n }\n";
|
|
111
|
+
export declare function createPlatformEntitlementsModule(transport: Transport, config: AdminClientConfig): PlatformEntitlementsModule;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { requireHumanSession } from './guard.js';
|
|
2
|
+
const ENTITLEMENT_FIELDS = /* GraphQL */ `
|
|
3
|
+
id
|
|
4
|
+
channelId
|
|
5
|
+
pluginKey
|
|
6
|
+
connectedAt
|
|
7
|
+
connectedByUserId
|
|
8
|
+
notes
|
|
9
|
+
createdAt
|
|
10
|
+
updatedAt
|
|
11
|
+
source
|
|
12
|
+
`;
|
|
13
|
+
export const TENANT_PLUGIN_ENTITLEMENTS_QUERY = /* GraphQL */ `
|
|
14
|
+
query PlatformTenantPluginEntitlements($channelId: ID!) {
|
|
15
|
+
tenantPluginEntitlements(channelId: $channelId) { ${ENTITLEMENT_FIELDS} }
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
export const ALL_PLUGIN_ENTITLEMENTS_QUERY = /* GraphQL */ `
|
|
19
|
+
query PlatformAllPluginEntitlements {
|
|
20
|
+
allPluginEntitlements { ${ENTITLEMENT_FIELDS} }
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
export const SET_TENANT_PLUGIN_ENTITLEMENT_MUTATION = /* GraphQL */ `
|
|
24
|
+
mutation PlatformSetTenantPluginEntitlement(
|
|
25
|
+
$channelId: ID!
|
|
26
|
+
$pluginKey: String!
|
|
27
|
+
$connected: Boolean!
|
|
28
|
+
$notes: String
|
|
29
|
+
) {
|
|
30
|
+
setTenantPluginEntitlement(
|
|
31
|
+
channelId: $channelId
|
|
32
|
+
pluginKey: $pluginKey
|
|
33
|
+
connected: $connected
|
|
34
|
+
notes: $notes
|
|
35
|
+
) { ${ENTITLEMENT_FIELDS} }
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
/** Only the fields this type promises, whatever else a newer engine adds to a row. */
|
|
39
|
+
function toEntitlement(row) {
|
|
40
|
+
return {
|
|
41
|
+
id: row.id,
|
|
42
|
+
channelId: row.channelId,
|
|
43
|
+
pluginKey: row.pluginKey,
|
|
44
|
+
connectedAt: row.connectedAt,
|
|
45
|
+
connectedByUserId: row.connectedByUserId,
|
|
46
|
+
notes: row.notes,
|
|
47
|
+
createdAt: row.createdAt,
|
|
48
|
+
updatedAt: row.updatedAt,
|
|
49
|
+
source: row.source,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function createPlatformEntitlementsModule(transport, config) {
|
|
53
|
+
// Platform-wide: a SuperAdmin names the store by id, so no tenant is needed.
|
|
54
|
+
// Scoped to a store instead, the transport would first resolve that store
|
|
55
|
+
// through the CMS channel mirror and fail on one missing there.
|
|
56
|
+
const unscoped = (call) => ({ ...call, allowUnscoped: true });
|
|
57
|
+
return {
|
|
58
|
+
async list(channelId, call) {
|
|
59
|
+
requireHumanSession(config);
|
|
60
|
+
const data = await transport.graphql({ api: 'admin', query: TENANT_PLUGIN_ENTITLEMENTS_QUERY, variables: { channelId } }, unscoped(call));
|
|
61
|
+
return data.tenantPluginEntitlements.map(toEntitlement);
|
|
62
|
+
},
|
|
63
|
+
async listAll(call) {
|
|
64
|
+
requireHumanSession(config);
|
|
65
|
+
const data = await transport.graphql({ api: 'admin', query: ALL_PLUGIN_ENTITLEMENTS_QUERY }, unscoped(call));
|
|
66
|
+
return data.allPluginEntitlements.map(toEntitlement);
|
|
67
|
+
},
|
|
68
|
+
async set(channelId, pluginKey, options, call) {
|
|
69
|
+
requireHumanSession(config);
|
|
70
|
+
const data = await transport.graphql({
|
|
71
|
+
api: 'admin',
|
|
72
|
+
query: SET_TENANT_PLUGIN_ENTITLEMENT_MUTATION,
|
|
73
|
+
variables: {
|
|
74
|
+
channelId,
|
|
75
|
+
pluginKey,
|
|
76
|
+
connected: options.connected,
|
|
77
|
+
// The engine reads null as "keep the stored notes"; '' is a value
|
|
78
|
+
// that clears them, so it must not collapse into null here.
|
|
79
|
+
notes: options.notes ?? null,
|
|
80
|
+
},
|
|
81
|
+
}, unscoped(call));
|
|
82
|
+
const row = data.setTenantPluginEntitlement;
|
|
83
|
+
return row === null ? null : toEntitlement(row);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|