@sixtyninecommerce/admin 0.10.0 → 0.12.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 +5772 -118
- package/dist/auth/index.d.ts +7 -1
- package/dist/auth/index.js +8 -1
- package/dist/catalog/assets.d.ts +120 -8
- package/dist/catalog/assets.js +98 -12
- package/dist/catalog/collections.d.ts +279 -14
- package/dist/catalog/collections.js +246 -15
- package/dist/catalog/documents.d.ts +142 -0
- package/dist/catalog/documents.js +114 -0
- package/dist/catalog/index.d.ts +54 -6
- package/dist/catalog/index.js +10 -1
- package/dist/catalog/metadata.d.ts +244 -0
- package/dist/catalog/metadata.js +220 -0
- package/dist/catalog/products.d.ts +159 -19
- package/dist/catalog/products.js +88 -13
- package/dist/catalog/properties.d.ts +140 -14
- package/dist/catalog/properties.js +65 -1
- package/dist/catalog/recommendations.d.ts +288 -0
- package/dist/catalog/recommendations.js +237 -0
- package/dist/catalog/reviews.d.ts +381 -0
- package/dist/catalog/reviews.js +325 -0
- package/dist/catalog/types.d.ts +17 -1
- package/dist/catalog/types.js +10 -0
- package/dist/catalog/variant-find.d.ts +136 -0
- package/dist/catalog/variant-find.js +239 -0
- package/dist/catalog/variants.d.ts +293 -26
- package/dist/catalog/variants.js +164 -7
- package/dist/catalog/visibility.d.ts +38 -7
- package/dist/catalog/visibility.js +19 -1
- package/dist/client.d.ts +121 -9
- package/dist/client.js +53 -14
- package/dist/content/assets.d.ts +285 -0
- package/dist/content/assets.js +181 -0
- package/dist/content/forms.d.ts +332 -1
- package/dist/content/forms.js +140 -4
- package/dist/content/items.d.ts +292 -0
- package/dist/content/items.js +170 -0
- package/dist/content/menus.d.ts +254 -0
- package/dist/content/menus.js +70 -0
- package/dist/content/strings.d.ts +137 -0
- package/dist/content/strings.js +82 -0
- package/dist/content/types.d.ts +282 -0
- package/dist/content/types.js +100 -0
- package/dist/customers/age-verification.d.ts +66 -0
- package/dist/customers/age-verification.js +79 -0
- package/dist/customers/credit.d.ts +263 -0
- package/dist/customers/credit.js +244 -0
- package/dist/customers/delivery-preferences.d.ts +120 -0
- package/dist/customers/delivery-preferences.js +97 -0
- package/dist/customers/delivery-terms.d.ts +44 -0
- package/dist/customers/delivery-terms.js +32 -0
- package/dist/customers/groups.d.ts +207 -0
- package/dist/customers/groups.js +438 -0
- package/dist/customers/index.d.ts +462 -37
- package/dist/customers/index.js +317 -40
- package/dist/customers/invitation-status.d.ts +57 -0
- package/dist/customers/invitation-status.js +21 -0
- package/dist/customers/notes.d.ts +67 -0
- package/dist/customers/notes.js +51 -0
- package/dist/customers/notification-recipients.d.ts +97 -0
- package/dist/customers/notification-recipients.js +84 -0
- package/dist/customers/saved-lists.d.ts +42 -0
- package/dist/customers/saved-lists.js +61 -0
- package/dist/customers/vat.d.ts +83 -0
- package/dist/customers/vat.js +46 -0
- package/dist/customers/wire.d.ts +20 -0
- package/dist/customers/wire.js +35 -0
- package/dist/documents/ar.d.ts +307 -0
- package/dist/documents/ar.js +254 -0
- package/dist/documents/emails.d.ts +150 -0
- package/dist/documents/emails.js +220 -0
- package/dist/documents/index.d.ts +744 -21
- package/dist/documents/index.js +438 -24
- package/dist/documents/pdf.d.ts +2 -0
- package/dist/documents/pdf.js +11 -0
- package/dist/fulfillments/create.d.ts +167 -0
- package/dist/fulfillments/create.js +216 -0
- package/dist/fulfillments/index.d.ts +163 -11
- package/dist/fulfillments/index.js +135 -6
- package/dist/fulfillments/payment-policy.d.ts +64 -0
- package/dist/fulfillments/payment-policy.js +140 -0
- package/dist/fulfillments/warehouse.d.ts +724 -0
- package/dist/fulfillments/warehouse.js +914 -0
- package/dist/geo/index.d.ts +119 -0
- package/dist/geo/index.js +107 -0
- package/dist/index.d.ts +147 -39
- package/dist/index.js +51 -12
- package/dist/integrations/afas-actions.d.ts +234 -0
- package/dist/integrations/afas-actions.js +310 -0
- package/dist/integrations/afas-types.d.ts +483 -0
- package/dist/integrations/afas-types.js +5 -0
- package/dist/integrations/afas.d.ts +234 -0
- package/dist/integrations/afas.js +290 -0
- package/dist/integrations/connections.d.ts +103 -0
- package/dist/integrations/connections.js +55 -0
- package/dist/integrations/esp.d.ts +313 -0
- package/dist/integrations/esp.js +169 -0
- package/dist/integrations/exact-online-types.d.ts +965 -0
- package/dist/integrations/exact-online-types.js +6 -0
- package/dist/integrations/exact-online.d.ts +784 -0
- package/dist/integrations/exact-online.js +1089 -0
- package/dist/integrations/index.d.ts +44 -3
- package/dist/integrations/index.js +23 -3
- package/dist/integrations/pipeline-discoveries.d.ts +65 -0
- package/dist/integrations/pipeline-discoveries.js +65 -0
- package/dist/integrations/pipeline-keys.d.ts +13 -0
- package/dist/integrations/pipeline-keys.js +22 -0
- package/dist/integrations/pipeline-runs.d.ts +119 -0
- package/dist/integrations/pipeline-runs.js +87 -0
- package/dist/integrations/pipelines.d.ts +87 -15
- package/dist/integrations/pipelines.js +41 -22
- package/dist/integrations/price-import.d.ts +223 -0
- package/dist/integrations/price-import.js +124 -0
- package/dist/integrations/types.d.ts +367 -3
- package/dist/integrations/types.js +4 -3
- package/dist/locations/index.d.ts +66 -0
- package/dist/locations/index.js +31 -0
- package/dist/meta/index.d.ts +172 -0
- package/dist/metafields/index.d.ts +8 -1
- package/dist/metafields/index.js +10 -0
- package/dist/orders/cancel-with-money.d.ts +237 -0
- package/dist/orders/cancel-with-money.js +155 -0
- package/dist/orders/commit-edit.d.ts +23 -1
- package/dist/orders/commit-edit.js +19 -2
- package/dist/orders/deposit-policy.d.ts +3 -1
- package/dist/orders/drafts.d.ts +800 -0
- package/dist/orders/drafts.js +771 -0
- package/dist/orders/fields.d.ts +245 -0
- package/dist/orders/fields.js +188 -0
- package/dist/orders/get.d.ts +29 -19
- package/dist/orders/get.js +17 -8
- package/dist/orders/index.d.ts +66 -15
- package/dist/orders/index.js +25 -2
- package/dist/orders/lifecycle.d.ts +128 -0
- package/dist/orders/lifecycle.js +158 -0
- package/dist/orders/line-price-preview.d.ts +7 -2
- package/dist/orders/list.d.ts +243 -0
- package/dist/orders/list.js +487 -0
- package/dist/orders/manual-discounts.d.ts +13 -5
- package/dist/orders/manual-pricing.d.ts +5 -0
- package/dist/orders/money-view.d.ts +149 -0
- package/dist/orders/money-view.js +99 -0
- package/dist/orders/order-entry.d.ts +20 -7
- package/dist/orders/plan-edit.d.ts +34 -2
- package/dist/orders/plan-edit.js +112 -11
- package/dist/orders/settings.d.ts +201 -0
- package/dist/orders/settings.js +325 -0
- package/dist/orders/status.d.ts +159 -13
- package/dist/orders/status.js +21 -0
- package/dist/orders/surcharges.d.ts +20 -1
- package/dist/orders/surcharges.js +37 -11
- package/dist/payments/account-credit.d.ts +46 -0
- package/dist/payments/account-credit.js +42 -0
- package/dist/payments/ar.d.ts +292 -0
- package/dist/payments/ar.js +223 -0
- package/dist/payments/connected-account.d.ts +47 -0
- package/dist/payments/connected-account.js +61 -0
- package/dist/payments/credit-noted-fields.d.ts +19 -0
- package/dist/payments/credit-noted-fields.js +69 -0
- package/dist/payments/customer-consent.d.ts +30 -0
- package/dist/payments/customer-consent.js +49 -0
- package/dist/payments/index.d.ts +340 -19
- package/dist/payments/index.js +151 -1
- package/dist/payments/methods.d.ts +303 -8
- package/dist/payments/methods.js +337 -7
- package/dist/payments/mollie-methods.d.ts +159 -0
- package/dist/payments/mollie-methods.js +141 -0
- package/dist/payments/payment-links.d.ts +180 -0
- package/dist/payments/payment-links.js +97 -0
- package/dist/payments/provider-keys.d.ts +150 -0
- package/dist/payments/provider-keys.js +118 -0
- package/dist/payments/providers.d.ts +86 -11
- package/dist/payments/providers.js +29 -0
- package/dist/payments/requests.d.ts +147 -0
- package/dist/payments/requests.js +93 -0
- package/dist/payments/store-credit-fields.d.ts +15 -0
- package/dist/payments/store-credit-fields.js +51 -0
- package/dist/pickups/index.d.ts +106 -0
- package/dist/pickups/index.js +108 -0
- package/dist/platform/channels.d.ts +132 -0
- package/dist/platform/channels.js +128 -0
- package/dist/platform/email-gateways.d.ts +165 -0
- package/dist/platform/email-gateways.js +78 -0
- package/dist/platform/geo.d.ts +123 -0
- package/dist/platform/geo.js +144 -0
- package/dist/platform/tax.d.ts +162 -0
- package/dist/platform/tax.js +169 -0
- package/dist/pos/anomalies.d.ts +158 -0
- package/dist/pos/anomalies.js +64 -0
- package/dist/pos/assisted-selling.d.ts +79 -0
- package/dist/pos/assisted-selling.js +38 -0
- package/dist/pos/exchanges.d.ts +108 -0
- package/dist/pos/exchanges.js +22 -0
- package/dist/pos/gift-cards.d.ts +135 -0
- package/dist/pos/gift-cards.js +47 -0
- package/dist/pos/index.d.ts +2370 -58
- package/dist/pos/index.js +854 -7
- package/dist/pos/no-receipt.d.ts +94 -0
- package/dist/pos/no-receipt.js +25 -0
- package/dist/pos/offline-sales.d.ts +228 -0
- package/dist/pos/offline-sales.js +170 -0
- package/dist/pos/pay-by-link.d.ts +143 -0
- package/dist/pos/pay-by-link.js +57 -0
- package/dist/pos/quick-keys.d.ts +103 -0
- package/dist/pos/quick-keys.js +45 -0
- package/dist/pos/reports.d.ts +360 -0
- package/dist/pos/reports.js +62 -0
- package/dist/pos/returns.d.ts +244 -0
- package/dist/pos/returns.js +83 -0
- package/dist/pricing/bulk-coupons.d.ts +139 -0
- package/dist/pricing/bulk-coupons.js +112 -0
- package/dist/pricing/index.d.ts +174 -4
- package/dist/pricing/index.js +174 -1
- package/dist/pricing/price-lists.d.ts +606 -0
- package/dist/pricing/price-lists.js +600 -0
- package/dist/purchasing/alerts.d.ts +104 -0
- package/dist/purchasing/alerts.js +88 -0
- package/dist/purchasing/backorders.d.ts +61 -0
- package/dist/purchasing/backorders.js +92 -0
- package/dist/purchasing/cost.d.ts +110 -0
- package/dist/purchasing/cost.js +86 -0
- package/dist/purchasing/index.d.ts +54 -0
- package/dist/purchasing/index.js +24 -0
- package/dist/purchasing/links.d.ts +263 -0
- package/dist/purchasing/links.js +192 -0
- package/dist/purchasing/purchase-orders.d.ts +551 -0
- package/dist/purchasing/purchase-orders.js +491 -0
- package/dist/purchasing/receipts.d.ts +396 -0
- package/dist/purchasing/receipts.js +287 -0
- package/dist/purchasing/replenishment.d.ts +281 -0
- package/dist/purchasing/replenishment.js +240 -0
- package/dist/purchasing/returns.d.ts +273 -0
- package/dist/purchasing/returns.js +229 -0
- package/dist/purchasing/settings.d.ts +152 -0
- package/dist/purchasing/settings.js +181 -0
- package/dist/purchasing/suppliers.d.ts +476 -0
- package/dist/purchasing/suppliers.js +438 -0
- package/dist/quotes/index.d.ts +518 -0
- package/dist/quotes/index.js +439 -0
- package/dist/reports/index.d.ts +71 -8
- package/dist/reports/index.js +74 -11
- package/dist/returns/index.d.ts +675 -21
- package/dist/returns/index.js +756 -11
- package/dist/search/index.d.ts +76 -1
- package/dist/search/index.js +63 -0
- package/dist/seo/config.d.ts +128 -0
- package/dist/seo/config.js +95 -0
- package/dist/seo/redirects.d.ts +125 -0
- package/dist/seo/redirects.js +60 -1
- package/dist/seo/settings.d.ts +147 -0
- package/dist/seo/settings.js +209 -0
- package/dist/shipping/definitions.d.ts +29 -0
- package/dist/shipping/definitions.js +47 -0
- package/dist/shipping/fulfillment-profiles.d.ts +174 -0
- package/dist/shipping/fulfillment-profiles.js +104 -0
- package/dist/shipping/hendriks.d.ts +97 -0
- package/dist/shipping/hendriks.js +50 -0
- package/dist/shipping/index.d.ts +54 -0
- package/dist/shipping/index.js +27 -0
- package/dist/shipping/methods.d.ts +205 -0
- package/dist/shipping/methods.js +240 -0
- package/dist/shipping/myparcel.d.ts +668 -0
- package/dist/shipping/myparcel.js +466 -0
- package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
- package/dist/shipping/parcel-rule.d.ts +6 -5
- package/dist/shipping/parcel-rule.js +21 -23
- package/dist/shipping/rate-tables.d.ts +324 -0
- package/dist/shipping/rate-tables.js +390 -0
- package/dist/shipping/ship-to-countries.d.ts +75 -0
- package/dist/shipping/ship-to-countries.js +47 -0
- package/dist/stock/by-location.d.ts +86 -0
- package/dist/stock/by-location.js +94 -0
- package/dist/stock/index.d.ts +18 -4
- package/dist/stock/index.js +7 -0
- package/dist/stock/locations.d.ts +144 -0
- package/dist/stock/locations.js +168 -0
- package/dist/stock/settings.d.ts +106 -0
- package/dist/stock/settings.js +103 -0
- package/dist/stock/transfers.d.ts +386 -52
- package/dist/stock/transfers.js +222 -17
- package/dist/stored-value/index.d.ts +312 -11
- package/dist/stored-value/index.js +223 -4
- package/dist/subscriptions/index.d.ts +682 -0
- package/dist/subscriptions/index.js +815 -0
- package/dist/tax/display-policy.d.ts +105 -0
- package/dist/tax/display-policy.js +84 -0
- package/dist/tax/index.d.ts +79 -1
- package/dist/tax/index.js +52 -0
- package/dist/tenant/email-style.d.ts +102 -0
- package/dist/tenant/email-style.js +72 -0
- package/dist/tenant/email-templates.d.ts +287 -0
- package/dist/tenant/email-templates.js +271 -0
- package/dist/tenant/notifications.d.ts +23 -17
- package/dist/tenant/settings-sources.d.ts +1 -1
- package/dist/tenant/time-zone.d.ts +17 -0
- package/dist/tenant/time-zone.js +11 -0
- package/dist/types.d.ts +50 -3
- package/dist/users/index.d.ts +3 -0
- package/dist/users/index.js +2 -0
- package/dist/users/warehouses.d.ts +50 -0
- package/dist/users/warehouses.js +51 -0
- package/package.json +2 -2
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { redactionWatcher } from '../customer-data-redaction.js';
|
|
2
|
+
/**
|
|
3
|
+
* Store pickup (click & collect, POS C1/33): webshop orders a shopper collects at one of the
|
|
4
|
+
* store's shops. Placing such an order writes its pickup (`preparing`); "ready" starts the hold
|
|
5
|
+
* and emails the customer once; the shop's till hands it over (`collected`); a ready pickup not
|
|
6
|
+
* collected within the location's hold days goes back to stock (`expired`), an expired one the
|
|
7
|
+
* shop's till took off its queue is `archived` (its order is unchanged: cancel or refund it here;
|
|
8
|
+
* a late customer still collects it), and the pickup of a cancelled order is `cancelled`. A
|
|
9
|
+
* location's settings are `admin.locations.pickup/setPickup`.
|
|
10
|
+
*/
|
|
11
|
+
/** The statuses a pickup has today. More may come: read any other value as itself. */
|
|
12
|
+
export const STORE_PICKUP_STATUSES = [
|
|
13
|
+
'preparing',
|
|
14
|
+
'ready',
|
|
15
|
+
'collected',
|
|
16
|
+
'expired',
|
|
17
|
+
'archived',
|
|
18
|
+
'cancelled',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Why the release at expiry moved nothing (D23): `invoice_locked`, the order is invoiced and
|
|
22
|
+
* locked; `order_state`, the order is past the states a reservation is released from. More may
|
|
23
|
+
* come.
|
|
24
|
+
*/
|
|
25
|
+
export const STORE_PICKUP_RELEASE_BLOCKED = ['invoice_locked', 'order_state'];
|
|
26
|
+
const STORE_PICKUP_FIELDS = /* GraphQL */ `
|
|
27
|
+
id
|
|
28
|
+
orderId
|
|
29
|
+
orderCode
|
|
30
|
+
stockLocationId
|
|
31
|
+
stockLocationName
|
|
32
|
+
status
|
|
33
|
+
customerName
|
|
34
|
+
totalQuantity
|
|
35
|
+
stockShort
|
|
36
|
+
placedAt
|
|
37
|
+
readyAt
|
|
38
|
+
holdUntil
|
|
39
|
+
collectedAt
|
|
40
|
+
expiredAt
|
|
41
|
+
releaseBlocked
|
|
42
|
+
createdAt
|
|
43
|
+
`;
|
|
44
|
+
export const STORE_PICKUPS_QUERY = /* GraphQL */ `
|
|
45
|
+
query StorePickups($input: StorePickupListInput) {
|
|
46
|
+
storePickups(input: $input) {
|
|
47
|
+
items { ${STORE_PICKUP_FIELDS} }
|
|
48
|
+
totalItems
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
export const STORE_PICKUP_FOR_ORDER_QUERY = /* GraphQL */ `
|
|
53
|
+
query StorePickupForOrder($orderId: ID!) {
|
|
54
|
+
storePickupForOrder(orderId: $orderId) { ${STORE_PICKUP_FIELDS} }
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
export const MARK_STORE_PICKUP_READY_MUTATION = /* GraphQL */ `
|
|
58
|
+
mutation MarkStorePickupReady($id: ID!) {
|
|
59
|
+
markStorePickupReady(id: $id) { ${STORE_PICKUP_FIELDS} }
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
export function createPickupsModule(transport) {
|
|
63
|
+
return {
|
|
64
|
+
async list(filter, options) {
|
|
65
|
+
const redaction = redactionWatcher();
|
|
66
|
+
// Only what the caller set: an absent key leaves the engine's default (50, every status).
|
|
67
|
+
const input = {};
|
|
68
|
+
if (filter?.stockLocationId !== undefined)
|
|
69
|
+
input.stockLocationId = filter.stockLocationId;
|
|
70
|
+
if (filter?.statuses !== undefined)
|
|
71
|
+
input.statuses = [...filter.statuses];
|
|
72
|
+
if (filter?.take !== undefined)
|
|
73
|
+
input.take = filter.take;
|
|
74
|
+
if (filter?.skip !== undefined)
|
|
75
|
+
input.skip = filter.skip;
|
|
76
|
+
const data = await transport.graphql({
|
|
77
|
+
api: 'admin',
|
|
78
|
+
query: STORE_PICKUPS_QUERY,
|
|
79
|
+
variables: { input },
|
|
80
|
+
onExtensions: redaction.onExtensions,
|
|
81
|
+
}, options);
|
|
82
|
+
return {
|
|
83
|
+
items: data.storePickups.items.map((item) => redaction.mark(item)),
|
|
84
|
+
totalItems: data.storePickups.totalItems,
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
async forOrder(orderId, options) {
|
|
88
|
+
const redaction = redactionWatcher();
|
|
89
|
+
const data = await transport.graphql({
|
|
90
|
+
api: 'admin',
|
|
91
|
+
query: STORE_PICKUP_FOR_ORDER_QUERY,
|
|
92
|
+
variables: { orderId },
|
|
93
|
+
onExtensions: redaction.onExtensions,
|
|
94
|
+
}, options);
|
|
95
|
+
return redaction.mark(data.storePickupForOrder);
|
|
96
|
+
},
|
|
97
|
+
async markReady(id, options) {
|
|
98
|
+
const redaction = redactionWatcher();
|
|
99
|
+
const data = await transport.graphql({
|
|
100
|
+
api: 'admin',
|
|
101
|
+
query: MARK_STORE_PICKUP_READY_MUTATION,
|
|
102
|
+
variables: { id },
|
|
103
|
+
onExtensions: redaction.onExtensions,
|
|
104
|
+
}, options);
|
|
105
|
+
return redaction.mark(data.markStorePickupReady);
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { type Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { AdminClientConfig } from '../client.js';
|
|
3
|
+
import { type JobHandle } from '../jobs/index.js';
|
|
4
|
+
import type { Call } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Why a code is held:
|
|
7
|
+
* - `rename-source`: the code a rename is moving OFF, held until its files are moved forward.
|
|
8
|
+
* - `rename-target`: the code a rename is moving ONTO.
|
|
9
|
+
* - `rename-alias`: a renamed store's old code, still answering for its storefronts until
|
|
10
|
+
* `expiresAt`.
|
|
11
|
+
*/
|
|
12
|
+
export type ChannelCodeHoldReason = 'rename-source' | 'rename-target' | 'rename-alias';
|
|
13
|
+
/** What a hold's rename job is doing: `running` blocks a release; the rest are releasable. */
|
|
14
|
+
export type ChannelCodeHoldActivity = 'running' | 'stalled' | 'finished' | 'no-job';
|
|
15
|
+
/** Why a release ended without freeing the code. The hold stays; releasing again retries. */
|
|
16
|
+
export interface ChannelCodeReleaseFailure {
|
|
17
|
+
/**
|
|
18
|
+
* `CLEANUP_FAILED` (the cleanup failed, reported errors, or stopped short), `CLEANUP_JOB_LOST`
|
|
19
|
+
* (the plugins service no longer knows the cleanup), `CLEANUP_LEFT_OBJECTS` (objects remained
|
|
20
|
+
* after a cleanup that reported success), `CLEANUP_TIMED_OUT` (not decided within 24 hours), or
|
|
21
|
+
* `CHANNEL_CODE_TAKEN` (another store took the code meanwhile).
|
|
22
|
+
*/
|
|
23
|
+
code: string;
|
|
24
|
+
message: string;
|
|
25
|
+
}
|
|
26
|
+
/** A code a rename still holds against reuse, with what is happening to it. */
|
|
27
|
+
export interface ChannelCodeReservation {
|
|
28
|
+
code: string;
|
|
29
|
+
channelId: string | null;
|
|
30
|
+
/** The code that store carries NOW; null when the store is gone. */
|
|
31
|
+
channelCode: string | null;
|
|
32
|
+
reason: ChannelCodeHoldReason;
|
|
33
|
+
jobId: string | null;
|
|
34
|
+
jobState: string | null;
|
|
35
|
+
/** ISO-8601 UTC. */
|
|
36
|
+
jobUpdatedAt: string | null;
|
|
37
|
+
/** The rename stopped moving for longer than the recovery window. */
|
|
38
|
+
jobStale: boolean;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
/** `rename-alias` only: when the old code stops answering and frees. */
|
|
41
|
+
expiresAt: string | null;
|
|
42
|
+
activity: ChannelCodeHoldActivity;
|
|
43
|
+
/** Whether {@link PlatformChannelsModule.releaseCode} may be asked now. */
|
|
44
|
+
releasable: boolean;
|
|
45
|
+
/** When a release started the cleanup it waits on (or the one that failed). */
|
|
46
|
+
releaseRequestedAt: string | null;
|
|
47
|
+
releaseCleanupJobId: string | null;
|
|
48
|
+
releaseCleanupMode: 'drain-source' | 'delete-source' | null;
|
|
49
|
+
/** Set when the last release ended without freeing the code; null while one is pending. */
|
|
50
|
+
releaseFailure: ChannelCodeReleaseFailure | null;
|
|
51
|
+
}
|
|
52
|
+
/** How a release ended when it ended well. */
|
|
53
|
+
export type ChannelCodeReleaseOutcome = 'released' | 'aliased';
|
|
54
|
+
export interface ChannelCodeRelease {
|
|
55
|
+
code: string;
|
|
56
|
+
/**
|
|
57
|
+
* - `released`: the code is free now.
|
|
58
|
+
* - `aliased`: the rename behind the hold had committed and its old code still answers for the
|
|
59
|
+
* renamed store, so the hold became that old-code alias. Release it again to end it early.
|
|
60
|
+
* - `pending`: files remained, so the engine started a cleanup (or one already was running).
|
|
61
|
+
* The engine finishes the release itself once the cleanup ends; nobody has to stay and call
|
|
62
|
+
* again. Follow it with `handle.wait()`, or read `codeReservations()` later.
|
|
63
|
+
*/
|
|
64
|
+
outcome: ChannelCodeReleaseOutcome | 'pending';
|
|
65
|
+
/** The cleanup a `pending` release waits on; null otherwise. */
|
|
66
|
+
cleanup: {
|
|
67
|
+
jobId: string;
|
|
68
|
+
mode: 'drain-source' | 'delete-source' | null;
|
|
69
|
+
/** Files under the prefix when the cleanup started; null for a repeat of a pending release. */
|
|
70
|
+
objectsRemaining: number | null;
|
|
71
|
+
} | null;
|
|
72
|
+
/**
|
|
73
|
+
* `runId` is the cleanup's job id when there is one, else the code. `wait()` resolves at once
|
|
74
|
+
* for a release that already ended; for a `pending` one it polls {@link
|
|
75
|
+
* PlatformChannelsModule.codeReservations} until the code is no longer held (`released`), is
|
|
76
|
+
* held as an old-code alias (`aliased`), or shows a `releaseFailure`, which it throws as
|
|
77
|
+
* `CHANNEL_CODE_RELEASE_FAILED` (a {@link BusinessRuleError}, 422) with the failure's code in
|
|
78
|
+
* `details[0].code`. Defaults: a read every 5 s, giving up after 15 minutes with
|
|
79
|
+
* `CHANNEL_CODE_RELEASE_TIMEOUT` (retryable: the release goes on regardless).
|
|
80
|
+
*
|
|
81
|
+
* When a `pending` release ends as the alias while nobody is calling (the engine's worker
|
|
82
|
+
* finished it), the engine remembers that ending until a release has answered it: the next
|
|
83
|
+
* `releaseCode` answers `aliased` again and ends nothing. This keeps an older dashboard's
|
|
84
|
+
* automatic second call from cutting the alias window short. A release that answered `aliased`
|
|
85
|
+
* itself leaves nothing to answer. Either way, to end the alias early, call `releaseCode` until
|
|
86
|
+
* it answers `released`.
|
|
87
|
+
*/
|
|
88
|
+
handle: JobHandle<ChannelCodeReleaseOutcome>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* `admin.platform.channels` — the codes renames hold against reuse, and their release. A human
|
|
92
|
+
* SuperAdmin session only: an API token or an app credential is refused before anything is sent
|
|
93
|
+
* (`PLATFORM_REQUIRES_SESSION`), and the engine answers anyone else `FORBIDDEN`.
|
|
94
|
+
*/
|
|
95
|
+
export interface PlatformChannelsModule {
|
|
96
|
+
/** Every live hold on the platform, oldest first. */
|
|
97
|
+
codeReservations(options?: Call): Promise<ChannelCodeReservation[]>;
|
|
98
|
+
/**
|
|
99
|
+
* Free a held code, or start the cleanup that makes that safe. Refusals:
|
|
100
|
+
* - `CHANNEL_CODE_HOLD_NOT_FOUND` (404): nothing holds that code.
|
|
101
|
+
* - `CHANNEL_CODE_RELEASE_CONFLICT` (409): something live is in the way; `details[0].code` is
|
|
102
|
+
* `rename_queued`, `rename_running`, `rename_stalled_postponed`, `move_running` or
|
|
103
|
+
* `cdn_route`.
|
|
104
|
+
* - `CHANNEL_CODE_TAKEN` (409): another store carries the code.
|
|
105
|
+
* - `CHANNEL_CODE_RELEASE_UNVERIFIED` (422): the object store, the store's object moves, the
|
|
106
|
+
* CDN route, the database or the content service could not be read. Nothing changed; it is
|
|
107
|
+
* safe to try again (`details[0].code` names what).
|
|
108
|
+
*
|
|
109
|
+
* - `CHANNEL_CODE_RELEASE_FAILED` (422): a repeat of a pending release found it ended without
|
|
110
|
+
* freeing the code; `details[0].code` is the failure's code (`CLEANUP_…`).
|
|
111
|
+
*
|
|
112
|
+
* A repeat while a release is pending starts nothing: it finishes the release if its cleanup
|
|
113
|
+
* has ended, and answers `released`, `aliased`, the same `pending` while the cleanup runs, or
|
|
114
|
+
* the failure above. A cleanup that has ended but whose release cannot finish right now is
|
|
115
|
+
* refused with the reason (`CHANNEL_CODE_RELEASE_CONFLICT` or `…_UNVERIFIED`). A repeat after
|
|
116
|
+
* a failure has been answered is a fresh attempt.
|
|
117
|
+
*/
|
|
118
|
+
releaseCode(code: string, options?: Call): Promise<ChannelCodeRelease>;
|
|
119
|
+
}
|
|
120
|
+
export declare const CHANNEL_CODE_RESERVATIONS_QUERY = "\n query PlatformChannelCodeReservations {\n channelCodeReservations { \n code\n channelId\n channelCode\n reason\n jobId\n jobState\n jobUpdatedAt\n jobStale\n createdAt\n expiresAt\n activity\n releasable\n releaseRequestedAt\n releaseCleanupJobId\n releaseCleanupMode\n releaseFailure {\n code\n message\n }\n }\n }\n";
|
|
121
|
+
export declare const RELEASE_CHANNEL_CODE_MUTATION = "\n mutation PlatformReleaseChannelCode($code: String!) {\n releaseChannelCode(code: $code) {\n code\n outcome\n cleanupJobId\n cleanupMode\n objectsRemaining\n }\n }\n";
|
|
122
|
+
/** Where a release stands, read from the holds: still pending, or how it ended. */
|
|
123
|
+
export declare function releaseStateOf(code: string, holds: readonly ChannelCodeReservation[]): {
|
|
124
|
+
state: 'pending';
|
|
125
|
+
} | {
|
|
126
|
+
state: 'ended';
|
|
127
|
+
outcome: ChannelCodeReleaseOutcome;
|
|
128
|
+
} | {
|
|
129
|
+
state: 'failed';
|
|
130
|
+
failure: ChannelCodeReleaseFailure;
|
|
131
|
+
};
|
|
132
|
+
export declare function createPlatformChannelsModule(transport: Transport, config: AdminClientConfig): PlatformChannelsModule;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { BusinessRuleError, TransportError } from '@sixtyninecommerce/core';
|
|
2
|
+
import { pollUntilTerminal } from '../jobs/index.js';
|
|
3
|
+
import { requireHumanSession } from './guard.js';
|
|
4
|
+
const RESERVATION_FIELDS = /* GraphQL */ `
|
|
5
|
+
code
|
|
6
|
+
channelId
|
|
7
|
+
channelCode
|
|
8
|
+
reason
|
|
9
|
+
jobId
|
|
10
|
+
jobState
|
|
11
|
+
jobUpdatedAt
|
|
12
|
+
jobStale
|
|
13
|
+
createdAt
|
|
14
|
+
expiresAt
|
|
15
|
+
activity
|
|
16
|
+
releasable
|
|
17
|
+
releaseRequestedAt
|
|
18
|
+
releaseCleanupJobId
|
|
19
|
+
releaseCleanupMode
|
|
20
|
+
releaseFailure {
|
|
21
|
+
code
|
|
22
|
+
message
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
export const CHANNEL_CODE_RESERVATIONS_QUERY = /* GraphQL */ `
|
|
26
|
+
query PlatformChannelCodeReservations {
|
|
27
|
+
channelCodeReservations { ${RESERVATION_FIELDS} }
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
export const RELEASE_CHANNEL_CODE_MUTATION = /* GraphQL */ `
|
|
31
|
+
mutation PlatformReleaseChannelCode($code: String!) {
|
|
32
|
+
releaseChannelCode(code: $code) {
|
|
33
|
+
code
|
|
34
|
+
outcome
|
|
35
|
+
cleanupJobId
|
|
36
|
+
cleanupMode
|
|
37
|
+
objectsRemaining
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
const DEFAULT_WAIT_INTERVAL_MS = 5_000;
|
|
42
|
+
const DEFAULT_WAIT_TIMEOUT_MS = 15 * 60_000;
|
|
43
|
+
/** Where a release stands, read from the holds: still pending, or how it ended. */
|
|
44
|
+
export function releaseStateOf(code, holds) {
|
|
45
|
+
const key = code.trim().toLowerCase();
|
|
46
|
+
const hold = holds.find((candidate) => candidate.code.toLowerCase() === key);
|
|
47
|
+
if (!hold)
|
|
48
|
+
return { state: 'ended', outcome: 'released' };
|
|
49
|
+
if (hold.reason === 'rename-alias')
|
|
50
|
+
return { state: 'ended', outcome: 'aliased' };
|
|
51
|
+
if (hold.releaseFailure)
|
|
52
|
+
return { state: 'failed', failure: hold.releaseFailure };
|
|
53
|
+
return { state: 'pending' };
|
|
54
|
+
}
|
|
55
|
+
export function createPlatformChannelsModule(transport, config) {
|
|
56
|
+
// Platform-wide: a SuperAdmin needs no tenant on the client.
|
|
57
|
+
const unscoped = (call) => ({ ...call, allowUnscoped: true });
|
|
58
|
+
async function codeReservations(options) {
|
|
59
|
+
requireHumanSession(config);
|
|
60
|
+
const data = await transport.graphql({ api: 'admin', query: CHANNEL_CODE_RESERVATIONS_QUERY }, unscoped(options));
|
|
61
|
+
return data.channelCodeReservations;
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
codeReservations,
|
|
65
|
+
async releaseCode(code, options) {
|
|
66
|
+
requireHumanSession(config);
|
|
67
|
+
const data = await transport.graphql({ api: 'admin', query: RELEASE_CHANNEL_CODE_MUTATION, variables: { code } }, unscoped(options));
|
|
68
|
+
const wire = data.releaseChannelCode;
|
|
69
|
+
// `cleanup-started` is what an engine before the pending release answered: the cleanup it
|
|
70
|
+
// started is then NOT finished for you, so it is reported as pending and followed the same
|
|
71
|
+
// way, and `wait()` times out unless something calls the release again.
|
|
72
|
+
const pending = wire.outcome === 'pending' || wire.outcome === 'cleanup-started';
|
|
73
|
+
const ended = wire.outcome === 'released' || wire.outcome === 'aliased' ? wire.outcome : null;
|
|
74
|
+
const cleanup = pending && wire.cleanupJobId
|
|
75
|
+
? {
|
|
76
|
+
jobId: wire.cleanupJobId,
|
|
77
|
+
mode: wire.cleanupMode,
|
|
78
|
+
objectsRemaining: wire.objectsRemaining,
|
|
79
|
+
}
|
|
80
|
+
: null;
|
|
81
|
+
const runId = cleanup?.jobId ?? wire.code;
|
|
82
|
+
return {
|
|
83
|
+
code: wire.code,
|
|
84
|
+
outcome: ended ?? 'pending',
|
|
85
|
+
cleanup,
|
|
86
|
+
handle: {
|
|
87
|
+
runId,
|
|
88
|
+
async wait(waitOptions) {
|
|
89
|
+
if (ended)
|
|
90
|
+
return ended;
|
|
91
|
+
const holds = await pollUntilTerminal({
|
|
92
|
+
read: (signal) => codeReservations({ ...options, ...(signal ? { signal } : {}) }),
|
|
93
|
+
isTerminal: (value) => releaseStateOf(wire.code, value).state !== 'pending',
|
|
94
|
+
onTimeout: () => timedOut(wire.code, runId),
|
|
95
|
+
intervalMs: DEFAULT_WAIT_INTERVAL_MS,
|
|
96
|
+
timeoutMs: DEFAULT_WAIT_TIMEOUT_MS,
|
|
97
|
+
...waitOptions,
|
|
98
|
+
});
|
|
99
|
+
const state = releaseStateOf(wire.code, holds);
|
|
100
|
+
if (state.state === 'ended')
|
|
101
|
+
return state.outcome;
|
|
102
|
+
if (state.state === 'failed')
|
|
103
|
+
throw releaseFailed(wire.code, state.failure);
|
|
104
|
+
throw timedOut(wire.code, runId);
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function releaseFailed(code, failure) {
|
|
112
|
+
return new BusinessRuleError({
|
|
113
|
+
code: 'CHANNEL_CODE_RELEASE_FAILED',
|
|
114
|
+
message: `The release of "${code}" did not free it (${failure.code}): ${failure.message}`,
|
|
115
|
+
status: 422,
|
|
116
|
+
service: 'vendure',
|
|
117
|
+
details: [{ code: failure.code, message: failure.message }],
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function timedOut(code, runId) {
|
|
121
|
+
return new TransportError({
|
|
122
|
+
code: 'CHANNEL_CODE_RELEASE_TIMEOUT',
|
|
123
|
+
message: `Stopped waiting for the release of "${code}" (cleanup ${runId}). The engine finishes it on its own; read platform.channels.codeReservations() later to see how it ended.`,
|
|
124
|
+
status: 0,
|
|
125
|
+
service: 'vendure',
|
|
126
|
+
retryable: true,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { Transport } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { AdminClientConfig } from '../client.js';
|
|
3
|
+
import type { Call } from '../types.js';
|
|
4
|
+
/** How a gateway sends: an SMTP server, or the sixtynine.email HTTP API. Fixed at creation. */
|
|
5
|
+
export type EmailGatewayType = 'smtp' | 'sixtynine_email';
|
|
6
|
+
/**
|
|
7
|
+
* A store's email gateway: the way its transactional email leaves the platform. One per store at
|
|
8
|
+
* most; a store without one sends through the platform's default sender.
|
|
9
|
+
*
|
|
10
|
+
* Credentials (the SMTP password, the sixtynine.email API key) are write-only and never read back.
|
|
11
|
+
*/
|
|
12
|
+
export interface EmailGateway {
|
|
13
|
+
id: string;
|
|
14
|
+
/** The store (channel id) it sends for. */
|
|
15
|
+
channelId: string;
|
|
16
|
+
type: EmailGatewayType;
|
|
17
|
+
name: string;
|
|
18
|
+
/** SMTP only; `null` on a sixtynine.email gateway. */
|
|
19
|
+
host: string | null;
|
|
20
|
+
/** SMTP only; `null` on a sixtynine.email gateway. */
|
|
21
|
+
port: number | null;
|
|
22
|
+
/** SMTP only; `null` for an unauthenticated relay or a sixtynine.email gateway. */
|
|
23
|
+
username: string | null;
|
|
24
|
+
fromAddress: string;
|
|
25
|
+
fromName: string;
|
|
26
|
+
/**
|
|
27
|
+
* SMTP only: `tls` (TLS from the first byte, usually port 465), `starttls` (a plain connection
|
|
28
|
+
* that must upgrade to TLS, usually port 587) or `none`. `null` on a sixtynine.email gateway.
|
|
29
|
+
*/
|
|
30
|
+
encryption: string | null;
|
|
31
|
+
/** sixtynine.email only: the API's address, such as `https://sixtynine.email`. */
|
|
32
|
+
apiUrl: string | null;
|
|
33
|
+
/** A disabled gateway is skipped: its store sends through the platform's default sender. */
|
|
34
|
+
enabled: boolean;
|
|
35
|
+
/** ISO-8601 UTC. */
|
|
36
|
+
createdAt: string;
|
|
37
|
+
/** ISO-8601 UTC. */
|
|
38
|
+
updatedAt: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A new gateway. `type` decides which fields apply: an SMTP gateway (the default) needs `host` and
|
|
42
|
+
* `password`; a sixtynine.email gateway needs `apiKey`. The other type's fields are ignored.
|
|
43
|
+
*/
|
|
44
|
+
export interface CreateEmailGatewayInput {
|
|
45
|
+
channelId: string;
|
|
46
|
+
/** Default `smtp`. */
|
|
47
|
+
type?: EmailGatewayType;
|
|
48
|
+
name: string;
|
|
49
|
+
host?: string;
|
|
50
|
+
/** 1–65535; default 587. */
|
|
51
|
+
port?: number;
|
|
52
|
+
username?: string;
|
|
53
|
+
/** Write-only. */
|
|
54
|
+
password?: string;
|
|
55
|
+
/**
|
|
56
|
+
* `tls`, `starttls` or `none`. Left out: `tls` on port 465, `starttls` on any other. Any other
|
|
57
|
+
* letter case of `TLS`/`STARTTLS` is stored by the port (465 → `tls`, else `starttls`).
|
|
58
|
+
*/
|
|
59
|
+
encryption?: string;
|
|
60
|
+
/**
|
|
61
|
+
* An https:// address on one of the platform's sixtynine.email hosts (such as
|
|
62
|
+
* `https://sixtynine.email`), with no user name, query or fragment: the API key is sent there.
|
|
63
|
+
* Left out, none is stored and the gateway sends nothing until one is set.
|
|
64
|
+
*/
|
|
65
|
+
apiUrl?: string;
|
|
66
|
+
/** Write-only. */
|
|
67
|
+
apiKey?: string;
|
|
68
|
+
fromAddress: string;
|
|
69
|
+
fromName: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A change to a gateway. A field left out keeps what the gateway has; `password` and `apiKey`
|
|
73
|
+
* left out or empty keep the stored credential. The type cannot change: the SMTP fields are
|
|
74
|
+
* ignored on a sixtynine.email gateway, and `apiUrl`/`apiKey` on an SMTP one.
|
|
75
|
+
*/
|
|
76
|
+
export interface UpdateEmailGatewayInput {
|
|
77
|
+
/** Moves the gateway to another store. */
|
|
78
|
+
channelId?: string;
|
|
79
|
+
name?: string;
|
|
80
|
+
host?: string;
|
|
81
|
+
port?: number;
|
|
82
|
+
/** `null` clears it (an unauthenticated relay). */
|
|
83
|
+
username?: string | null;
|
|
84
|
+
password?: string;
|
|
85
|
+
encryption?: string;
|
|
86
|
+
/** `null` clears it: the gateway then sends nothing. */
|
|
87
|
+
apiUrl?: string | null;
|
|
88
|
+
apiKey?: string;
|
|
89
|
+
fromAddress?: string;
|
|
90
|
+
fromName?: string;
|
|
91
|
+
enabled?: boolean;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* `admin.platform.emailGateways` — the stores' email gateways, for platform staff (spec §9.2
|
|
95
|
+
* `platform`). Gateways of every store, so no tenant is needed on the client.
|
|
96
|
+
*
|
|
97
|
+
* Every method needs a human SuperAdmin session: an API token or an app credential is refused
|
|
98
|
+
* with `PLATFORM_REQUIRES_SESSION` (`PermissionDeniedError`, 403) BEFORE anything is sent, and a
|
|
99
|
+
* session without SuperAdmin is refused by the engine (`FORBIDDEN`).
|
|
100
|
+
*
|
|
101
|
+
* **Coded refusals** (GraphQL errors mapped by `core`):
|
|
102
|
+
* - `EMAIL_GATEWAY_INPUT_INVALID` → `ValidationError` (400): a value the gateway cannot use. Every
|
|
103
|
+
* problem of one save comes back at once in `details[]` (`field` names the input property, e.g.
|
|
104
|
+
* `apiUrl`), and no refusal repeats a value it was sent (a password put in the wrong field
|
|
105
|
+
* never comes back).
|
|
106
|
+
* - `EMAIL_GATEWAY_NOT_FOUND` → `NotFoundError` (404): no gateway has that id.
|
|
107
|
+
* - `EMAIL_GATEWAY_CHANNEL_TAKEN` → `ConflictError` (409): the store already has a gateway.
|
|
108
|
+
* - `ORGANIZATION_WORKSPACE_NOT_A_STORE` → `BusinessRuleError` (422): a workspace's channel is
|
|
109
|
+
* not a store; moving a gateway there, enabling one there or storing a credential on it is
|
|
110
|
+
* refused.
|
|
111
|
+
* - `EMAIL_GATEWAY_TEST_FAILED` → `BusinessRuleError` (422): see {@link test}.
|
|
112
|
+
* - `EMAIL_GATEWAY_TEST_NOT_SUPPORTED` → `BusinessRuleError` (422): see {@link test}.
|
|
113
|
+
*/
|
|
114
|
+
export interface PlatformEmailGatewaysModule {
|
|
115
|
+
/**
|
|
116
|
+
* Every store's gateway, newest first.
|
|
117
|
+
*
|
|
118
|
+
* @effect read
|
|
119
|
+
*/
|
|
120
|
+
list(options?: Call): Promise<EmailGateway[]>;
|
|
121
|
+
/**
|
|
122
|
+
* One gateway, or `null` when no gateway has that id.
|
|
123
|
+
*
|
|
124
|
+
* @effect read
|
|
125
|
+
*/
|
|
126
|
+
get(id: string, options?: Call): Promise<EmailGateway | null>;
|
|
127
|
+
/**
|
|
128
|
+
* Create a store's gateway, enabled, and answer it as read.
|
|
129
|
+
*
|
|
130
|
+
* @effect write
|
|
131
|
+
*/
|
|
132
|
+
create(input: CreateEmailGatewayInput, options?: Call): Promise<EmailGateway>;
|
|
133
|
+
/**
|
|
134
|
+
* Change a gateway, and answer it as read.
|
|
135
|
+
*
|
|
136
|
+
* @effect write
|
|
137
|
+
*/
|
|
138
|
+
update(id: string, patch: UpdateEmailGatewayInput, options?: Call): Promise<EmailGateway>;
|
|
139
|
+
/**
|
|
140
|
+
* Delete a gateway. Its store then sends through the platform's default sender.
|
|
141
|
+
*
|
|
142
|
+
* @effect destructive
|
|
143
|
+
*/
|
|
144
|
+
delete(id: string, options?: Call): Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* Check that an SMTP gateway's server takes a connection with its stored settings and
|
|
147
|
+
* credentials. No email is sent. Resolves when the check passed.
|
|
148
|
+
*
|
|
149
|
+
* - `EMAIL_GATEWAY_TEST_FAILED`: the server refused or could not be reached. The message names
|
|
150
|
+
* the step that failed (TLS handshake, user name or password, host name, timeout,
|
|
151
|
+
* connection), never the server's own reply.
|
|
152
|
+
* - `EMAIL_GATEWAY_TEST_NOT_SUPPORTED`: a sixtynine.email gateway; the check is SMTP-only.
|
|
153
|
+
* - `EMAIL_GATEWAY_NOT_FOUND`: no gateway has that id.
|
|
154
|
+
*
|
|
155
|
+
* @effect read
|
|
156
|
+
*/
|
|
157
|
+
test(id: string, options?: Call): Promise<void>;
|
|
158
|
+
}
|
|
159
|
+
export declare const EMAIL_GATEWAYS_QUERY = "\n query PlatformEmailGateways {\n emailGateways { \n id\n channelId\n type\n name\n host\n port\n username\n fromAddress\n fromName\n encryption\n apiUrl\n enabled\n createdAt\n updatedAt\n }\n }\n";
|
|
160
|
+
export declare const EMAIL_GATEWAY_QUERY = "\n query PlatformEmailGateway($id: ID!) {\n emailGateway(id: $id) { \n id\n channelId\n type\n name\n host\n port\n username\n fromAddress\n fromName\n encryption\n apiUrl\n enabled\n createdAt\n updatedAt\n }\n }\n";
|
|
161
|
+
export declare const CREATE_EMAIL_GATEWAY_MUTATION = "\n mutation PlatformCreateEmailGateway($input: CreateEmailGatewayInput!) {\n createEmailGateway(input: $input) { \n id\n channelId\n type\n name\n host\n port\n username\n fromAddress\n fromName\n encryption\n apiUrl\n enabled\n createdAt\n updatedAt\n }\n }\n";
|
|
162
|
+
export declare const UPDATE_EMAIL_GATEWAY_MUTATION = "\n mutation PlatformUpdateEmailGateway($id: ID!, $input: UpdateEmailGatewayInput!) {\n updateEmailGateway(id: $id, input: $input) { \n id\n channelId\n type\n name\n host\n port\n username\n fromAddress\n fromName\n encryption\n apiUrl\n enabled\n createdAt\n updatedAt\n }\n }\n";
|
|
163
|
+
export declare const DELETE_EMAIL_GATEWAY_MUTATION = "\n mutation PlatformDeleteEmailGateway($id: ID!) {\n deleteEmailGateway(id: $id) { result }\n }\n";
|
|
164
|
+
export declare const TEST_EMAIL_GATEWAY_MUTATION = "\n mutation PlatformTestEmailGateway($id: ID!) {\n testEmailGateway(id: $id)\n }\n";
|
|
165
|
+
export declare function createPlatformEmailGatewaysModule(transport: Transport, config: AdminClientConfig): PlatformEmailGatewaysModule;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { fieldOf } from '../catalog/types.js';
|
|
2
|
+
import { requireHumanSession } from './guard.js';
|
|
3
|
+
const GATEWAY_FIELDS = /* GraphQL */ `
|
|
4
|
+
id
|
|
5
|
+
channelId
|
|
6
|
+
type
|
|
7
|
+
name
|
|
8
|
+
host
|
|
9
|
+
port
|
|
10
|
+
username
|
|
11
|
+
fromAddress
|
|
12
|
+
fromName
|
|
13
|
+
encryption
|
|
14
|
+
apiUrl
|
|
15
|
+
enabled
|
|
16
|
+
createdAt
|
|
17
|
+
updatedAt
|
|
18
|
+
`;
|
|
19
|
+
export const EMAIL_GATEWAYS_QUERY = /* GraphQL */ `
|
|
20
|
+
query PlatformEmailGateways {
|
|
21
|
+
emailGateways { ${GATEWAY_FIELDS} }
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
export const EMAIL_GATEWAY_QUERY = /* GraphQL */ `
|
|
25
|
+
query PlatformEmailGateway($id: ID!) {
|
|
26
|
+
emailGateway(id: $id) { ${GATEWAY_FIELDS} }
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
export const CREATE_EMAIL_GATEWAY_MUTATION = /* GraphQL */ `
|
|
30
|
+
mutation PlatformCreateEmailGateway($input: CreateEmailGatewayInput!) {
|
|
31
|
+
createEmailGateway(input: $input) { ${GATEWAY_FIELDS} }
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
export const UPDATE_EMAIL_GATEWAY_MUTATION = /* GraphQL */ `
|
|
35
|
+
mutation PlatformUpdateEmailGateway($id: ID!, $input: UpdateEmailGatewayInput!) {
|
|
36
|
+
updateEmailGateway(id: $id, input: $input) { ${GATEWAY_FIELDS} }
|
|
37
|
+
}
|
|
38
|
+
`;
|
|
39
|
+
export const DELETE_EMAIL_GATEWAY_MUTATION = /* GraphQL */ `
|
|
40
|
+
mutation PlatformDeleteEmailGateway($id: ID!) {
|
|
41
|
+
deleteEmailGateway(id: $id) { result }
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
export const TEST_EMAIL_GATEWAY_MUTATION = /* GraphQL */ `
|
|
45
|
+
mutation PlatformTestEmailGateway($id: ID!) {
|
|
46
|
+
testEmailGateway(id: $id)
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
export function createPlatformEmailGatewaysModule(transport, config) {
|
|
50
|
+
// Gateways of every store: no store is involved, so no tenant is needed (as `platform.tax`).
|
|
51
|
+
const unscoped = (call) => ({ ...call, allowUnscoped: true });
|
|
52
|
+
return {
|
|
53
|
+
async list(options) {
|
|
54
|
+
requireHumanSession(config);
|
|
55
|
+
return fieldOf(transport, EMAIL_GATEWAYS_QUERY, 'emailGateways', {}, unscoped(options));
|
|
56
|
+
},
|
|
57
|
+
async get(id, options) {
|
|
58
|
+
requireHumanSession(config);
|
|
59
|
+
return fieldOf(transport, EMAIL_GATEWAY_QUERY, 'emailGateway', { id }, unscoped(options));
|
|
60
|
+
},
|
|
61
|
+
async create(input, options) {
|
|
62
|
+
requireHumanSession(config);
|
|
63
|
+
return fieldOf(transport, CREATE_EMAIL_GATEWAY_MUTATION, 'createEmailGateway', { input }, unscoped(options));
|
|
64
|
+
},
|
|
65
|
+
async update(id, patch, options) {
|
|
66
|
+
requireHumanSession(config);
|
|
67
|
+
return fieldOf(transport, UPDATE_EMAIL_GATEWAY_MUTATION, 'updateEmailGateway', { id, input: patch }, unscoped(options));
|
|
68
|
+
},
|
|
69
|
+
async delete(id, options) {
|
|
70
|
+
requireHumanSession(config);
|
|
71
|
+
await fieldOf(transport, DELETE_EMAIL_GATEWAY_MUTATION, 'deleteEmailGateway', { id }, unscoped(options));
|
|
72
|
+
},
|
|
73
|
+
async test(id, options) {
|
|
74
|
+
requireHumanSession(config);
|
|
75
|
+
await fieldOf(transport, TEST_EMAIL_GATEWAY_MUTATION, 'testEmailGateway', { id }, unscoped(options));
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|