@sixtyninecommerce/admin 0.10.0 → 0.11.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 +4347 -171
- 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/index.d.ts +9 -2
- package/dist/catalog/index.js +4 -0
- package/dist/catalog/products.d.ts +159 -19
- package/dist/catalog/products.js +88 -13
- package/dist/catalog/properties.d.ts +102 -14
- package/dist/catalog/properties.js +13 -0
- package/dist/catalog/recommendations.d.ts +261 -0
- package/dist/catalog/recommendations.js +145 -0
- package/dist/catalog/reviews.d.ts +368 -0
- package/dist/catalog/reviews.js +318 -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 +233 -26
- package/dist/catalog/variants.js +125 -7
- package/dist/catalog/visibility.d.ts +20 -5
- package/dist/catalog/visibility.js +19 -1
- package/dist/client.d.ts +95 -9
- package/dist/client.js +35 -7
- package/dist/content/assets.d.ts +286 -0
- package/dist/content/assets.js +181 -0
- package/dist/content/items.d.ts +269 -0
- package/dist/content/items.js +170 -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/groups.d.ts +207 -0
- package/dist/customers/groups.js +438 -0
- package/dist/customers/index.d.ts +447 -37
- package/dist/customers/index.js +313 -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/index.d.ts +731 -21
- package/dist/documents/index.js +436 -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 +44 -10
- package/dist/fulfillments/index.js +11 -2
- 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 +123 -35
- package/dist/index.js +45 -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 +423 -0
- package/dist/integrations/exact-online-types.js +6 -0
- package/dist/integrations/exact-online.d.ts +248 -0
- package/dist/integrations/exact-online.js +376 -0
- package/dist/integrations/index.d.ts +29 -3
- package/dist/integrations/index.js +18 -3
- package/dist/integrations/pipeline-keys.d.ts +13 -0
- package/dist/integrations/pipeline-keys.js +22 -0
- package/dist/integrations/pipelines.d.ts +15 -14
- package/dist/integrations/pipelines.js +6 -22
- package/dist/integrations/types.d.ts +146 -0
- package/dist/locations/index.d.ts +66 -0
- package/dist/locations/index.js +31 -0
- package/dist/meta/index.d.ts +162 -0
- package/dist/orders/cancel-with-money.d.ts +227 -0
- package/dist/orders/cancel-with-money.js +153 -0
- package/dist/orders/commit-edit.d.ts +7 -1
- package/dist/orders/commit-edit.js +5 -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 +49 -9
- package/dist/orders/index.js +22 -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 +222 -0
- package/dist/orders/list.js +377 -0
- package/dist/orders/manual-pricing.d.ts +5 -0
- package/dist/orders/order-entry.d.ts +20 -7
- package/dist/orders/plan-edit.d.ts +1 -1
- package/dist/orders/plan-edit.js +4 -1
- 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 +8 -1
- package/dist/orders/surcharges.js +3 -2
- package/dist/payments/account-credit.d.ts +30 -0
- package/dist/payments/account-credit.js +19 -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 +12 -0
- package/dist/payments/credit-noted-fields.js +37 -0
- package/dist/payments/index.d.ts +257 -11
- package/dist/payments/index.js +131 -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 +144 -0
- package/dist/payments/requests.js +92 -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 +104 -0
- package/dist/pickups/index.js +105 -0
- package/dist/platform/channels.d.ts +132 -0
- package/dist/platform/channels.js +128 -0
- package/dist/platform/geo.d.ts +123 -0
- package/dist/platform/geo.js +144 -0
- package/dist/platform/tax.d.ts +154 -0
- package/dist/platform/tax.js +139 -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 +103 -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 +2125 -32
- package/dist/pos/index.js +742 -4
- package/dist/pos/no-receipt.d.ts +94 -0
- package/dist/pos/no-receipt.js +25 -0
- package/dist/pos/offline-sales.d.ts +187 -0
- package/dist/pos/offline-sales.js +116 -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 +217 -0
- package/dist/pos/returns.js +53 -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 +604 -0
- package/dist/pricing/price-lists.js +600 -0
- package/dist/purchasing/alerts.d.ts +89 -0
- package/dist/purchasing/alerts.js +62 -0
- package/dist/purchasing/cost.d.ts +110 -0
- package/dist/purchasing/cost.js +86 -0
- package/dist/purchasing/index.d.ts +36 -0
- package/dist/purchasing/index.js +16 -0
- package/dist/purchasing/purchase-orders.d.ts +548 -0
- package/dist/purchasing/purchase-orders.js +491 -0
- package/dist/purchasing/replenishment.d.ts +281 -0
- package/dist/purchasing/replenishment.js +240 -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 +501 -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 +614 -21
- package/dist/returns/index.js +695 -11
- package/dist/search/index.d.ts +76 -1
- package/dist/search/index.js +63 -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 +48 -0
- package/dist/shipping/index.js +25 -0
- package/dist/shipping/methods.d.ts +198 -0
- package/dist/shipping/methods.js +240 -0
- package/dist/shipping/myparcel.d.ts +254 -0
- package/dist/shipping/myparcel.js +178 -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 +179 -0
- package/dist/shipping/rate-tables.js +184 -0
- package/dist/stock/index.d.ts +10 -3
- package/dist/stock/index.js +3 -0
- package/dist/stock/locations.d.ts +144 -0
- package/dist/stock/locations.js +168 -0
- package/dist/stock/transfers.d.ts +216 -43
- package/dist/stock/transfers.js +140 -13
- package/dist/stored-value/index.d.ts +214 -4
- package/dist/stored-value/index.js +152 -0
- package/dist/subscriptions/index.d.ts +682 -0
- package/dist/subscriptions/index.js +815 -0
- package/dist/tax/index.d.ts +76 -1
- package/dist/tax/index.js +50 -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 +66 -0
- package/dist/tenant/email-templates.js +54 -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,254 @@
|
|
|
1
|
+
import { type PlatformError, type Transport, type VendureErrorResult } from '@sixtyninecommerce/core';
|
|
2
|
+
import type { Call } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The store-wide default label format: `A4` or `A6` paper, or `BOTH`, which has MyParcel make
|
|
5
|
+
* both for every new fulfillment.
|
|
6
|
+
*/
|
|
7
|
+
export type MyparcelLabelFormat = 'A4' | 'A6' | 'BOTH';
|
|
8
|
+
/**
|
|
9
|
+
* A store's MyParcel set-up, as every reader gets it: both secrets masked. The API key is never
|
|
10
|
+
* readable again once saved, and the webhook secret is only ever needed by the platform itself
|
|
11
|
+
* (it registers the callback URL at MyParcel), so neither is answered in clear here.
|
|
12
|
+
*/
|
|
13
|
+
export interface MyparcelConfig {
|
|
14
|
+
id: string;
|
|
15
|
+
channelId: string;
|
|
16
|
+
/** A key is stored. */
|
|
17
|
+
hasApiKey: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* The key's preview, for display only: mask glyphs (`•`) and its last 4 characters. It is never
|
|
20
|
+
* the key: `update` refuses it (`SECRET_ARG_MASK_INVALID`).
|
|
21
|
+
*/
|
|
22
|
+
maskedApiKey: string;
|
|
23
|
+
/** The webhook secret's preview, for display only: 8 mask glyphs and its last 4 characters. */
|
|
24
|
+
maskedWebhookSecret: string;
|
|
25
|
+
defaultLabelFormat: MyparcelLabelFormat;
|
|
26
|
+
/** MyParcel is on for the store. Independent of the key: switching off keeps it. */
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** What `config.update` takes: the key alone. */
|
|
30
|
+
export interface UpdateMyparcelConfigInput {
|
|
31
|
+
/** The MyParcel API key, from the store's MyParcel account. Trimmed; never empty. */
|
|
32
|
+
apiKey: string;
|
|
33
|
+
}
|
|
34
|
+
/** A carrier MyParcel offers. The list is the same for every store (`GET /carriers`). */
|
|
35
|
+
export interface MyparcelCarrier {
|
|
36
|
+
/** MyParcel's carrier id, as a shipment names it. */
|
|
37
|
+
id: number;
|
|
38
|
+
/** The carrier's slug (`postnl`, `dhlforyou`, …). */
|
|
39
|
+
name: string;
|
|
40
|
+
/** Its name for people (`PostNL`). */
|
|
41
|
+
human: string;
|
|
42
|
+
/** MyParcel's logo links; `''` where MyParcel has none. */
|
|
43
|
+
meta: {
|
|
44
|
+
logoSvg: string;
|
|
45
|
+
logoPng: string;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** A stock location's parcel sender address. */
|
|
49
|
+
export interface MyparcelSenderAddress {
|
|
50
|
+
streetLine1: string;
|
|
51
|
+
streetLine2: string | null;
|
|
52
|
+
city: string;
|
|
53
|
+
postalCode: string;
|
|
54
|
+
province: string | null;
|
|
55
|
+
/** ISO 3166-1 alpha-2, upper case. */
|
|
56
|
+
countryCode: string;
|
|
57
|
+
phoneNumber: string | null;
|
|
58
|
+
contactName: string | null;
|
|
59
|
+
company: string | null;
|
|
60
|
+
}
|
|
61
|
+
/** One stock location of the store as a parcel sender. */
|
|
62
|
+
export interface MyparcelSenderLocation {
|
|
63
|
+
/** The stock location's id. */
|
|
64
|
+
id: string;
|
|
65
|
+
name: string;
|
|
66
|
+
/** Its sender address, or `null` when none is stored or the stored one does not parse. */
|
|
67
|
+
address: MyparcelSenderAddress | null;
|
|
68
|
+
/** The store's default sender: a shipment that names no location ships from it. */
|
|
69
|
+
isDefaultSender: boolean;
|
|
70
|
+
/** The address has every field a shipment needs (street, city, postal code, country). */
|
|
71
|
+
isResolvable: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** What `senders.set` writes. */
|
|
74
|
+
export interface SetMyparcelSenderInput {
|
|
75
|
+
stockLocationId: string;
|
|
76
|
+
/** The whole address. `null` or a blank optional field is stored as absent. */
|
|
77
|
+
address: {
|
|
78
|
+
streetLine1: string;
|
|
79
|
+
streetLine2?: string | null;
|
|
80
|
+
city: string;
|
|
81
|
+
postalCode: string;
|
|
82
|
+
province?: string | null;
|
|
83
|
+
countryCode: string;
|
|
84
|
+
phoneNumber?: string | null;
|
|
85
|
+
contactName?: string | null;
|
|
86
|
+
company?: string | null;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Make this location the store's default sender (`true`) or not. Send the location's CURRENT
|
|
90
|
+
* flag to change the address alone: only a change of the flag is judged and written.
|
|
91
|
+
*/
|
|
92
|
+
isDefault: boolean;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The MyParcel set-up of the store the client is scoped to (`admin.shipping.myparcel.config`).
|
|
96
|
+
* There is no channel argument: a SuperAdmin head configures ANOTHER store with
|
|
97
|
+
* `admin.withTenant({ scope: { kind: 'channel', code } })`, or a per-call `tenant`.
|
|
98
|
+
*
|
|
99
|
+
* Every method needs `UpdateSettings`. An app installation is refused all of them
|
|
100
|
+
* (`APP_CREDENTIAL_NOT_ALLOWED`), the read included: the engine still answers the webhook secret
|
|
101
|
+
* in clear on an older field of the same object. A store without the MyParcel plugin is refused
|
|
102
|
+
* `PLUGIN_ACCESS_DENIED` (`PermissionDeniedError`, 403) on every write, `remove` included, for an
|
|
103
|
+
* ordinary caller; the engine never refuses a SuperAdmin on that ground.
|
|
104
|
+
*/
|
|
105
|
+
export interface MyparcelConfigModule {
|
|
106
|
+
/**
|
|
107
|
+
* The store's set-up, or `null` when no key was ever saved (or it was removed). A stored key the
|
|
108
|
+
* engine can no longer decrypt (its encryption key was rotated) fails this read with the
|
|
109
|
+
* engine's internal error (code `INTERNAL_SERVER_ERROR`: a non-retryable 500, raised as
|
|
110
|
+
* `UpstreamError`), not a coded refusal: the read is not a union. Saving the key again with `update` repairs it; the writes answer the
|
|
111
|
+
* same condition as `MYPARCEL_DECRYPTION_FAILED_ERROR` (`BusinessRuleError`, 422).
|
|
112
|
+
*
|
|
113
|
+
* @effect read
|
|
114
|
+
* @permission UpdateSettings
|
|
115
|
+
*/
|
|
116
|
+
get(options?: Call): Promise<MyparcelConfig | null>;
|
|
117
|
+
/**
|
|
118
|
+
* Save the store's API key; the first save also switches MyParcel on, with `A6` labels and a new
|
|
119
|
+
* webhook secret. A later save replaces the key alone (the on/off switch, the label format and
|
|
120
|
+
* the webhook secret stay). The key is trimmed and stored as given: the engine does not test it
|
|
121
|
+
* against MyParcel, so a wrong key fails at the first shipment or label.
|
|
122
|
+
*
|
|
123
|
+
* An empty key is refused here (`TypeError`), never sent: an empty key REMOVES the whole set-up
|
|
124
|
+
* on the wire, and that is `remove()`'s job. The message never quotes the argument.
|
|
125
|
+
*
|
|
126
|
+
* Refused, with nothing stored:
|
|
127
|
+
* - `SECRET_ARG_MASK_INVALID` (`ValidationError`, 400, `details[0].field: 'apiKey'`): a key
|
|
128
|
+
* carrying the mask glyph (`•`), {@link MyparcelConfig.maskedApiKey} sent back included. The
|
|
129
|
+
* preview is never the key; leave the key alone to keep it;
|
|
130
|
+
* - `ORGANIZATION_WORKSPACE_NOT_A_STORE` (`BusinessRuleError`, 422): an organization's workspace
|
|
131
|
+
* ships nothing.
|
|
132
|
+
*
|
|
133
|
+
* @effect write
|
|
134
|
+
* @permission UpdateSettings
|
|
135
|
+
*/
|
|
136
|
+
update(input: UpdateMyparcelConfigInput, options?: Call): Promise<MyparcelConfig>;
|
|
137
|
+
/**
|
|
138
|
+
* Remove the store's MyParcel set-up: the API key, the label format, the on/off switch AND the
|
|
139
|
+
* webhook secret. MyParcel's status and label callbacks for the store are refused from then on,
|
|
140
|
+
* and every MyParcel shipment or label of the store is refused (`MYPARCEL_NOT_CONFIGURED_ERROR`)
|
|
141
|
+
* until a key is saved again, which mints a new webhook secret. Resolves when the store has no
|
|
142
|
+
* set-up any more, also when it had none. Allowed in an organization's workspace.
|
|
143
|
+
*
|
|
144
|
+
* @effect destructive
|
|
145
|
+
* @permission UpdateSettings
|
|
146
|
+
*/
|
|
147
|
+
remove(options?: Call): Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* Set the store-wide default label format. A fulfillment's own format, when it has one, wins.
|
|
150
|
+
*
|
|
151
|
+
* Refused: `MYPARCEL_NOT_CONFIGURED_ERROR` (`BusinessRuleError`, 422) before a key is saved;
|
|
152
|
+
* `MYPARCEL_INVALID_LABEL_FORMAT_ERROR` (422) for any other value.
|
|
153
|
+
*
|
|
154
|
+
* @effect write
|
|
155
|
+
* @permission UpdateSettings
|
|
156
|
+
*/
|
|
157
|
+
setLabelFormat(format: MyparcelLabelFormat, options?: Call): Promise<MyparcelConfig>;
|
|
158
|
+
/**
|
|
159
|
+
* Switch MyParcel on or off for the store without touching the key or the webhook secret. Off,
|
|
160
|
+
* every new MyParcel shipment and label of the store is refused; what MyParcel already holds is
|
|
161
|
+
* left as it is.
|
|
162
|
+
*
|
|
163
|
+
* Refused: `MYPARCEL_NOT_CONFIGURED_ERROR` (`BusinessRuleError`, 422) before a key is saved;
|
|
164
|
+
* switching ON in an organization's workspace, `ORGANIZATION_WORKSPACE_NOT_A_STORE` (422).
|
|
165
|
+
*
|
|
166
|
+
* @effect write
|
|
167
|
+
* @permission UpdateSettings
|
|
168
|
+
*/
|
|
169
|
+
setEnabled(enabled: boolean, options?: Call): Promise<MyparcelConfig>;
|
|
170
|
+
/**
|
|
171
|
+
* Replace the store's webhook secret, answering the set-up with the NEW secret's preview (the
|
|
172
|
+
* secret itself is never answered). The old secret stops authenticating at once, and MyParcel
|
|
173
|
+
* keeps calling the old callback URL, refused, until the engine's worker registers the webhooks
|
|
174
|
+
* again, which it does when it starts. Status and label updates sent meanwhile are lost.
|
|
175
|
+
*
|
|
176
|
+
* The secret is answered in clear by the engine's older `webhookSecret` field and stored in
|
|
177
|
+
* clear; hashing it at rest is an open follow-up, after which only a preview can be read.
|
|
178
|
+
*
|
|
179
|
+
* Refused: `MYPARCEL_NOT_CONFIGURED_ERROR` (`BusinessRuleError`, 422) before a key is saved.
|
|
180
|
+
*
|
|
181
|
+
* @effect destructive
|
|
182
|
+
* @permission UpdateSettings
|
|
183
|
+
*/
|
|
184
|
+
regenerateWebhookSecret(options?: Call): Promise<MyparcelConfig>;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* The stock locations of the store as MyParcel parcel senders (`admin.shipping.myparcel.senders`).
|
|
188
|
+
* A location's sender address and default flag are columns of its ONE row, so a location several
|
|
189
|
+
* stores hold has one sender for all of them.
|
|
190
|
+
*/
|
|
191
|
+
export interface MyparcelSendersModule {
|
|
192
|
+
/**
|
|
193
|
+
* Every stock location of the store with its sender address, default flag and whether a
|
|
194
|
+
* shipment can ship from it. Needs `ReadStockLocation`; answered whether or not MyParcel is set
|
|
195
|
+
* up.
|
|
196
|
+
*
|
|
197
|
+
* @effect read
|
|
198
|
+
* @permission ReadStockLocation
|
|
199
|
+
*/
|
|
200
|
+
list(options?: Call): Promise<MyparcelSenderLocation[]>;
|
|
201
|
+
/**
|
|
202
|
+
* Set a stock location's sender address, and whether it is the store's default sender. Making it
|
|
203
|
+
* the default clears the flag on the store's other locations in the same transaction, so the
|
|
204
|
+
* store keeps exactly one. This is the one door that writes either column: core's stock-location
|
|
205
|
+
* create and update refuse a change to them (`STOCK_LOCATION_SENDER_READ_ONLY`).
|
|
206
|
+
*
|
|
207
|
+
* Refused, with nothing written:
|
|
208
|
+
* - `MYPARCEL_INVALID_SENDER_ERROR` (`BusinessRuleError`, 422): a mandatory field missing or a
|
|
209
|
+
* country code that is not 2 letters; `details[]` names each field (`streetLine1`, `city`,
|
|
210
|
+
* `postalCode`, `countryCode`);
|
|
211
|
+
* - `MYPARCEL_STOCK_LOCATION_NOT_FOUND_ERROR` (`NotFoundError`, 404): no such location in the
|
|
212
|
+
* store (another store's, a missing and a malformed id alike);
|
|
213
|
+
* - `SHARED_STOCK_LOCATION_NOT_ALLOWED` (403): the location, or a default it would clear, is
|
|
214
|
+
* also another store's, and the caller may not update stock locations there;
|
|
215
|
+
* - `LOCATION_NOT_ALLOWED` (403): a credential restricted to some locations names, or would
|
|
216
|
+
* clear the default of, one it does not hold;
|
|
217
|
+
* - `ORGANIZATION_WORKSPACE_NOT_A_STORE` / `DEFAULT_SENDER_NOT_A_STORE` (422): changing the
|
|
218
|
+
* default flag in an organization's workspace or on the platform root. Sending the current
|
|
219
|
+
* flag there saves the address alone.
|
|
220
|
+
*
|
|
221
|
+
* @effect write
|
|
222
|
+
* @permission UpdateStockLocation
|
|
223
|
+
*/
|
|
224
|
+
set(input: SetMyparcelSenderInput, options?: Call): Promise<MyparcelSenderLocation>;
|
|
225
|
+
}
|
|
226
|
+
/** `admin.shipping.myparcel`: the MyParcel integration of the store. */
|
|
227
|
+
export interface MyparcelModule {
|
|
228
|
+
config: MyparcelConfigModule;
|
|
229
|
+
/**
|
|
230
|
+
* The carriers MyParcel offers, the same list for every store (cached by the engine for an
|
|
231
|
+
* hour). What a carrier can do for one address is not in it. Needs `ReadSettings`; a store
|
|
232
|
+
* without the MyParcel plugin is refused `PLUGIN_ACCESS_DENIED` (403).
|
|
233
|
+
*
|
|
234
|
+
* @effect read
|
|
235
|
+
* @permission ReadSettings
|
|
236
|
+
*/
|
|
237
|
+
carriers(options?: Call): Promise<MyparcelCarrier[]>;
|
|
238
|
+
senders: MyparcelSendersModule;
|
|
239
|
+
}
|
|
240
|
+
export declare const MYPARCEL_CONFIG_QUERY = "\n query MyparcelConfig {\n myparcelConfig { \n id\n channelId\n hasApiKey\n maskedApiKey\n maskedWebhookSecret\n defaultLabelFormat\n enabled\n }\n }\n";
|
|
241
|
+
export declare const UPDATE_MYPARCEL_CONFIG_MUTATION = "\n mutation UpdateMyparcelConfig($input: MyparcelConfigInput!) {\n updateMyparcelConfig(input: $input) {\n \n __typename\n ... on MyparcelConfig { \n id\n channelId\n hasApiKey\n maskedApiKey\n maskedWebhookSecret\n defaultLabelFormat\n enabled\n }\n ... on ErrorResult { errorCode message }\n\n ... on MyparcelValidationError { errors }\n }\n }\n";
|
|
242
|
+
export declare const UPDATE_MYPARCEL_LABEL_FORMAT_MUTATION = "\n mutation UpdateMyparcelLabelFormat($input: MyparcelLabelFormatInput!) {\n updateMyparcelLabelFormat(input: $input) { \n __typename\n ... on MyparcelConfig { \n id\n channelId\n hasApiKey\n maskedApiKey\n maskedWebhookSecret\n defaultLabelFormat\n enabled\n }\n ... on ErrorResult { errorCode message }\n }\n }\n";
|
|
243
|
+
export declare const SET_MYPARCEL_ENABLED_MUTATION = "\n mutation SetMyparcelEnabled($enabled: Boolean!) {\n setMyparcelEnabled(enabled: $enabled) {\n \n __typename\n ... on MyparcelConfig { \n id\n channelId\n hasApiKey\n maskedApiKey\n maskedWebhookSecret\n defaultLabelFormat\n enabled\n }\n ... on ErrorResult { errorCode message }\n\n ... on MyparcelValidationError { errors }\n }\n }\n";
|
|
244
|
+
export declare const REGENERATE_MYPARCEL_WEBHOOK_SECRET_MUTATION = "\n mutation RegenerateMyparcelWebhookSecret {\n regenerateMyparcelWebhookSecret { \n __typename\n ... on MyparcelConfig { \n id\n channelId\n hasApiKey\n maskedApiKey\n maskedWebhookSecret\n defaultLabelFormat\n enabled\n }\n ... on ErrorResult { errorCode message }\n }\n }\n";
|
|
245
|
+
export declare const MYPARCEL_CARRIERS_QUERY = "\n query MyparcelCarriers {\n myparcelCarriers {\n id\n name\n human\n meta { logoSvg logoPng }\n }\n }\n";
|
|
246
|
+
export declare const MYPARCEL_SENDERS_QUERY = "\n query MyparcelSenderStockLocations {\n myparcelSenderStockLocations { \n id\n name\n address {\n streetLine1\n streetLine2\n city\n postalCode\n province\n countryCode\n phoneNumber\n contactName\n company\n }\n isDefaultSender\n isResolvable\n }\n }\n";
|
|
247
|
+
export declare const SET_MYPARCEL_SENDER_MUTATION = "\n mutation SetStockLocationSender($input: MyparcelSetStockLocationSenderInput!) {\n setStockLocationSender(input: $input) {\n __typename\n ... on MyparcelSenderStockLocationSummary { \n id\n name\n address {\n streetLine1\n streetLine2\n city\n postalCode\n province\n countryCode\n phoneNumber\n contactName\n company\n }\n isDefaultSender\n isResolvable\n }\n ... on ErrorResult { errorCode message }\n ... on MyparcelInvalidSenderError { fields }\n }\n }\n";
|
|
248
|
+
/**
|
|
249
|
+
* A refusal member as its error class, by core's `errorFromErrorResult`. The field names a member
|
|
250
|
+
* carries (`MyparcelInvalidSenderError.fields`, `MyparcelValidationError.errors`) ride
|
|
251
|
+
* `details[]`, one per field, so a form can mark each without matching the sentence.
|
|
252
|
+
*/
|
|
253
|
+
export declare function myparcelRefusal(result: VendureErrorResult): PlatformError;
|
|
254
|
+
export declare function createMyparcelModule(transport: Transport): MyparcelModule;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { errorForStatus, errorFromErrorResult, isErrorResult, } from '@sixtyninecommerce/core';
|
|
2
|
+
const CONFIG_FIELDS = /* GraphQL */ `
|
|
3
|
+
id
|
|
4
|
+
channelId
|
|
5
|
+
hasApiKey
|
|
6
|
+
maskedApiKey
|
|
7
|
+
maskedWebhookSecret
|
|
8
|
+
defaultLabelFormat
|
|
9
|
+
enabled
|
|
10
|
+
`;
|
|
11
|
+
/** The union answer of every config write: the set-up, or a refusal. */
|
|
12
|
+
const CONFIG_RESULT = /* GraphQL */ `
|
|
13
|
+
__typename
|
|
14
|
+
... on MyparcelConfig { ${CONFIG_FIELDS} }
|
|
15
|
+
... on ErrorResult { errorCode message }
|
|
16
|
+
`;
|
|
17
|
+
export const MYPARCEL_CONFIG_QUERY = /* GraphQL */ `
|
|
18
|
+
query MyparcelConfig {
|
|
19
|
+
myparcelConfig { ${CONFIG_FIELDS} }
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
export const UPDATE_MYPARCEL_CONFIG_MUTATION = /* GraphQL */ `
|
|
23
|
+
mutation UpdateMyparcelConfig($input: MyparcelConfigInput!) {
|
|
24
|
+
updateMyparcelConfig(input: $input) {
|
|
25
|
+
${CONFIG_RESULT}
|
|
26
|
+
... on MyparcelValidationError { errors }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
export const UPDATE_MYPARCEL_LABEL_FORMAT_MUTATION = /* GraphQL */ `
|
|
31
|
+
mutation UpdateMyparcelLabelFormat($input: MyparcelLabelFormatInput!) {
|
|
32
|
+
updateMyparcelLabelFormat(input: $input) { ${CONFIG_RESULT} }
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
export const SET_MYPARCEL_ENABLED_MUTATION = /* GraphQL */ `
|
|
36
|
+
mutation SetMyparcelEnabled($enabled: Boolean!) {
|
|
37
|
+
setMyparcelEnabled(enabled: $enabled) {
|
|
38
|
+
${CONFIG_RESULT}
|
|
39
|
+
... on MyparcelValidationError { errors }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
export const REGENERATE_MYPARCEL_WEBHOOK_SECRET_MUTATION = /* GraphQL */ `
|
|
44
|
+
mutation RegenerateMyparcelWebhookSecret {
|
|
45
|
+
regenerateMyparcelWebhookSecret { ${CONFIG_RESULT} }
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
export const MYPARCEL_CARRIERS_QUERY = /* GraphQL */ `
|
|
49
|
+
query MyparcelCarriers {
|
|
50
|
+
myparcelCarriers {
|
|
51
|
+
id
|
|
52
|
+
name
|
|
53
|
+
human
|
|
54
|
+
meta { logoSvg logoPng }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
`;
|
|
58
|
+
const SENDER_FIELDS = /* GraphQL */ `
|
|
59
|
+
id
|
|
60
|
+
name
|
|
61
|
+
address {
|
|
62
|
+
streetLine1
|
|
63
|
+
streetLine2
|
|
64
|
+
city
|
|
65
|
+
postalCode
|
|
66
|
+
province
|
|
67
|
+
countryCode
|
|
68
|
+
phoneNumber
|
|
69
|
+
contactName
|
|
70
|
+
company
|
|
71
|
+
}
|
|
72
|
+
isDefaultSender
|
|
73
|
+
isResolvable
|
|
74
|
+
`;
|
|
75
|
+
export const MYPARCEL_SENDERS_QUERY = /* GraphQL */ `
|
|
76
|
+
query MyparcelSenderStockLocations {
|
|
77
|
+
myparcelSenderStockLocations { ${SENDER_FIELDS} }
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
export const SET_MYPARCEL_SENDER_MUTATION = /* GraphQL */ `
|
|
81
|
+
mutation SetStockLocationSender($input: MyparcelSetStockLocationSenderInput!) {
|
|
82
|
+
setStockLocationSender(input: $input) {
|
|
83
|
+
__typename
|
|
84
|
+
... on MyparcelSenderStockLocationSummary { ${SENDER_FIELDS} }
|
|
85
|
+
... on ErrorResult { errorCode message }
|
|
86
|
+
... on MyparcelInvalidSenderError { fields }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
/** `updateMyparcelConfig` with an empty key answers this member: the set-up is gone. */
|
|
91
|
+
const NOT_CONFIGURED = 'MYPARCEL_NOT_CONFIGURED_ERROR';
|
|
92
|
+
/**
|
|
93
|
+
* A refusal member as its error class, by core's `errorFromErrorResult`. The field names a member
|
|
94
|
+
* carries (`MyparcelInvalidSenderError.fields`, `MyparcelValidationError.errors`) ride
|
|
95
|
+
* `details[]`, one per field, so a form can mark each without matching the sentence.
|
|
96
|
+
*/
|
|
97
|
+
export function myparcelRefusal(result) {
|
|
98
|
+
const base = errorFromErrorResult(result);
|
|
99
|
+
const named = Array.isArray(result.fields)
|
|
100
|
+
? result.fields
|
|
101
|
+
: Array.isArray(result.errors)
|
|
102
|
+
? result.errors
|
|
103
|
+
: [];
|
|
104
|
+
const details = named
|
|
105
|
+
.filter((field) => typeof field === 'string' && field.trim() !== '')
|
|
106
|
+
.map((field) => ({ field, code: base.code, message: base.message }));
|
|
107
|
+
if (details.length === 0)
|
|
108
|
+
return base;
|
|
109
|
+
return errorForStatus(base.status, {
|
|
110
|
+
code: base.code,
|
|
111
|
+
message: base.message,
|
|
112
|
+
service: 'vendure',
|
|
113
|
+
details,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function settled(answer) {
|
|
117
|
+
if (isErrorResult(answer))
|
|
118
|
+
throw myparcelRefusal(answer);
|
|
119
|
+
const { __typename: _typename, ...rest } = answer;
|
|
120
|
+
return rest;
|
|
121
|
+
}
|
|
122
|
+
export function createMyparcelModule(transport) {
|
|
123
|
+
const gql = (query, variables, options) => transport.graphql({ api: 'admin', query, variables }, options);
|
|
124
|
+
return {
|
|
125
|
+
config: {
|
|
126
|
+
async get(options) {
|
|
127
|
+
const data = await gql(MYPARCEL_CONFIG_QUERY, {}, options);
|
|
128
|
+
return data.myparcelConfig ?? null;
|
|
129
|
+
},
|
|
130
|
+
async update(input, options) {
|
|
131
|
+
const apiKey = typeof input?.apiKey === 'string' ? input.apiKey.trim() : '';
|
|
132
|
+
if (apiKey === '') {
|
|
133
|
+
// Never quotes the argument: a key padded with whitespace must not reach a log.
|
|
134
|
+
throw new TypeError('config.update needs a MyParcel API key; remove the stored one with config.remove().');
|
|
135
|
+
}
|
|
136
|
+
const data = await gql(UPDATE_MYPARCEL_CONFIG_MUTATION, { input: { apiKey } }, options);
|
|
137
|
+
return settled(data.updateMyparcelConfig);
|
|
138
|
+
},
|
|
139
|
+
async remove(options) {
|
|
140
|
+
// The wire's removal is an empty key, answered by the not-configured member.
|
|
141
|
+
const data = await gql(UPDATE_MYPARCEL_CONFIG_MUTATION, { input: { apiKey: '' } }, options);
|
|
142
|
+
const answer = data.updateMyparcelConfig;
|
|
143
|
+
if (isErrorResult(answer) && answer.errorCode === NOT_CONFIGURED)
|
|
144
|
+
return;
|
|
145
|
+
if (isErrorResult(answer))
|
|
146
|
+
throw myparcelRefusal(answer);
|
|
147
|
+
// An engine that answered a set-up kept it: never report that as removed.
|
|
148
|
+
throw new Error('MyParcel answered the set-up instead of removing it; nothing was removed.');
|
|
149
|
+
},
|
|
150
|
+
async setLabelFormat(format, options) {
|
|
151
|
+
const data = await gql(UPDATE_MYPARCEL_LABEL_FORMAT_MUTATION, { input: { format } }, options);
|
|
152
|
+
return settled(data.updateMyparcelLabelFormat);
|
|
153
|
+
},
|
|
154
|
+
async setEnabled(enabled, options) {
|
|
155
|
+
const data = await gql(SET_MYPARCEL_ENABLED_MUTATION, { enabled }, options);
|
|
156
|
+
return settled(data.setMyparcelEnabled);
|
|
157
|
+
},
|
|
158
|
+
async regenerateWebhookSecret(options) {
|
|
159
|
+
const data = await gql(REGENERATE_MYPARCEL_WEBHOOK_SECRET_MUTATION, {}, options);
|
|
160
|
+
return settled(data.regenerateMyparcelWebhookSecret);
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
async carriers(options) {
|
|
164
|
+
const data = await gql(MYPARCEL_CARRIERS_QUERY, {}, options);
|
|
165
|
+
return data.myparcelCarriers;
|
|
166
|
+
},
|
|
167
|
+
senders: {
|
|
168
|
+
async list(options) {
|
|
169
|
+
const data = await gql(MYPARCEL_SENDERS_QUERY, {}, options);
|
|
170
|
+
return data.myparcelSenderStockLocations;
|
|
171
|
+
},
|
|
172
|
+
async set(input, options) {
|
|
173
|
+
const data = await gql(SET_MYPARCEL_SENDER_MUTATION, { input }, options);
|
|
174
|
+
return settled(data.setStockLocationSender);
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
@@ -46,6 +46,8 @@ export interface ParcelCountSuggestion {
|
|
|
46
46
|
* or `ReadFulfillment`. A malformed input is refused before any request with
|
|
47
47
|
* `PARCEL_COUNT_INPUT_INVALID` ({@link ValidationError}); an unknown variant,
|
|
48
48
|
* or another store's, is `PARCEL_COUNT_VARIANT_NOT_FOUND` (`NotFoundError`).
|
|
49
|
+
*
|
|
50
|
+
* @effect read
|
|
49
51
|
*/
|
|
50
52
|
export type ParcelCountSuggestionMethod = (input: ParcelCountSuggestionInput, options?: Call) => Promise<ParcelCountSuggestion>;
|
|
51
53
|
export declare const PARCEL_COUNT_SUGGESTION_QUERY = "\n query ParcelCountSuggestion($input: ParcelCountSuggestionInput!) {\n parcelCountSuggestion(input: $input) {\n count\n raw\n resolvable\n exact\n capped\n capacity\n maxWeightGrams\n unratedLines\n unweighedLines\n reason\n warnings\n destinationRequiresCustoms\n }\n }\n";
|
|
@@ -36,13 +36,14 @@ export interface SetParcelRuleInput {
|
|
|
36
36
|
* change reaches another process after that cache refreshes.
|
|
37
37
|
*/
|
|
38
38
|
export interface ParcelRuleModule {
|
|
39
|
+
/** @effect read */
|
|
39
40
|
get(options?: Call): Promise<ParcelRule>;
|
|
41
|
+
/**
|
|
42
|
+
* @effect write
|
|
43
|
+
* @permission UpdateChannel
|
|
44
|
+
*/
|
|
40
45
|
set(input: SetParcelRuleInput, options?: Call): Promise<ParcelRule>;
|
|
41
46
|
}
|
|
42
|
-
/** `admin.shipping` (spec §9.2 `shipping`). */
|
|
43
|
-
export interface ShippingModule {
|
|
44
|
-
parcelRule: ParcelRuleModule;
|
|
45
|
-
}
|
|
46
47
|
export declare const PARCEL_RULE_QUERY = "\n query ParcelRule {\n activeChannel { \n id\n customFields {\n defaultParcelCapacity\n maxParcelWeightGrams\n }\n }\n }\n";
|
|
47
48
|
export declare const SET_PARCEL_RULE_MUTATION = "\n mutation SetParcelRule($input: UpdateChannelInput!) {\n updateChannel(input: $input) {\n ... on Channel { \n id\n customFields {\n defaultParcelCapacity\n maxParcelWeightGrams\n }\n }\n ... on ErrorResult { errorCode message }\n }\n }\n";
|
|
48
|
-
export declare function
|
|
49
|
+
export declare function createParcelRuleModule(transport: Transport): ParcelRuleModule;
|
|
@@ -58,34 +58,32 @@ function customFieldsOf(input) {
|
|
|
58
58
|
}
|
|
59
59
|
return fields;
|
|
60
60
|
}
|
|
61
|
-
export function
|
|
61
|
+
export function createParcelRuleModule(transport) {
|
|
62
62
|
const get = async (options) => {
|
|
63
63
|
const data = await transport.graphql({ api: 'admin', query: PARCEL_RULE_QUERY }, options);
|
|
64
64
|
return toRule(data.activeChannel);
|
|
65
65
|
};
|
|
66
66
|
return {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return toRule(data.updateChannel);
|
|
88
|
-
},
|
|
67
|
+
get,
|
|
68
|
+
async set(input, options) {
|
|
69
|
+
assertBound('defaultParcelCapacity', input?.defaultParcelCapacity, PARCEL_CAPACITY_MAX, 'items');
|
|
70
|
+
assertBound('maxParcelWeightGrams', input?.maxParcelWeightGrams, PARCEL_WEIGHT_GRAMS_MAX, 'grams');
|
|
71
|
+
const customFields = customFieldsOf(input ?? {});
|
|
72
|
+
if (Object.keys(customFields).length === 0) {
|
|
73
|
+
throw invalid('input', 'Pass defaultParcelCapacity and/or maxParcelWeightGrams; an empty input changes nothing.');
|
|
74
|
+
}
|
|
75
|
+
// `updateChannel` needs the id, and the active channel is the one the
|
|
76
|
+
// client is scoped to — read it with the SAME call options, so a
|
|
77
|
+
// per-call tenant writes its own channel rather than the client's.
|
|
78
|
+
const { channelId } = await get(options);
|
|
79
|
+
const data = await transport.graphql({
|
|
80
|
+
api: 'admin',
|
|
81
|
+
query: SET_PARCEL_RULE_MUTATION,
|
|
82
|
+
variables: { input: { id: channelId, customFields } },
|
|
83
|
+
}, options);
|
|
84
|
+
if (isErrorResult(data.updateChannel))
|
|
85
|
+
throw errorFromErrorResult(data.updateChannel);
|
|
86
|
+
return toRule(data.updateChannel);
|
|
89
87
|
},
|
|
90
88
|
};
|
|
91
89
|
}
|