@jskit-ai/connectors-catalog 0.1.1
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 +192 -0
- package/docs/ai-upstream-notices.md +51 -0
- package/docs/ai.md +191 -0
- package/docs/airtable.md +130 -0
- package/docs/algolia.md +190 -0
- package/docs/amazon-redshift.md +229 -0
- package/docs/amplitude.md +210 -0
- package/docs/apify.md +162 -0
- package/docs/apollo-io.md +200 -0
- package/docs/asana.md +126 -0
- package/docs/ashby.md +188 -0
- package/docs/atlassian.md +224 -0
- package/docs/attention.md +162 -0
- package/docs/aws-athena.md +150 -0
- package/docs/aws-credentials.md +75 -0
- package/docs/aws-s3.md +193 -0
- package/docs/bigquery.md +146 -0
- package/docs/brevo.md +122 -0
- package/docs/calendly.md +158 -0
- package/docs/canva.md +191 -0
- package/docs/chargebee.md +192 -0
- package/docs/clay.md +194 -0
- package/docs/clickhouse.md +242 -0
- package/docs/confidence-exp.md +74 -0
- package/docs/confidence-flags.md +75 -0
- package/docs/confidence-oauth.md +137 -0
- package/docs/contentful.md +123 -0
- package/docs/databricks.md +180 -0
- package/docs/dbt-semantic-layer.md +175 -0
- package/docs/elevenlabs.md +102 -0
- package/docs/figma.md +132 -0
- package/docs/firebase-cloud-messaging.md +262 -0
- package/docs/firecrawl.md +137 -0
- package/docs/fireflies.md +95 -0
- package/docs/fireworks-ai.md +53 -0
- package/docs/gatewayapi.md +113 -0
- package/docs/gemini-enterprise.md +224 -0
- package/docs/github-api.md +133 -0
- package/docs/gitlab-api.md +138 -0
- package/docs/gmail.md +133 -0
- package/docs/gong.md +205 -0
- package/docs/google-ads.md +156 -0
- package/docs/google-analytics.md +202 -0
- package/docs/google-docs.md +105 -0
- package/docs/google-drive.md +108 -0
- package/docs/google-maps-platform.md +208 -0
- package/docs/google-oauth.md +123 -0
- package/docs/google-search-console.md +124 -0
- package/docs/google-sheets.md +94 -0
- package/docs/google-slides.md +95 -0
- package/docs/granola.md +178 -0
- package/docs/hex.md +161 -0
- package/docs/heygen.md +124 -0
- package/docs/hubspot.md +142 -0
- package/docs/incident-io.md +118 -0
- package/docs/inngest.md +178 -0
- package/docs/klipy.md +149 -0
- package/docs/lexware.md +144 -0
- package/docs/lightspeed.md +193 -0
- package/docs/linear.md +137 -0
- package/docs/linkedin.md +196 -0
- package/docs/logo-dev.md +152 -0
- package/docs/mailgun.md +153 -0
- package/docs/mapbox.md +174 -0
- package/docs/microsoft-excel.md +97 -0
- package/docs/microsoft-fabric.md +249 -0
- package/docs/microsoft-oauth.md +143 -0
- package/docs/microsoft-onedrive.md +76 -0
- package/docs/microsoft-onenote.md +75 -0
- package/docs/microsoft-outlook.md +90 -0
- package/docs/microsoft-powerpoint.md +119 -0
- package/docs/microsoft-sharepoint.md +75 -0
- package/docs/microsoft-teams.md +72 -0
- package/docs/microsoft-word.md +119 -0
- package/docs/miro.md +117 -0
- package/docs/n8n.md +169 -0
- package/docs/notion.md +137 -0
- package/docs/oura.md +161 -0
- package/docs/paddle.md +168 -0
- package/docs/perplexity.md +85 -0
- package/docs/pipedrive.md +120 -0
- package/docs/polar.md +63 -0
- package/docs/posthog.md +227 -0
- package/docs/prestashop.md +151 -0
- package/docs/replicate.md +60 -0
- package/docs/resend.md +153 -0
- package/docs/salesforce.md +234 -0
- package/docs/sanity.md +122 -0
- package/docs/semrush.md +276 -0
- package/docs/sentry.md +112 -0
- package/docs/sevdesk.md +107 -0
- package/docs/shopify.md +206 -0
- package/docs/slack.md +257 -0
- package/docs/snowflake.md +271 -0
- package/docs/storyblok.md +130 -0
- package/docs/stripe.md +77 -0
- package/docs/supabase.md +51 -0
- package/docs/tally.md +105 -0
- package/docs/telegram.md +165 -0
- package/docs/tiktok.md +201 -0
- package/docs/twilio.md +284 -0
- package/docs/twitch.md +428 -0
- package/docs/wave.md +440 -0
- package/docs/wix.md +1249 -0
- package/docs/wiz.md +177 -0
- package/docs/woocommerce.md +400 -0
- package/docs/wordpress-com.md +358 -0
- package/docs/wordpress-self-hosted.md +252 -0
- package/docs/workday.md +292 -0
- package/docs/x-twitter.md +188 -0
- package/docs/xero.md +429 -0
- package/docs/zoho-books.md +214 -0
- package/docs/zoho-crm.md +192 -0
- package/package.json +153 -0
- package/patterns/ai-connections/PATTERN.md +84 -0
- package/patterns/ai-connections/example/ai-model.js +11 -0
- package/patterns/ai-connections/example/perplexity-answer.js +29 -0
- package/patterns/api-key-connection/PATTERN.md +368 -0
- package/patterns/api-key-connection/example/connections.js +18 -0
- package/patterns/api-key-connection/example/integrations.json +19 -0
- package/patterns/assistant-mcp/PATTERN.md +110 -0
- package/patterns/assistant-mcp/example/integrations.json +13 -0
- package/patterns/assistant-mcp-oauth/PATTERN.md +235 -0
- package/patterns/assistant-mcp-oauth/example/integrations.json +102 -0
- package/patterns/aws-storage-queries/PATTERN.md +169 -0
- package/patterns/aws-storage-queries/example/formats/data-formats.js +35 -0
- package/patterns/aws-storage-queries/example/formats/package-lock.json +49 -0
- package/patterns/aws-storage-queries/example/formats/package.json +12 -0
- package/patterns/aws-storage-queries/example/formats/verify-formats.mjs +37 -0
- package/patterns/aws-storage-queries/example/integrations.json +38 -0
- package/patterns/aws-storage-queries/example/s3-transfer.js +34 -0
- package/patterns/event-delivery/PATTERN.md +151 -0
- package/patterns/event-delivery/example/integrations.json +17 -0
- package/patterns/firebase-messaging/PATTERN.md +156 -0
- package/patterns/firebase-messaging/example/server/notifications.js +23 -0
- package/patterns/google-ads-search/PATTERN.md +92 -0
- package/patterns/google-ads-search/example/ads-setup.js +25 -0
- package/patterns/oauth-connection/PATTERN.md +724 -0
- package/patterns/oauth-connection/example/integrations.json +20 -0
- package/patterns/paddle-catalogue/PATTERN.md +78 -0
- package/patterns/paddle-catalogue/example/create-products.js +38 -0
- package/patterns/public-image/PATTERN.md +84 -0
- package/patterns/public-image/example/integrations.json +13 -0
- package/patterns/public-image/example/logo-url.js +16 -0
- package/patterns/redshift-queries/PATTERN.md +150 -0
- package/patterns/redshift-queries/example/integrations.json +33 -0
- package/patterns/source-scanning/PATTERN.md +83 -0
- package/patterns/source-scanning/example/source-scanner.js +13 -0
- package/src/client/firebase-cloud-messaging.js +10 -0
- package/src/client/google-analytics.js +72 -0
- package/src/client/google-maps-platform.js +39 -0
- package/src/client/logo-dev.js +36 -0
- package/src/server/ai.js +46 -0
- package/src/server/airtable.js +67 -0
- package/src/server/algolia.js +63 -0
- package/src/server/amazon-redshift.js +96 -0
- package/src/server/amplitude.js +31 -0
- package/src/server/apify.js +68 -0
- package/src/server/apollo-io.js +85 -0
- package/src/server/asana.js +64 -0
- package/src/server/ashby.js +61 -0
- package/src/server/atlassian.js +32 -0
- package/src/server/attention.js +93 -0
- package/src/server/aws-athena.js +82 -0
- package/src/server/aws-s3.js +49 -0
- package/src/server/awsTransport.js +75 -0
- package/src/server/bigquery.js +97 -0
- package/src/server/brevo.js +97 -0
- package/src/server/calendly.js +77 -0
- package/src/server/canva.js +13 -0
- package/src/server/chargebee.js +88 -0
- package/src/server/clay.js +78 -0
- package/src/server/clickhouse.js +66 -0
- package/src/server/confidence.js +29 -0
- package/src/server/contentful.js +40 -0
- package/src/server/databricks.js +55 -0
- package/src/server/dbt-semantic-layer.js +95 -0
- package/src/server/elevenlabs.js +81 -0
- package/src/server/figma.js +24 -0
- package/src/server/firebase-cloud-messaging.js +111 -0
- package/src/server/firecrawl.js +70 -0
- package/src/server/fireflies.js +31 -0
- package/src/server/fireworks-ai.js +16 -0
- package/src/server/gatewayapi.js +48 -0
- package/src/server/gemini-enterprise.js +47 -0
- package/src/server/github-api.js +90 -0
- package/src/server/gitlab-api.js +67 -0
- package/src/server/gmail.js +60 -0
- package/src/server/gong.js +52 -0
- package/src/server/google-ads-search.js +134 -0
- package/src/server/google-ads.js +80 -0
- package/src/server/google-docs.js +36 -0
- package/src/server/google-drive.js +71 -0
- package/src/server/google-maps-platform.js +90 -0
- package/src/server/google-search-console.js +31 -0
- package/src/server/google-sheets.js +60 -0
- package/src/server/google-slides.js +36 -0
- package/src/server/google.js +61 -0
- package/src/server/granola.js +115 -0
- package/src/server/graphqlOperation.js +15 -0
- package/src/server/hex.js +29 -0
- package/src/server/heygen.js +54 -0
- package/src/server/hubspot.js +85 -0
- package/src/server/incident-io.js +68 -0
- package/src/server/inngest.js +86 -0
- package/src/server/jsonOperation.js +34 -0
- package/src/server/klipy.js +34 -0
- package/src/server/lexware.js +59 -0
- package/src/server/lightspeed.js +82 -0
- package/src/server/linear.js +52 -0
- package/src/server/linkedin.js +74 -0
- package/src/server/mailgun.js +76 -0
- package/src/server/mapbox.js +46 -0
- package/src/server/mcpRegistration.js +41 -0
- package/src/server/mcpTransport.js +60 -0
- package/src/server/microsoft-excel.js +67 -0
- package/src/server/microsoft-fabric.js +81 -0
- package/src/server/microsoft-onedrive.js +58 -0
- package/src/server/microsoft-onenote.js +52 -0
- package/src/server/microsoft-outlook.js +58 -0
- package/src/server/microsoft-powerpoint.js +5 -0
- package/src/server/microsoft-sharepoint.js +46 -0
- package/src/server/microsoft-teams.js +29 -0
- package/src/server/microsoft-word.js +5 -0
- package/src/server/microsoft.js +88 -0
- package/src/server/miro.js +29 -0
- package/src/server/n8n.js +78 -0
- package/src/server/notion.js +76 -0
- package/src/server/oura.js +48 -0
- package/src/server/paddle.js +83 -0
- package/src/server/perplexity.js +15 -0
- package/src/server/pipedrive.js +95 -0
- package/src/server/polar.js +20 -0
- package/src/server/posthog.js +44 -0
- package/src/server/prestashop.js +42 -0
- package/src/server/replicate.js +42 -0
- package/src/server/resend.js +93 -0
- package/src/server/salesforce.js +84 -0
- package/src/server/sanity.js +31 -0
- package/src/server/semrush.js +359 -0
- package/src/server/sentry.js +30 -0
- package/src/server/sevdesk.js +20 -0
- package/src/server/shopify.js +99 -0
- package/src/server/slack.js +143 -0
- package/src/server/snowflake.js +139 -0
- package/src/server/storyblok.js +49 -0
- package/src/server/stripe.js +13 -0
- package/src/server/supabase.js +13 -0
- package/src/server/tally.js +78 -0
- package/src/server/telegram.js +55 -0
- package/src/server/tiktok.js +86 -0
- package/src/server/twilio.js +90 -0
- package/src/server/twitch.js +409 -0
- package/src/server/wave.js +520 -0
- package/src/server/wix.js +1048 -0
- package/src/server/wiz.js +117 -0
- package/src/server/woocommerce.js +412 -0
- package/src/server/wordpress-com.js +236 -0
- package/src/server/wordpress-self-hosted.js +134 -0
- package/src/server/workday.js +136 -0
- package/src/server/x-twitter.js +110 -0
- package/src/server/xero.js +397 -0
- package/src/server/zoho-books.js +90 -0
- package/src/server/zoho-crm.js +89 -0
- package/src/server/zohoOAuth.js +21 -0
- package/src/shared/ai-models.json +8049 -0
- package/src/shared/ai.js +63 -0
- package/src/shared/algolia.js +29 -0
- package/src/shared/amazon-redshift.js +51 -0
- package/src/shared/amplitude.js +28 -0
- package/src/shared/atlassian.js +43 -0
- package/src/shared/aws.js +72 -0
- package/src/shared/canva.js +66 -0
- package/src/shared/chargebee.js +26 -0
- package/src/shared/clickhouse.js +38 -0
- package/src/shared/confidence.js +28 -0
- package/src/shared/databricks.js +33 -0
- package/src/shared/dbt-semantic-layer.js +32 -0
- package/src/shared/definitions.js +140 -0
- package/src/shared/figma.js +21 -0
- package/src/shared/firebase-cloud-messaging.js +45 -0
- package/src/shared/gemini-enterprise.js +33 -0
- package/src/shared/gong.js +33 -0
- package/src/shared/google-ads-search.js +31 -0
- package/src/shared/google-ads.js +28 -0
- package/src/shared/google-maps-platform.js +28 -0
- package/src/shared/google.js +133 -0
- package/src/shared/granola.js +25 -0
- package/src/shared/hex.js +28 -0
- package/src/shared/inngest.js +35 -0
- package/src/shared/lightspeed.js +75 -0
- package/src/shared/linkedin.js +27 -0
- package/src/shared/logo-dev.js +21 -0
- package/src/shared/mapbox.js +38 -0
- package/src/shared/mcp.js +92 -0
- package/src/shared/microsoft-fabric.js +47 -0
- package/src/shared/microsoft.js +231 -0
- package/src/shared/miro.js +27 -0
- package/src/shared/oura.js +23 -0
- package/src/shared/posthog.js +33 -0
- package/src/shared/prestashop.js +23 -0
- package/src/shared/salesforce.js +36 -0
- package/src/shared/semrush.js +24 -0
- package/src/shared/shopify.js +51 -0
- package/src/shared/siteUrl.js +12 -0
- package/src/shared/slack.js +88 -0
- package/src/shared/snowflake.js +55 -0
- package/src/shared/tiktok.js +22 -0
- package/src/shared/tokens.js +747 -0
- package/src/shared/twilio.js +41 -0
- package/src/shared/twitch.js +49 -0
- package/src/shared/wave.js +52 -0
- package/src/shared/wix.js +40 -0
- package/src/shared/wiz.js +41 -0
- package/src/shared/wordpress-com.js +30 -0
- package/src/shared/wordpress.js +69 -0
- package/src/shared/workday.js +50 -0
- package/src/shared/x-twitter.js +19 -0
- package/src/shared/xero.js +46 -0
- package/src/shared/zoho-books.js +39 -0
- package/src/shared/zoho-crm.js +43 -0
- package/src/shared/zoho-regions.js +12 -0
- package/test/ai.test.js +136 -0
- package/test/airtable.test.js +125 -0
- package/test/algolia.test.js +207 -0
- package/test/amazon-redshift.test.js +272 -0
- package/test/amplitude.test.js +239 -0
- package/test/apify.test.js +93 -0
- package/test/apollo-io.test.js +125 -0
- package/test/asana.test.js +99 -0
- package/test/ashby.test.js +97 -0
- package/test/atlassian.test.js +249 -0
- package/test/attention.test.js +79 -0
- package/test/aws.test.js +405 -0
- package/test/brevo.test.js +71 -0
- package/test/calendly.test.js +136 -0
- package/test/canva.test.js +258 -0
- package/test/chargebee.test.js +195 -0
- package/test/clickhouse.test.js +278 -0
- package/test/confidence.test.js +205 -0
- package/test/databricks.test.js +253 -0
- package/test/dbt-semantic-layer.test.js +221 -0
- package/test/designMcp.test.js +336 -0
- package/test/elevenlabs.test.js +52 -0
- package/test/firebase-cloud-messaging.test.js +242 -0
- package/test/firecrawl.test.js +66 -0
- package/test/gemini-enterprise.test.js +218 -0
- package/test/github-api.test.js +167 -0
- package/test/gitlab-api.test.js +160 -0
- package/test/gong.test.js +192 -0
- package/test/google-ads.test.js +350 -0
- package/test/google-maps-platform.test.js +191 -0
- package/test/google.test.js +710 -0
- package/test/granola.test.js +240 -0
- package/test/hex.test.js +253 -0
- package/test/heygen.test.js +121 -0
- package/test/hubspot.test.js +103 -0
- package/test/inngest.test.js +219 -0
- package/test/lightspeed.test.js +250 -0
- package/test/linear.test.js +146 -0
- package/test/linkedin.test.js +243 -0
- package/test/logo-dev.test.js +79 -0
- package/test/mailgun.test.js +107 -0
- package/test/mapbox.test.js +181 -0
- package/test/mcp.test.js +227 -0
- package/test/microsoft-fabric.test.js +258 -0
- package/test/microsoft.test.js +554 -0
- package/test/n8nOAuth.test.js +188 -0
- package/test/notionOAuth.test.js +183 -0
- package/test/oura.test.js +211 -0
- package/test/paddleCatalogue.test.js +51 -0
- package/test/paddlePermissions.test.js +101 -0
- package/test/perplexityComposition.test.js +39 -0
- package/test/pipedriveOAuth.test.js +140 -0
- package/test/posthog.test.js +200 -0
- package/test/prestashop.test.js +207 -0
- package/test/resend.test.js +163 -0
- package/test/salesforce.test.js +276 -0
- package/test/semrush.test.js +479 -0
- package/test/sentry.test.js +35 -0
- package/test/shopify.test.js +247 -0
- package/test/slack.test.js +320 -0
- package/test/snowflake.test.js +391 -0
- package/test/tally.test.js +75 -0
- package/test/telegram.test.js +58 -0
- package/test/tiktok.test.js +243 -0
- package/test/tokens.test.js +1079 -0
- package/test/twilio.test.js +221 -0
- package/test/twitch.test.js +573 -0
- package/test/wave.test.js +678 -0
- package/test/wix.test.js +1099 -0
- package/test/wiz.test.js +185 -0
- package/test/wordpress-com.test.js +469 -0
- package/test/wordpress.test.js +660 -0
- package/test/workday.test.js +314 -0
- package/test/x-twitter.test.js +256 -0
- package/test/xero.test.js +700 -0
- package/test/zoho-books.test.js +253 -0
- package/test/zoho-crm.test.js +223 -0
|
@@ -0,0 +1,1048 @@
|
|
|
1
|
+
import { ConnectorError } from "@jskit-ai/connectors-core/server";
|
|
2
|
+
import { createSchema } from "json-rest-schema";
|
|
3
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
4
|
+
import { wixDefinition } from "../shared/wix.js";
|
|
5
|
+
|
|
6
|
+
// Preserve native Wix JSON without silently dropping unsupported values.
|
|
7
|
+
function serializeNativeJson(value) {
|
|
8
|
+
return JSON.stringify(value, (key, item) => {
|
|
9
|
+
if (["undefined", "function", "symbol", "bigint"].includes(typeof item) ||
|
|
10
|
+
typeof item === "number" && !Number.isFinite(item)) throw new Error("JSON required");
|
|
11
|
+
return item;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const guid = (value) => typeof value === "string" && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/iu.test(value);
|
|
16
|
+
const cursor = (value) => typeof value === "string" && value.length > 0 && value.length <= 16000 && !/[\p{Cc}]/u.test(value);
|
|
17
|
+
const querySchema = createSchema({
|
|
18
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
|
|
19
|
+
cursor: { type: "string", minLength: 1, maxLength: 16000, noTrim: true, validator: (value) => cursor(value) || "Use the returned Wix page cursor." }
|
|
20
|
+
});
|
|
21
|
+
const contactsSchema = createSchema({
|
|
22
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
|
|
23
|
+
offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 },
|
|
24
|
+
search: { type: "string", minLength: 1, maxLength: 100 }
|
|
25
|
+
});
|
|
26
|
+
function bookingDirectoryOperation(kind) {
|
|
27
|
+
const staff = kind === "staff";
|
|
28
|
+
return {
|
|
29
|
+
scopes: [],
|
|
30
|
+
request(input, settings) {
|
|
31
|
+
const cursorPaging = validateSchemaPayload({ schema: querySchema, mode: "replace" }, input, { statusCode: 422 });
|
|
32
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
33
|
+
return { method: "POST", url: staff ? "https://www.wixapis.com/bookings/v1/staff-members/query" : "https://www.wixapis.com/bookings/v2/categories/query",
|
|
34
|
+
headers: { "wix-site-id": settings.siteId }, body: {
|
|
35
|
+
query: { cursorPaging, sort: [{ fieldName: "id", order: "ASC" }], ...(staff ? { filter: { serviceProvider: true } } : {}) },
|
|
36
|
+
...(staff ? { fields: ["RESOURCE_DETAILS"] } : {})
|
|
37
|
+
} };
|
|
38
|
+
},
|
|
39
|
+
validateResult(value) {
|
|
40
|
+
const values = staff ? value?.staffMembers : value?.categories;
|
|
41
|
+
const paging = value?.pagingMetadata;
|
|
42
|
+
return Array.isArray(values) && values.length <= 100 && values.every(item => guid(item?.id) && typeof item.name === "string" &&
|
|
43
|
+
(!staff || guid(item.resourceId))) && (paging === undefined || paging && typeof paging === "object" && !Array.isArray(paging) &&
|
|
44
|
+
(paging.hasNext === undefined || typeof paging.hasNext === "boolean") &&
|
|
45
|
+
(paging.cursors === undefined || paging.cursors && typeof paging.cursors === "object" && !Array.isArray(paging.cursors) &&
|
|
46
|
+
["next", "prev"].every(key => paging.cursors[key] === undefined || cursor(paging.cursors[key]))));
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const contactIdField = { type: "string", required: true, validator: value => guid(value) || "Use the contact ID returned by Wix." };
|
|
51
|
+
const contactInfoSchema = createSchema({
|
|
52
|
+
name: { type: "object", schema: createSchema({ first: { type: "string", maxLength: 1000 }, last: { type: "string", maxLength: 1000 } }) },
|
|
53
|
+
company: { type: "string", maxLength: 1000 }, jobTitle: { type: "string", maxLength: 1000 },
|
|
54
|
+
emails: { type: "object", schema: createSchema({ items: { type: "array", required: true, validator: values => values.length <= 50 || "Use at most 50 email addresses.",
|
|
55
|
+
items: { type: "object", schema: createSchema({ email: { type: "string", required: true, minLength: 1, maxLength: 320 },
|
|
56
|
+
primary: { type: "boolean" }, tag: { type: "string", enum: ["UNTAGGED", "MAIN", "HOME", "WORK"] } }) } } }) },
|
|
57
|
+
phones: { type: "object", schema: createSchema({ items: { type: "array", required: true, validator: values => values.length <= 50 || "Use at most 50 phone numbers.",
|
|
58
|
+
items: { type: "object", schema: createSchema({ phone: { type: "string", required: true, minLength: 1, maxLength: 50 },
|
|
59
|
+
countryCode: { type: "string", validator: value => /^[A-Z]{2}$/u.test(value) || "Use a two-letter country code." }, primary: { type: "boolean" } }) } } }) }
|
|
60
|
+
});
|
|
61
|
+
const validContact = value => guid(value?.id) && Number.isSafeInteger(value.revision) && value.revision >= 0 &&
|
|
62
|
+
value.info && typeof value.info === "object" && !Array.isArray(value.info);
|
|
63
|
+
function contactOperation(action) {
|
|
64
|
+
const fields = action === "get" ? { contactId: contactIdField } : {
|
|
65
|
+
...(action === "update" ? { contactId: contactIdField, revision: { type: "integer", required: true, min: 0, max: 2147483647 } } : {}),
|
|
66
|
+
info: { type: "object", required: true, schema: contactInfoSchema }, allowDuplicates: { type: "boolean", defaultTo: false }
|
|
67
|
+
};
|
|
68
|
+
const schema = createSchema(fields);
|
|
69
|
+
return {
|
|
70
|
+
scopes: [],
|
|
71
|
+
request(input, settings) {
|
|
72
|
+
const { contactId, ...body } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
|
|
73
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the project's Wix Site ID first.", { statusCode: 422 });
|
|
74
|
+
if (action !== "get" && (!Object.keys(body.info).length || (action === "create" &&
|
|
75
|
+
!(body.info.name?.first || body.info.name?.last || body.info.emails?.items?.length || body.info.phones?.items?.length)))) {
|
|
76
|
+
throw new ConnectorError("connector_input_invalid", "Provide contact details; creation requires a name, email or phone.", { statusCode: 422 });
|
|
77
|
+
}
|
|
78
|
+
return { method: action === "get" ? "GET" : action === "create" ? "POST" : "PATCH",
|
|
79
|
+
url: `https://www.wixapis.com/contacts/v4/contacts${contactId ? `/${contactId}` : ""}`,
|
|
80
|
+
headers: { "wix-site-id": settings.siteId }, ...(action === "get" ? {} : { body }) };
|
|
81
|
+
},
|
|
82
|
+
validateResult: value => validContact(value?.contact)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const servicePagingSchema = createSchema({ limit: { type: "integer", min: 1, max: 100, defaultTo: 20 }, offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 } });
|
|
86
|
+
const serviceIdSchema = createSchema({ serviceId: contactIdField });
|
|
87
|
+
const nativeJsonObjectField = { type: "none", validator: value => {
|
|
88
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return "Use a native Wix JSON object.";
|
|
89
|
+
try {
|
|
90
|
+
const json = serializeNativeJson(value);
|
|
91
|
+
return Buffer.byteLength(json) <= 100000 || "Keep native Wix object data within 100KB.";
|
|
92
|
+
} catch { return "Use serializable native Wix JSON data."; }
|
|
93
|
+
} };
|
|
94
|
+
const serviceUpdateSchema = createSchema({
|
|
95
|
+
serviceId: contactIdField,
|
|
96
|
+
revision: { type: "none", required: true, validator: value => typeof value === "string" && /^\d{1,19}$/u.test(value) && BigInt(value) <= 9223372036854775807n || "Use the returned service revision string." },
|
|
97
|
+
name: { type: "string", minLength: 1, maxLength: 400 }, description: { type: "string", maxLength: 7000 }, tagLine: { type: "string", maxLength: 6000 },
|
|
98
|
+
payment: nativeJsonObjectField,
|
|
99
|
+
onlineBooking: { type: "object", schema: createSchema({ enabled: { type: "boolean" }, requireManualApproval: { type: "boolean" }, allowMultipleRequests: { type: "boolean" } }) },
|
|
100
|
+
defaultCapacity: { type: "integer", min: 1, max: 1000 },
|
|
101
|
+
staffMemberIds: { type: "array", validator: values => values.length > 0 && values.length <= 100 || "Select 1–100 staff resource IDs.", items: { type: "string", validator: value => guid(value) || "Use a staff resource ID." } },
|
|
102
|
+
schedule: { type: "object", schema: createSchema({ availabilityConstraints: { type: "object", required: true, schema: createSchema({
|
|
103
|
+
sessionDurations: { type: "array", validator: values => values.length > 0 && values.length <= 50 || "Use 1–50 appointment durations.", items: { type: "integer", min: 1 } },
|
|
104
|
+
timeBetweenSessions: { type: "integer", min: 0 }
|
|
105
|
+
}) } }) }
|
|
106
|
+
});
|
|
107
|
+
const validService = value => guid(value?.id) && typeof value.name === "string" && typeof value.revision === "string" && /^\d{1,19}$/u.test(value.revision) &&
|
|
108
|
+
["APPOINTMENT", "CLASS", "COURSE"].includes(value.type);
|
|
109
|
+
const localDate = value => typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/u.test(value) &&
|
|
110
|
+
Number.isFinite(Date.parse(`${value}Z`)) && new Date(`${value}Z`).toISOString().slice(0, 19) === value;
|
|
111
|
+
const validSlot = value => guid(value?.serviceId) && localDate(value.localStartDate) && localDate(value.localEndDate) &&
|
|
112
|
+
value.localStartDate < value.localEndDate && typeof value.bookable === "boolean";
|
|
113
|
+
const availabilityLocationSchema = createSchema({
|
|
114
|
+
id: { type: "string", validator: value => guid(value) || "Use the Wix business location ID." },
|
|
115
|
+
locationType: { type: "string", required: true, enum: ["BUSINESS", "CUSTOM", "CUSTOMER"] },
|
|
116
|
+
name: { type: "string", maxLength: 1000 }, formattedAddress: { type: "string", maxLength: 4000 }
|
|
117
|
+
});
|
|
118
|
+
function availabilityOperation(action, events = false) {
|
|
119
|
+
const schema = createSchema({
|
|
120
|
+
...(!events ? {
|
|
121
|
+
customerChoices: { type: "object", schema: createSchema({
|
|
122
|
+
durationInMinutes: { type: "integer", min: 1, max: 57600 },
|
|
123
|
+
addOnIds: { type: "array", validator: values => values.length > 0 && values.length <= 21 && new Set(values).size === values.length || "Select 1–21 distinct add-ons.",
|
|
124
|
+
items: { type: "string", validator: value => guid(value) || "Use the add-on ID returned by Wix." } }
|
|
125
|
+
}) },
|
|
126
|
+
includeResourceTypeIds: { type: "array", validator: values => values.length <= 100 || "Choose at most 100 resource types.", items: { type: "string", validator: value => guid(value) || "Use a resource type ID." } },
|
|
127
|
+
...(action === "list" ? { locations: { type: "array", validator: values => values.length <= 5 || "Choose at most five locations.", items: { type: "object", schema: availabilityLocationSchema } } } : {
|
|
128
|
+
location: { type: "object", schema: availabilityLocationSchema },
|
|
129
|
+
resourceTypes: { type: "array", validator: values => values.length <= 8 || "Choose at most eight resource types.", items: { type: "object", schema: createSchema({
|
|
130
|
+
resourceTypeId: { type: "string", required: true, validator: value => guid(value) || "Use a resource type ID." },
|
|
131
|
+
resourceIds: { type: "array", required: true, validator: values => values.length > 0 && values.length <= 135 || "Choose 1–135 resource IDs.", items: { type: "string", validator: value => guid(value) || "Use a resource ID." } }
|
|
132
|
+
}) } }
|
|
133
|
+
})
|
|
134
|
+
} : {}),
|
|
135
|
+
...(events ? { minBookableCapacity: { type: "integer", min: 1, max: 1000 } } : {}),
|
|
136
|
+
serviceId: { type: "string", validator: value => guid(value) || "Use the appointment service ID." },
|
|
137
|
+
...(action === "list" ? {
|
|
138
|
+
fromLocalDate: { type: "string", validator: value => localDate(value) || "Use YYYY-MM-DDThh:mm:ss without an offset." },
|
|
139
|
+
toLocalDate: { type: "string", validator: value => localDate(value) || "Use YYYY-MM-DDThh:mm:ss without an offset." },
|
|
140
|
+
limit: { type: "integer", min: 1, max: 1000, defaultTo: 100 },
|
|
141
|
+
cursor: { type: "string", validator: value => cursor(value) || "Use the returned page cursor." }
|
|
142
|
+
} : {
|
|
143
|
+
localStartDate: { type: "string", required: true, validator: value => localDate(value) || "Use the returned local start date." },
|
|
144
|
+
localEndDate: { type: "string", required: true, validator: value => localDate(value) || "Use the returned local end date." }
|
|
145
|
+
}),
|
|
146
|
+
timeZone: { type: "string", maxLength: events ? 100 : 150, validator: value => {
|
|
147
|
+
try { new Intl.DateTimeFormat("en", { timeZone: value }); return true; } catch { return "Use an IANA time zone."; }
|
|
148
|
+
} }
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
scopes: [],
|
|
152
|
+
request(input, settings) {
|
|
153
|
+
const { limit, cursor: next, ...body } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
|
|
154
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
155
|
+
if (next && Object.keys(body).length) throw new ConnectorError("connector_input_invalid", "Use only cursor and limit for the next availability page.", { statusCode: 422 });
|
|
156
|
+
if (body.customerChoices && !Object.keys(body.customerChoices).length) throw new ConnectorError("connector_input_invalid", "Select a duration or add-ons before supplying customer choices.", { statusCode: 422 });
|
|
157
|
+
if (!next && (!body.serviceId || !body.timeZone || (action === "list" ? !body.fromLocalDate || !body.toLocalDate || body.fromLocalDate >= body.toLocalDate : body.localStartDate >= body.localEndDate))) {
|
|
158
|
+
throw new ConnectorError("connector_input_invalid", "Choose the service, time zone and an increasing local date range.", { statusCode: 422 });
|
|
159
|
+
}
|
|
160
|
+
const locations = body.locations ?? (body.location ? [body.location] : []);
|
|
161
|
+
if (locations.some(location => location.locationType === "BUSINESS" && !location.id)) throw new ConnectorError("connector_input_invalid", "Choose the business location ID.", { statusCode: 422 });
|
|
162
|
+
const { serviceId, ...eventBody } = body;
|
|
163
|
+
return { method: "POST", url: `https://www.wixapis.com/_api/service-availability/v2/time-slots${events ? "/event" : action === "get" ? "/get" : ""}`,
|
|
164
|
+
headers: { "wix-site-id": settings.siteId }, body: action === "get" ? body : next ? { cursorPaging: { limit, cursor: next } } : { ...(events ? { ...eventBody, serviceIds: [serviceId], includeNonBookable: false } : { ...body, bookable: true }), cursorPaging: { limit } } };
|
|
165
|
+
},
|
|
166
|
+
validateResult: value => typeof value?.timeZone === "string" && (action === "get" ? validSlot(value.timeSlot) :
|
|
167
|
+
Array.isArray(value.timeSlots) && value.timeSlots.length <= 1000 && value.timeSlots.every(slot => validSlot(slot) && (!events || typeof slot.eventInfo?.eventId === "string" && slot.eventInfo.eventId.length >= 36 && slot.eventInfo.eventId.length <= 250)) &&
|
|
168
|
+
(value[events ? "pagingMetadata" : "cursorPagingMetadata"] === undefined || value[events ? "pagingMetadata" : "cursorPagingMetadata"] && typeof value[events ? "pagingMetadata" : "cursorPagingMetadata"] === "object" && !Array.isArray(value[events ? "pagingMetadata" : "cursorPagingMetadata"]) &&
|
|
169
|
+
(value[events ? "pagingMetadata" : "cursorPagingMetadata"].cursors === undefined || value[events ? "pagingMetadata" : "cursorPagingMetadata"].cursors && typeof value[events ? "pagingMetadata" : "cursorPagingMetadata"].cursors === "object" && !Array.isArray(value[events ? "pagingMetadata" : "cursorPagingMetadata"].cursors) &&
|
|
170
|
+
["next", "prev"].every(key => value[events ? "pagingMetadata" : "cursorPagingMetadata"].cursors[key] === undefined || cursor(value[events ? "pagingMetadata" : "cursorPagingMetadata"].cursors[key])))))
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
const eventSlotSchema = createSchema({
|
|
174
|
+
eventId: { type: "string", required: true, minLength: 36, maxLength: 250, validator: value => !/[\p{Cc}]/u.test(value) || "Use the returned event ID." },
|
|
175
|
+
timeZone: { type: "string", required: true, maxLength: 100, validator: value => {
|
|
176
|
+
try { new Intl.DateTimeFormat("en", { timeZone: value }); return true; } catch { return "Use an IANA time zone."; }
|
|
177
|
+
} }
|
|
178
|
+
});
|
|
179
|
+
function productReadOperation(version, action) {
|
|
180
|
+
const schema = createSchema(action === "get" ? {
|
|
181
|
+
productId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix product ID." }
|
|
182
|
+
} : {
|
|
183
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
|
|
184
|
+
...(version === 1 ? { offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 }, includeVariants: { type: "boolean", defaultTo: false } } :
|
|
185
|
+
{ cursor: { type: "string", validator: value => cursor(value) || "Use the returned product cursor." } })
|
|
186
|
+
});
|
|
187
|
+
const validProduct = value => guid(value?.id) && typeof value.name === "string" && value.name.length > 0;
|
|
188
|
+
return {
|
|
189
|
+
scopes: [],
|
|
190
|
+
request(input, settings) {
|
|
191
|
+
const value = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
|
|
192
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
193
|
+
const base = version === 1 ? "https://www.wixapis.com/stores-reader/v1/products" : "https://www.wixapis.com/stores/v3/products";
|
|
194
|
+
if (action === "get") return { method: "GET", url: `${base}/${value.productId}`, headers: { "wix-site-id": settings.siteId } };
|
|
195
|
+
const { includeVariants, ...paging } = value;
|
|
196
|
+
return { method: "POST", url: `${base}/query`, headers: { "wix-site-id": settings.siteId },
|
|
197
|
+
body: version === 1 ? { query: { paging }, includeVariants } : { query: { cursorPaging: paging } } };
|
|
198
|
+
},
|
|
199
|
+
validateResult: value => action === "get" ? validProduct(value?.product) : Array.isArray(value?.products) && value.products.length <= 100 && value.products.every(validProduct) &&
|
|
200
|
+
(version === 1 ? (value.totalResults === undefined || Number.isSafeInteger(value.totalResults) && value.totalResults >= 0) &&
|
|
201
|
+
(value.metadata === undefined || value.metadata && typeof value.metadata === "object" && !Array.isArray(value.metadata) &&
|
|
202
|
+
["items", "offset"].every(key => value.metadata[key] === undefined || Number.isSafeInteger(value.metadata[key]) && value.metadata[key] >= 0)) :
|
|
203
|
+
(value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata) &&
|
|
204
|
+
(value.pagingMetadata.cursors === undefined || value.pagingMetadata.cursors && typeof value.pagingMetadata.cursors === "object" && !Array.isArray(value.pagingMetadata.cursors) &&
|
|
205
|
+
["next", "prev"].every(key => value.pagingMetadata.cursors[key] === undefined || cursor(value.pagingMetadata.cursors[key])))))
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
const collectionId = value => typeof value === "string" && value.length > 0 && value.length <= 256 &&
|
|
209
|
+
!/[\p{Cc}]/u.test(value) && value.split("/").every(part => part.length > 0 && ![".", ".."].includes(part));
|
|
210
|
+
const collectionListSchema = createSchema({
|
|
211
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 20 }, offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 },
|
|
212
|
+
consistentRead: { type: "boolean", defaultTo: false }
|
|
213
|
+
});
|
|
214
|
+
const cmsFilterField = { type: "none", validator: value => {
|
|
215
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return "Use a Wix filter object.";
|
|
216
|
+
try {
|
|
217
|
+
const json = serializeNativeJson(value);
|
|
218
|
+
return Buffer.byteLength(json) <= 100000 || "Keep the Wix filter within 100KB.";
|
|
219
|
+
} catch { return "Use serializable JSON filter data."; }
|
|
220
|
+
} };
|
|
221
|
+
const cmsItemQuerySchema = createSchema({
|
|
222
|
+
collectionId: { type: "string", required: true, validator: value => collectionId(value) || "Use the returned Wix collection ID." },
|
|
223
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 20 }, offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 },
|
|
224
|
+
consistentRead: { type: "boolean", defaultTo: false },
|
|
225
|
+
filter: cmsFilterField,
|
|
226
|
+
sort: { type: "array", validator: values => values.length <= 100 || "Use at most 100 sort fields.", items: { type: "object", schema: createSchema({
|
|
227
|
+
fieldName: { type: "string", required: true, minLength: 1, maxLength: 1000 }, order: { type: "string", required: true, enum: ["ASC", "DESC"] }
|
|
228
|
+
}) } },
|
|
229
|
+
fields: { type: "array", validator: values => values.length <= 1000 || "Use at most 1000 projected fields.", items: { type: "string", minLength: 1, maxLength: 5000 } },
|
|
230
|
+
returnTotalCount: { type: "boolean" },
|
|
231
|
+
includeReferences: { type: "array", validator: values => values.length <= 100 || "Use at most 100 reference configurations.", items: { type: "object", schema: createSchema({
|
|
232
|
+
field: { type: "string", required: true, minLength: 1, maxLength: 1000 }, limit: { type: "integer", min: 1, max: 1000 }
|
|
233
|
+
}) } }
|
|
234
|
+
});
|
|
235
|
+
const cmsItemIdField = { type: "string", required: true, minLength: 1, maxLength: 128,
|
|
236
|
+
validator: value => !/[\p{Cc}]/u.test(value) && ![".", ".."].includes(value) || "Use a returned Wix item ID." };
|
|
237
|
+
const cmsItemGetSchema = createSchema({
|
|
238
|
+
collectionId: { type: "string", required: true, validator: value => collectionId(value) || "Use the returned Wix collection ID." },
|
|
239
|
+
itemId: cmsItemIdField, consistentRead: { type: "boolean", defaultTo: false }
|
|
240
|
+
});
|
|
241
|
+
const cmsItemPatchSchema = createSchema({
|
|
242
|
+
collectionId: { type: "string", required: true, validator: value => collectionId(value) || "Use the returned Wix collection ID." },
|
|
243
|
+
itemId: cmsItemIdField, conditionFilter: cmsFilterField,
|
|
244
|
+
fieldModifications: { type: "none", required: true, validator: values => {
|
|
245
|
+
if (!Array.isArray(values) || values.length < 1 || values.length > 100) return "Provide 1–100 Wix field modifications.";
|
|
246
|
+
const options = { SET_FIELD: "setFieldOptions", INCREMENT_FIELD: "incrementFieldOptions", APPEND_TO_ARRAY: "appendToArrayOptions", REMOVE_FROM_ARRAY: "removeFromArrayOptions" };
|
|
247
|
+
for (const value of values) {
|
|
248
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || typeof value.fieldPath !== "string" ||
|
|
249
|
+
!value.fieldPath.length || value.fieldPath.length > 1000 || /[\p{Cc}]/u.test(value.fieldPath)) return "Use a Wix field path for each modification.";
|
|
250
|
+
if (value.action === "REMOVE_FIELD") {
|
|
251
|
+
if (Object.keys(value).some(key => !["fieldPath", "action"].includes(key))) return "REMOVE_FIELD accepts no value options.";
|
|
252
|
+
} else {
|
|
253
|
+
const key = Object.hasOwn(options, value.action) ? options[value.action] : undefined;
|
|
254
|
+
if (!key || Object.keys(value).some(name => !["fieldPath", "action", key].includes(name))) return "Use the options matching the Wix patch action.";
|
|
255
|
+
const config = value[key];
|
|
256
|
+
if (!config || typeof config !== "object" || Array.isArray(config) || !Object.hasOwn(config, "value") || Object.keys(config).length !== 1) return "Provide the patch action's value.";
|
|
257
|
+
if (value.action === "INCREMENT_FIELD" && (typeof config.value !== "number" || !Number.isFinite(config.value))) return "Use a finite increment value.";
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
const json = serializeNativeJson(values);
|
|
262
|
+
return Buffer.byteLength(json) <= 100000 || "Keep field modifications within 100KB.";
|
|
263
|
+
} catch { return "Use serializable JSON field values."; }
|
|
264
|
+
} }
|
|
265
|
+
});
|
|
266
|
+
const collectionGetSchema = createSchema({
|
|
267
|
+
collectionId: { type: "string", required: true, validator: value => collectionId(value) || "Use the returned Wix collection ID." },
|
|
268
|
+
consistentRead: { type: "boolean", defaultTo: false }
|
|
269
|
+
});
|
|
270
|
+
const validCollection = value => collectionId(value?.id) &&
|
|
271
|
+
(value.fields === undefined || Array.isArray(value.fields)) && (value.displayName === undefined || typeof value.displayName === "string");
|
|
272
|
+
const serviceLocationSchema = createSchema({
|
|
273
|
+
type: { type: "string", required: true, enum: ["BUSINESS", "CUSTOM", "CUSTOMER"] },
|
|
274
|
+
business: { type: "object", schema: createSchema({ id: contactIdField }) },
|
|
275
|
+
custom: { type: "object", schema: createSchema({ address: { ...nativeJsonObjectField, required: true } }) }
|
|
276
|
+
});
|
|
277
|
+
const setServiceLocationsSchema = createSchema({
|
|
278
|
+
serviceId: contactIdField,
|
|
279
|
+
locations: { type: "array", required: true, validator: values => values.length <= 100 || "Use at most 100 locations.", items: { type: "object", schema: serviceLocationSchema } },
|
|
280
|
+
removedLocationAction: { type: "string", required: true, enum: ["KEEP_AT_CURRENT_LOCATION", "MOVE_TO_LOCATION"] },
|
|
281
|
+
moveToLocation: { type: "object", schema: serviceLocationSchema },
|
|
282
|
+
notifyParticipants: { type: "boolean", defaultTo: false }, notificationMessage: { type: "string", maxLength: 2000 }
|
|
283
|
+
});
|
|
284
|
+
const serviceCreateSchema = createSchema({ service: { ...nativeJsonObjectField, required: true } });
|
|
285
|
+
function locationWriteOperation(action) {
|
|
286
|
+
const schema = createSchema({
|
|
287
|
+
...(action === "update" ? {
|
|
288
|
+
locationId: contactIdField,
|
|
289
|
+
revision: { type: "none", required: true, validator: value => typeof value === "string" && /^\d{1,19}$/u.test(value) && BigInt(value) <= 9223372036854775807n || "Use the current location revision string." }
|
|
290
|
+
} : {}),
|
|
291
|
+
name: { type: "string", required: true, minLength: 1, maxLength: 150 },
|
|
292
|
+
timeZone: { type: "string", required: true, maxLength: 150, validator: value => {
|
|
293
|
+
try { new Intl.DateTimeFormat("en", { timeZone: value }); return true; } catch { return "Use an IANA time zone."; }
|
|
294
|
+
} },
|
|
295
|
+
description: { type: "string", maxLength: 500 }, email: { type: "string", maxLength: 320 },
|
|
296
|
+
phone: { type: "string", maxLength: 100 }, fax: { type: "string", maxLength: 100 },
|
|
297
|
+
status: { type: "string", enum: ["ACTIVE"] },
|
|
298
|
+
locationTypes: { type: "array", validator: values => values.length <= 10 || "Use at most 10 location types.", items: { type: "string", enum: ["UNKNOWN", "BRANCH", "OFFICES", "RECEPTION", "HEADQUARTERS", "INVENTORY"] } },
|
|
299
|
+
businessSchedule: nativeJsonObjectField, extendedFields: nativeJsonObjectField,
|
|
300
|
+
address: { type: "object", required: true, schema: createSchema({
|
|
301
|
+
country: { type: "string", validator: value => /^[A-Z]{2}$/u.test(value) || "Use a two-letter country code." },
|
|
302
|
+
subdivision: { type: "string", maxLength: 100 }, city: { type: "string", maxLength: 1000 }, postalCode: { type: "string", maxLength: 20 },
|
|
303
|
+
formattedAddress: { type: "string", maxLength: 4000 }, hint: { type: "string", maxLength: 500 },
|
|
304
|
+
streetAddress: { type: "object", schema: createSchema({ number: { type: "string", maxLength: 100 }, name: { type: "string", maxLength: 1000 }, apt: { type: "string", maxLength: 100 } }) },
|
|
305
|
+
geocode: { type: "object", schema: createSchema({ latitude: { type: "number", required: true, min: -90, max: 90 }, longitude: { type: "number", required: true, min: -180, max: 180 } }) }
|
|
306
|
+
}) }
|
|
307
|
+
});
|
|
308
|
+
return {
|
|
309
|
+
scopes: [],
|
|
310
|
+
request(input, settings) {
|
|
311
|
+
const { locationId, ...location } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
|
|
312
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
313
|
+
if (!Object.keys(location.address).length) throw new ConnectorError("connector_input_invalid", "Provide the location address.", { statusCode: 422 });
|
|
314
|
+
if (location.businessSchedule && (["periods", "specialHourPeriod"].some(key => location.businessSchedule[key] !== undefined &&
|
|
315
|
+
(!Array.isArray(location.businessSchedule[key]) || location.businessSchedule[key].length > 100)) ||
|
|
316
|
+
Object.keys(location.businessSchedule).some(key => !["periods", "specialHourPeriod"].includes(key)))) {
|
|
317
|
+
throw new ConnectorError("connector_input_invalid", "Use native businessSchedule periods and specialHourPeriod arrays with at most 100 entries each.", { statusCode: 422 });
|
|
318
|
+
}
|
|
319
|
+
return { method: action === "create" ? "POST" : "PUT", url: `https://www.wixapis.com/locations/v1/locations${locationId ? `/${locationId}` : ""}`,
|
|
320
|
+
headers: { "wix-site-id": settings.siteId }, body: { location: { ...(locationId ? { id: locationId } : {}), ...location } } };
|
|
321
|
+
},
|
|
322
|
+
validateResult: value => validLocation(value?.location) && typeof value.location.revision === "string" && /^\d{1,19}$/u.test(value.location.revision)
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
function locationActionOperation(action) {
|
|
326
|
+
const schema = createSchema({ locationId: contactIdField });
|
|
327
|
+
return {
|
|
328
|
+
scopes: [],
|
|
329
|
+
request(input, settings) {
|
|
330
|
+
const { locationId } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
|
|
331
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
332
|
+
return { method: "POST", url: `https://www.wixapis.com/locations/v1/locations/${locationId}/${action}`,
|
|
333
|
+
headers: { "wix-site-id": settings.siteId }, body: {} };
|
|
334
|
+
},
|
|
335
|
+
validateResult: value => validLocation(value?.location) && (action === "archive" ? value.location.archived === true : value.location.default === true)
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
const locationQuerySchema = createSchema({
|
|
339
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 20 }, offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 },
|
|
340
|
+
archived: { type: "boolean", defaultTo: false }
|
|
341
|
+
});
|
|
342
|
+
const locationGetSchema = createSchema({ locationId: { type: "string", required: true, validator: value => guid(value) || "Use the returned location ID." } });
|
|
343
|
+
const validLocation = value => guid(value?.id) && typeof value.name === "string" &&
|
|
344
|
+
(value.timeZone === undefined || typeof value.timeZone === "string") && (value.archived === undefined || typeof value.archived === "boolean");
|
|
345
|
+
const productV1UpdateSchema = createSchema({
|
|
346
|
+
productId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix product ID." },
|
|
347
|
+
name: { type: "string", minLength: 1, maxLength: 80 }, visible: { type: "boolean" },
|
|
348
|
+
price: { type: "none", validator: value => typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 999999999.99 || "Use a numeric Wix base price from 0 to 999999999.99." }
|
|
349
|
+
});
|
|
350
|
+
const productV3UpdateSchema = createSchema({
|
|
351
|
+
productId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix product ID." },
|
|
352
|
+
revision: { type: "none", required: true, validator: value => typeof value === "string" && /^\d{1,19}$/u.test(value) && BigInt(value) <= 9223372036854775807n || "Use the current product revision string." },
|
|
353
|
+
name: { type: "string", minLength: 1, maxLength: 80 },
|
|
354
|
+
visible: { type: "boolean" }
|
|
355
|
+
});
|
|
356
|
+
const catalogVersionSchema = createSchema({});
|
|
357
|
+
const orderSearchSchema = createSchema({
|
|
358
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
|
|
359
|
+
cursor: { type: "string", validator: value => cursor(value) || "Use the returned order cursor." },
|
|
360
|
+
status: { type: "string", minLength: 1, maxLength: 100 },
|
|
361
|
+
contactId: { type: "string", validator: value => guid(value) || "Use the buyer's contact ID." }
|
|
362
|
+
});
|
|
363
|
+
const orderIdSchema = createSchema({ orderId: { type: "string", required: true, minLength: 1, maxLength: 100, validator: value => !/[\p{Cc}]/u.test(value) && ![".", ".."].includes(value) || "Use the returned order ID." } });
|
|
364
|
+
const cartCreateSchema = createSchema({
|
|
365
|
+
note: { type: "string", maxLength: 1000 },
|
|
366
|
+
catalogItems: { type: "array", required: true, validator: values => values.length > 0 && values.length <= 300 || "Select 1–300 catalogue items.",
|
|
367
|
+
items: { type: "object", schema: createSchema({
|
|
368
|
+
quantity: { type: "integer", required: true, min: 1, max: 100000 },
|
|
369
|
+
catalogReference: { type: "object", required: true, schema: createSchema({
|
|
370
|
+
appId: { type: "string", required: true, validator: value => guid(value) || "Use the catalogue app ID." },
|
|
371
|
+
catalogItemId: { type: "string", required: true, minLength: 1, maxLength: 36, validator: value => !/[\p{Cc}]/u.test(value) || "Use the selected catalogue item ID." },
|
|
372
|
+
options: nativeJsonObjectField
|
|
373
|
+
}) }
|
|
374
|
+
}) } }
|
|
375
|
+
});
|
|
376
|
+
const cartIdSchema = createSchema({ cartId: { type: "string", required: true, validator: value => guid(value) || "Use the cart ID returned by Wix." } });
|
|
377
|
+
const validOrder = value => typeof value?.id === "string" && value.id.length > 0 && value.id.length <= 100 &&
|
|
378
|
+
typeof value.status === "string" && value.status.length > 0 && (value.currency === undefined || typeof value.currency === "string") &&
|
|
379
|
+
(value.lineItems === undefined || Array.isArray(value.lineItems));
|
|
380
|
+
const formSummarySchema = createSchema({ formId: { type: "string", required: true, validator: value => guid(value) || "Use the service form ID." } });
|
|
381
|
+
const bookingCreateSchema = createSchema({
|
|
382
|
+
serviceId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix service ID." },
|
|
383
|
+
kind: { type: "string", required: true, enum: ["APPOINTMENT", "CLASS", "COURSE"] },
|
|
384
|
+
timeZone: { type: "string", required: true, maxLength: 150, validator: value => {
|
|
385
|
+
try { new Intl.DateTimeFormat("en", { timeZone: value }); return true; } catch { return "Use an IANA time zone."; }
|
|
386
|
+
} },
|
|
387
|
+
startDate: { type: "string", validator: value => localDate(value) || "Use the selected slot's local start date." },
|
|
388
|
+
endDate: { type: "string", validator: value => localDate(value) || "Use the selected slot's local end date." },
|
|
389
|
+
eventId: { type: "string", minLength: 36, maxLength: 250, validator: value => !/[\p{Cc}]/u.test(value) || "Use the returned event ID." },
|
|
390
|
+
scheduleId: { type: "string", validator: value => guid(value) || "Use the course schedule ID." },
|
|
391
|
+
resourceId: { type: "string", validator: value => guid(value) || "Use the selected resource ID." },
|
|
392
|
+
location: { type: "object", schema: createSchema({
|
|
393
|
+
id: { type: "string", validator: value => guid(value) || "Use the business location ID." },
|
|
394
|
+
locationType: { type: "string", required: true, enum: ["OWNER_BUSINESS", "OWNER_CUSTOM", "CUSTOM"] },
|
|
395
|
+
name: { type: "string", maxLength: 1000 }, formattedAddress: { type: "string", maxLength: 4000 }
|
|
396
|
+
}) },
|
|
397
|
+
totalParticipants: { type: "integer", min: 1, max: 1000 },
|
|
398
|
+
participantsChoices: { type: "object", schema: createSchema({
|
|
399
|
+
serviceChoices: { type: "array", required: true, validator: values => values.length > 0 && values.length <= 20 || "Use 1–20 participant groups.",
|
|
400
|
+
items: { type: "object", schema: createSchema({
|
|
401
|
+
numberOfParticipants: { type: "integer", required: true, min: 1, max: 1000 },
|
|
402
|
+
choices: { type: "array", required: true, validator: values => values.length > 0 && values.length <= 5 || "Use 1–5 service choices per group.",
|
|
403
|
+
items: { type: "object", schema: createSchema({
|
|
404
|
+
optionId: { type: "string", required: true, validator: value => guid(value) || "Use the option ID returned by Wix." },
|
|
405
|
+
custom: { type: "string", minLength: 1, maxLength: 1000 },
|
|
406
|
+
duration: { type: "object", schema: createSchema({ minutes: { type: "integer", required: true, min: 1, max: 44639 }, name: { type: "string", maxLength: 255 } }) }
|
|
407
|
+
}) } }
|
|
408
|
+
}) } }
|
|
409
|
+
}) },
|
|
410
|
+
bookedAddOns: { type: "array", validator: values => values.length > 0 && values.length <= 21 && new Set(values.map(value => value?.id)).size === values.length || "Select 1–21 distinct add-ons.",
|
|
411
|
+
items: { type: "object", schema: createSchema({
|
|
412
|
+
id: { type: "string", required: true, validator: value => guid(value) || "Use the add-on ID returned by Wix." },
|
|
413
|
+
groupId: { type: "string", validator: value => guid(value) || "Use the add-on group ID." },
|
|
414
|
+
quantity: { type: "integer", min: 1, max: 1000 }
|
|
415
|
+
}) } },
|
|
416
|
+
formSubmission: { type: "none", required: true, validator: value => {
|
|
417
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return "Provide the booking form's field-value object.";
|
|
418
|
+
try {
|
|
419
|
+
const json = serializeNativeJson(value);
|
|
420
|
+
return Buffer.byteLength(json) <= 100000 || "Keep booking form data within 100KB.";
|
|
421
|
+
} catch { return "Use serializable JSON booking form data."; }
|
|
422
|
+
} },
|
|
423
|
+
notifyParticipants: { type: "boolean", defaultTo: false }, sendSmsReminder: { type: "boolean", defaultTo: false }
|
|
424
|
+
});
|
|
425
|
+
const bookingQuerySchema = createSchema({
|
|
426
|
+
bookingId: { type: "string", validator: value => guid(value) || "Use the Wix booking ID." },
|
|
427
|
+
limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
|
|
428
|
+
offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 }
|
|
429
|
+
});
|
|
430
|
+
const bookingCancelSchema = createSchema({
|
|
431
|
+
bookingId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix booking ID." },
|
|
432
|
+
revision: { type: "none", required: true, validator: value => typeof value === "string" && /^\d{1,19}$/u.test(value) && BigInt(value) <= 9223372036854775807n || "Use the current booking revision string." },
|
|
433
|
+
notifyParticipants: { type: "boolean", defaultTo: false },
|
|
434
|
+
message: { type: "string", maxLength: 2000 },
|
|
435
|
+
waiveCharges: { type: "boolean", defaultTo: false }
|
|
436
|
+
});
|
|
437
|
+
const bookingRescheduleSchema = createSchema({
|
|
438
|
+
bookingId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix booking ID." },
|
|
439
|
+
revision: { type: "none", required: true, validator: value => typeof value === "string" && /^\d{1,19}$/u.test(value) && BigInt(value) <= 9223372036854775807n || "Use the current booking revision string." },
|
|
440
|
+
slot: { ...nativeJsonObjectField, required: true },
|
|
441
|
+
notifyParticipants: { type: "boolean", defaultTo: false }, message: { type: "string", maxLength: 2000 }
|
|
442
|
+
});
|
|
443
|
+
function bookingConfirmationOperation(action) {
|
|
444
|
+
const automatic = action === "confirmOrDecline";
|
|
445
|
+
const schema = createSchema({
|
|
446
|
+
bookingId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix booking ID." },
|
|
447
|
+
...(automatic ? {
|
|
448
|
+
paymentStatus: { type: "string", required: true, enum: ["UNDEFINED", "NOT_PAID", "PAID", "PARTIALLY_PAID", "REFUNDED", "EXEMPT"] }
|
|
449
|
+
} : {
|
|
450
|
+
revision: { type: "none", required: true, validator: value => typeof value === "string" && /^\d{1,19}$/u.test(value) && BigInt(value) <= 9223372036854775807n || "Use the current booking revision string." },
|
|
451
|
+
notifyParticipants: { type: "boolean", defaultTo: false }, message: { type: "string", maxLength: 2000 }
|
|
452
|
+
})
|
|
453
|
+
});
|
|
454
|
+
return {
|
|
455
|
+
scopes: [],
|
|
456
|
+
request(input, settings) {
|
|
457
|
+
const { bookingId, paymentStatus, revision, notifyParticipants, message } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
|
|
458
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
459
|
+
if (message !== undefined && !notifyParticipants) throw new ConnectorError("connector_input_invalid", "Enable participant notification before supplying a message.", { statusCode: 422 });
|
|
460
|
+
return { method: "POST", url: automatic ? `https://www.wixapis.com/bookings/v2/confirmation/${bookingId}:confirmOrDecline` :
|
|
461
|
+
`https://www.wixapis.com/_api/bookings-service/v2/bookings/${bookingId}/${action}`, headers: { "wix-site-id": settings.siteId },
|
|
462
|
+
body: automatic ? { paymentStatus } : { revision, participantNotification: { notifyParticipants, ...(message === undefined ? {} : { message }) } } };
|
|
463
|
+
},
|
|
464
|
+
validateResult: value => guid(value?.booking?.id) && typeof value.booking.revision === "string" && /^\d{1,19}$/u.test(value.booking.revision) &&
|
|
465
|
+
BigInt(value.booking.revision) <= 9223372036854775807n && (automatic ? ["CONFIRMED", "PENDING", "DECLINED"].includes(value.booking.status) : value.booking.status === (action === "confirm" ? "CONFIRMED" : "DECLINED"))
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function fulfillmentWriteOperation(update) {
|
|
469
|
+
const trackingSchema = createSchema({
|
|
470
|
+
trackingNumber: { type: "string", minLength: 1, maxLength: 100 },
|
|
471
|
+
shippingProvider: { type: "string", minLength: 1, maxLength: 100 },
|
|
472
|
+
trackingLink: { type: "string", minLength: 1, maxLength: 2048, validator: value => {
|
|
473
|
+
try { const url = new URL(value); return ["https:", "http:"].includes(url.protocol) && !url.username && !url.password || "Use a public tracking URL."; }
|
|
474
|
+
catch { return "Use a public tracking URL."; }
|
|
475
|
+
} }
|
|
476
|
+
});
|
|
477
|
+
const schema = createSchema({
|
|
478
|
+
orderId: { type: "string", required: true, minLength: 1, maxLength: 100, validator: value => !/[\p{Cc}]/u.test(value) && ![".", ".."].includes(value) || "Use the returned order ID." },
|
|
479
|
+
...(update ? { fulfillmentId: contactIdField } : {}),
|
|
480
|
+
lineItems: { type: "array", required: !update, validator: values => values.length > 0 && values.length <= 300 && new Set(values.map(item => item?.id)).size === values.length || "Select 1–300 distinct order line items.",
|
|
481
|
+
items: { type: "object", schema: createSchema({ id: contactIdField, quantity: { type: "integer", required: true, min: 1, max: 100000 } }) } },
|
|
482
|
+
trackingInfo: { type: "object", schema: trackingSchema },
|
|
483
|
+
status: { type: "string", enum: ["Pending", "Accepted", "Ready", "In_Delivery", "Fulfilled"] },
|
|
484
|
+
completed: { type: "boolean" }
|
|
485
|
+
});
|
|
486
|
+
return {
|
|
487
|
+
scopes: [],
|
|
488
|
+
request(input, settings) {
|
|
489
|
+
const { orderId, fulfillmentId, ...fulfillment } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
|
|
490
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
491
|
+
if (!Object.keys(fulfillment).length || fulfillment.trackingInfo && !Object.keys(fulfillment.trackingInfo).length) {
|
|
492
|
+
throw new ConnectorError("connector_input_invalid", "Supply the fulfillment properties to write.", { statusCode: 422 });
|
|
493
|
+
}
|
|
494
|
+
const tracking = fulfillment.trackingInfo;
|
|
495
|
+
if (!update && tracking && (!tracking.trackingNumber || !tracking.shippingProvider ||
|
|
496
|
+
!["fedex", "ups", "usps", "dhl", "canadaPost"].includes(tracking.shippingProvider) && !tracking.trackingLink)) {
|
|
497
|
+
throw new ConnectorError("connector_input_invalid", "Supply tracking number and provider; custom carriers also require a tracking URL.", { statusCode: 422 });
|
|
498
|
+
}
|
|
499
|
+
return { method: update ? "PATCH" : "POST", url: update
|
|
500
|
+
? `https://www.wixapis.com/ecom/v1/fulfillments/${fulfillmentId}/orders/${encodeURIComponent(orderId)}`
|
|
501
|
+
: `https://www.wixapis.com/ecom/v1/fulfillments/orders/${encodeURIComponent(orderId)}/create-fulfillment`,
|
|
502
|
+
headers: { "wix-site-id": settings.siteId }, body: { fulfillment } };
|
|
503
|
+
},
|
|
504
|
+
validateResult: value => typeof value?.orderWithFulfillments?.orderId === "string" && Array.isArray(value.orderWithFulfillments.fulfillments) &&
|
|
505
|
+
value.orderWithFulfillments.fulfillments.every(item => guid(item?.id) && Array.isArray(item.lineItems) && item.lineItems.length <= 300 &&
|
|
506
|
+
item.lineItems.every(line => guid(line?.id) && Number.isInteger(line.quantity) && line.quantity >= 1 && line.quantity <= 100000)) &&
|
|
507
|
+
(update || guid(value.fulfillmentId) && value.orderWithFulfillments.fulfillments.some(item => item.id === value.fulfillmentId))
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
const wixRuntime = {
|
|
511
|
+
...wixDefinition, apiOrigins: ["https://www.wixapis.com"],
|
|
512
|
+
apiKey: { headers: (key, settings) => ({ Authorization: key, "wix-account-id": settings.accountId }) },
|
|
513
|
+
checkOperation: "sites.list",
|
|
514
|
+
async exchange(address, options, { request }) {
|
|
515
|
+
const headers = { ...options.headers };
|
|
516
|
+
if (headers["wix-site-id"]) delete headers["wix-account-id"];
|
|
517
|
+
const result = await request(address, { ...options, headers });
|
|
518
|
+
const contactMatch = /\/contacts\/v4\/contacts\/([a-f0-9-]{36})$/iu.exec(new URL(address).pathname);
|
|
519
|
+
if (contactMatch && result?.contact?.id !== contactMatch[1]) {
|
|
520
|
+
throw new ConnectorError("connector_response_invalid", "Wix returned a different contact. Check the operation outcome before retrying.", { statusCode: 502 });
|
|
521
|
+
}
|
|
522
|
+
const serviceMatch = /\/_api\/bookings\/v2\/services\/([a-f0-9-]{36})(?:\/locations)?$/iu.exec(new URL(address).pathname);
|
|
523
|
+
if (serviceMatch && result?.service?.id !== serviceMatch[1]) {
|
|
524
|
+
throw new ConnectorError("connector_response_invalid", "Wix returned a different booking service. Check the outcome before retrying.", { statusCode: 502 });
|
|
525
|
+
}
|
|
526
|
+
const bookingMatch = /\/_api\/bookings-service\/v2\/bookings\/([a-f0-9-]{36})\/(?:cancel|confirm|decline|reschedule)$/iu.exec(new URL(address).pathname) ??
|
|
527
|
+
/^\/bookings\/v2\/confirmation\/([a-f0-9-]{36}):confirmOrDecline$/iu.exec(new URL(address).pathname);
|
|
528
|
+
const variantsMatch = /^\/bookings\/v1\/serviceOptionsAndVariants\/service_id\/([a-f0-9-]{36})$/iu.exec(new URL(address).pathname);
|
|
529
|
+
if (variantsMatch && result?.serviceVariants?.serviceId !== variantsMatch[1]) {
|
|
530
|
+
throw new ConnectorError("connector_response_invalid", "Wix returned variants for a different service.", { statusCode: 502 });
|
|
531
|
+
}
|
|
532
|
+
if (bookingMatch && result?.booking?.id !== bookingMatch[1]) {
|
|
533
|
+
throw new ConnectorError("connector_response_invalid", "Wix returned a different booking. Check the operation outcome before retrying.", { statusCode: 502 });
|
|
534
|
+
}
|
|
535
|
+
if (new URL(address).pathname === "/_api/bookings-reader/v2/extended-bookings/query") {
|
|
536
|
+
const id = options.body?.query?.filter?.id;
|
|
537
|
+
if (id && Array.isArray(result?.extendedBookings) && (result.extendedBookings.length > 1 || result.extendedBookings.some(value => value?.booking?.id !== id))) {
|
|
538
|
+
throw new ConnectorError("connector_response_invalid", "Wix returned an unexpected booking for the requested ID.", { statusCode: 502 });
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (new URL(address).pathname.startsWith("/_api/service-availability/v2/time-slots")) {
|
|
542
|
+
const requested = options.body;
|
|
543
|
+
const slots = result?.timeSlots ?? (result?.timeSlot ? [result.timeSlot] : []);
|
|
544
|
+
if (Array.isArray(slots) && (requested?.timeZone && result?.timeZone !== requested.timeZone || slots.some(slot =>
|
|
545
|
+
requested?.serviceId && slot?.serviceId !== requested.serviceId || requested?.localStartDate && (slot?.localStartDate !== requested.localStartDate || slot?.localEndDate !== requested.localEndDate)))) {
|
|
546
|
+
throw new ConnectorError("connector_response_invalid", "Wix returned availability for a different service, time or time zone.", { statusCode: 502 });
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
const formMatch = /\/form-schema-service\/v4\/forms\/([a-f0-9-]{36})\/summary$/iu.exec(new URL(address).pathname);
|
|
550
|
+
if (formMatch && result?.formSummary?.id !== formMatch[1]) {
|
|
551
|
+
throw new ConnectorError("connector_response_invalid", "Wix returned a different form summary.", { statusCode: 502 });
|
|
552
|
+
}
|
|
553
|
+
const eventMatch = /\/_api\/service-availability\/v2\/time-slots\/event\/(.+)$/u.exec(new URL(address).pathname);
|
|
554
|
+
if (eventMatch && (result?.timeSlot?.eventInfo?.eventId !== decodeURIComponent(eventMatch[1]) || result?.timeZone !== new URL(address).searchParams.get("timeZone"))) {
|
|
555
|
+
throw new ConnectorError("connector_response_invalid", "Wix returned a different event slot or time zone.", { statusCode: 502 });
|
|
556
|
+
}
|
|
557
|
+
if (new URL(address).pathname === "/_api/service-availability/v2/time-slots/event" && options.body?.serviceIds && Array.isArray(result?.timeSlots) &&
|
|
558
|
+
result.timeSlots.some(slot => !options.body.serviceIds.includes(slot?.serviceId))) {
|
|
559
|
+
throw new ConnectorError("connector_response_invalid", "Wix returned a class slot for a different service.", { statusCode: 502 });
|
|
560
|
+
}
|
|
561
|
+
const orderMatch = /\/ecom\/v1\/orders\/([^/]+)$/u.exec(new URL(address).pathname);
|
|
562
|
+
if (options.method === "GET" && orderMatch && result?.order?.id !== decodeURIComponent(orderMatch[1])) {
|
|
563
|
+
throw new ConnectorError("connector_response_invalid", "Wix returned a different order.", { statusCode: 502 });
|
|
564
|
+
}
|
|
565
|
+
const fulfillmentOrderMatch = /\/ecom\/v1\/fulfillments(?:\/[a-f0-9-]{36})?\/orders\/([^/]+)(?:\/create-fulfillment)?$/iu.exec(new URL(address).pathname);
|
|
566
|
+
if (fulfillmentOrderMatch && result?.orderWithFulfillments?.orderId !== decodeURIComponent(fulfillmentOrderMatch[1])) {
|
|
567
|
+
throw new ConnectorError("connector_response_invalid", "Wix returned fulfillments for a different order.", { statusCode: 502 });
|
|
568
|
+
}
|
|
569
|
+
const updatedFulfillmentMatch = /^\/ecom\/v1\/fulfillments\/([a-f0-9-]{36})\/orders\//iu.exec(new URL(address).pathname);
|
|
570
|
+
if (options.method === "PATCH" && updatedFulfillmentMatch &&
|
|
571
|
+
(!Array.isArray(result?.orderWithFulfillments?.fulfillments) || !result.orderWithFulfillments.fulfillments.some(item => item?.id === updatedFulfillmentMatch[1]))) {
|
|
572
|
+
throw new ConnectorError("connector_response_invalid", "Wix did not return the updated fulfillment. Inspect the order before retrying.", { statusCode: 502 });
|
|
573
|
+
}
|
|
574
|
+
const productMatch = /\/(?:stores-reader\/v1|stores\/v1|stores\/v3)\/products\/([a-f0-9-]{36})$/iu.exec(new URL(address).pathname);
|
|
575
|
+
if (productMatch && result?.product?.id !== productMatch[1]) {
|
|
576
|
+
throw new ConnectorError("connector_response_invalid", "Wix returned a different product.", { statusCode: 502 });
|
|
577
|
+
}
|
|
578
|
+
const locationMatch = /\/locations\/v1\/locations\/([a-f0-9-]{36})(?:\/(?:archive|set-default))?$/iu.exec(new URL(address).pathname);
|
|
579
|
+
if (locationMatch && result?.location?.id !== locationMatch[1]) throw new ConnectorError("connector_response_invalid", "Wix returned a different business location.", { statusCode: 502 });
|
|
580
|
+
const collectionMatch = /^\/wix-data\/v2\/collections\/(.+)$/u.exec(new URL(address).pathname);
|
|
581
|
+
if (collectionMatch && result?.collection?.id !== decodeURIComponent(collectionMatch[1])) throw new ConnectorError("connector_response_invalid", "Wix returned a different CMS collection.", { statusCode: 502 });
|
|
582
|
+
if (new URL(address).pathname === "/wix-data/v2/items/query" && Array.isArray(result?.dataItems) &&
|
|
583
|
+
result.dataItems.some(item => item?.dataCollectionId !== options.body.dataCollectionId)) throw new ConnectorError("connector_response_invalid", "Wix returned items from a different CMS collection.", { statusCode: 502 });
|
|
584
|
+
const itemMatch = /^\/wix-data\/v2\/items\/([^/]+)$/u.exec(new URL(address).pathname);
|
|
585
|
+
if (itemMatch && ["GET", "PATCH"].includes(options.method) &&
|
|
586
|
+
(result?.dataItem?.id !== decodeURIComponent(itemMatch[1]) || result?.dataItem?.dataCollectionId !==
|
|
587
|
+
(options.method === "GET" ? new URL(address).searchParams.get("dataCollectionId") : options.body.dataCollectionId))) {
|
|
588
|
+
throw new ConnectorError("connector_response_invalid", "Wix returned a different CMS item or collection. Check write outcomes before retrying.", { statusCode: 502 });
|
|
589
|
+
}
|
|
590
|
+
return result;
|
|
591
|
+
},
|
|
592
|
+
operations: {
|
|
593
|
+
"carts.create": {
|
|
594
|
+
scopes: [],
|
|
595
|
+
request(input, settings) {
|
|
596
|
+
const { note, catalogItems } = validateSchemaPayload({ schema: cartCreateSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
597
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
598
|
+
return { method: "POST", url: "https://www.wixapis.com/ecom/v2/carts", headers: { "wix-site-id": settings.siteId },
|
|
599
|
+
body: { cart: { source: { channelType: "WEB" }, ...(note === undefined ? {} : { note }) }, catalogItems } };
|
|
600
|
+
},
|
|
601
|
+
validateResult: value => guid(value?.cart?.id) && Array.isArray(value.cart.lineItems) && value.cart.lineItems.length <= 300 &&
|
|
602
|
+
value.cart.lineItems.every(item => guid(item?.id))
|
|
603
|
+
},
|
|
604
|
+
"carts.getCheckoutUrl": {
|
|
605
|
+
scopes: [],
|
|
606
|
+
request(input, settings) {
|
|
607
|
+
const { cartId } = validateSchemaPayload({ schema: cartIdSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
608
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
609
|
+
return { method: "POST", url: `https://www.wixapis.com/ecom/v2/carts/${cartId}/get-checkout-url`, headers: { "wix-site-id": settings.siteId }, body: {} };
|
|
610
|
+
},
|
|
611
|
+
validateResult(value) {
|
|
612
|
+
if (typeof value?.checkoutUrl !== "string" || value.checkoutUrl.length > 8000 || /[\p{Cc}]/u.test(value.checkoutUrl)) return false;
|
|
613
|
+
try { const url = new URL(value.checkoutUrl); return url.protocol === "https:" && !url.username && !url.password; } catch { return false; }
|
|
614
|
+
}
|
|
615
|
+
},
|
|
616
|
+
"bookings.reschedule": {
|
|
617
|
+
scopes: [],
|
|
618
|
+
request(input, settings) {
|
|
619
|
+
const { bookingId, revision, slot, notifyParticipants, message } = validateSchemaPayload({ schema: bookingRescheduleSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
620
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
621
|
+
if (message !== undefined && !notifyParticipants) throw new ConnectorError("connector_input_invalid", "Enable participant notification before supplying a message.", { statusCode: 422 });
|
|
622
|
+
if (slot.eventId !== undefined) {
|
|
623
|
+
if (Object.keys(slot).length !== 1 || typeof slot.eventId !== "string" || slot.eventId.length < 36 || slot.eventId.length > 250 || /[\p{Cc}]/u.test(slot.eventId)) {
|
|
624
|
+
throw new ConnectorError("connector_input_invalid", "For a class, supply only the selected eventId in slot.", { statusCode: 422 });
|
|
625
|
+
}
|
|
626
|
+
} else {
|
|
627
|
+
let validTimeZone = false;
|
|
628
|
+
try { validTimeZone = typeof slot.timezone === "string" && !!new Intl.DateTimeFormat("en", { timeZone: slot.timezone }); } catch {}
|
|
629
|
+
if (!guid(slot.serviceId) || !guid(slot.scheduleId) || !localDate(slot.startDate) || !localDate(slot.endDate) || slot.startDate >= slot.endDate || !validTimeZone) {
|
|
630
|
+
throw new ConnectorError("connector_input_invalid", "For an appointment, supply its service/schedule IDs, increasing local slot dates and IANA timezone. Courses cannot be rescheduled.", { statusCode: 422 });
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
return { method: "POST", url: `https://www.wixapis.com/_api/bookings-service/v2/bookings/${bookingId}/reschedule`, headers: { "wix-site-id": settings.siteId },
|
|
634
|
+
body: { revision, slot, participantNotification: { notifyParticipants, ...(message === undefined ? {} : { message }) } } };
|
|
635
|
+
},
|
|
636
|
+
validateResult: value => guid(value?.booking?.id) && typeof value.booking.revision === "string" && /^\d{1,19}$/u.test(value.booking.revision) &&
|
|
637
|
+
BigInt(value.booking.revision) <= 9223372036854775807n && typeof value.booking.status === "string" && value.booking.status.length > 0
|
|
638
|
+
},
|
|
639
|
+
"bookings.confirm": bookingConfirmationOperation("confirm"),
|
|
640
|
+
"bookings.decline": bookingConfirmationOperation("decline"),
|
|
641
|
+
"bookings.confirmOrDecline": bookingConfirmationOperation("confirmOrDecline"),
|
|
642
|
+
"bookingServices.listAddOnGroups": {
|
|
643
|
+
scopes: [],
|
|
644
|
+
request(input, settings) {
|
|
645
|
+
const value = validateSchemaPayload({ schema: serviceIdSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
646
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
647
|
+
return { method: "POST", url: "https://www.wixapis.com/_api/bookings/v2/services/add-on-groups/list-add-on-groups-by-service-id",
|
|
648
|
+
headers: { "wix-site-id": settings.siteId }, body: value };
|
|
649
|
+
},
|
|
650
|
+
validateResult: value => Array.isArray(value?.addOnGroupsDetails) && value.addOnGroupsDetails.length <= 3 &&
|
|
651
|
+
value.addOnGroupsDetails.every(group => guid(group?.groupId) && typeof group.groupName === "string" &&
|
|
652
|
+
Number.isInteger(group.maxNumberOfAddOns) && group.maxNumberOfAddOns >= 0 &&
|
|
653
|
+
Array.isArray(group.addOns) && group.addOns.length <= 7 && group.addOns.every(addOn => guid(addOn?.addOnId) && typeof addOn.name === "string"))
|
|
654
|
+
},
|
|
655
|
+
"bookingServices.getVariants": {
|
|
656
|
+
scopes: [],
|
|
657
|
+
request(input, settings) {
|
|
658
|
+
const { serviceId } = validateSchemaPayload({ schema: serviceIdSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
659
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
660
|
+
return { method: "GET", url: `https://www.wixapis.com/bookings/v1/serviceOptionsAndVariants/service_id/${serviceId}`, headers: { "wix-site-id": settings.siteId } };
|
|
661
|
+
},
|
|
662
|
+
validateResult(value) {
|
|
663
|
+
const variants = value?.serviceVariants;
|
|
664
|
+
return guid(variants?.id) && guid(variants.serviceId) &&
|
|
665
|
+
Array.isArray(variants.options?.values) && variants.options.values.every(option => guid(option?.id) && typeof option.type === "string") &&
|
|
666
|
+
Array.isArray(variants.variants?.values) && variants.variants.values.every(variant =>
|
|
667
|
+
Array.isArray(variant?.choices) && variant.choices.every(choice => guid(choice?.optionId)));
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
"bookingCategories.list": bookingDirectoryOperation("categories"),
|
|
671
|
+
"bookingStaff.list": bookingDirectoryOperation("staff"),
|
|
672
|
+
"bookingServices.setLocations": {
|
|
673
|
+
scopes: [],
|
|
674
|
+
request(input, settings) {
|
|
675
|
+
const { serviceId, locations, removedLocationAction: action, moveToLocation, notifyParticipants, notificationMessage } = validateSchemaPayload({ schema: setServiceLocationsSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
676
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
677
|
+
if ((action === "MOVE_TO_LOCATION") !== (moveToLocation !== undefined)) throw new ConnectorError("connector_input_invalid", "Provide a destination only when moving future sessions.", { statusCode: 422 });
|
|
678
|
+
if (notificationMessage !== undefined && !notifyParticipants) throw new ConnectorError("connector_input_invalid", "Enable participant notification before supplying a message.", { statusCode: 422 });
|
|
679
|
+
for (const location of [...locations, ...(moveToLocation ? [moveToLocation] : [])]) {
|
|
680
|
+
if (location.type === "BUSINESS" && (!location.business || location.custom) ||
|
|
681
|
+
location.type === "CUSTOM" && (!location.custom || location.business || !Object.keys(location.custom.address).length) ||
|
|
682
|
+
location.type === "CUSTOMER" && (location.business || location.custom)) {
|
|
683
|
+
throw new ConnectorError("connector_input_invalid", "Supply the details matching each service location type.", { statusCode: 422 });
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return { method: "POST", url: `https://www.wixapis.com/_api/bookings/v2/services/${serviceId}/locations`, headers: { "wix-site-id": settings.siteId }, body: {
|
|
687
|
+
locations, removedLocationSessionsAction: { action, ...(moveToLocation ? { moveToLocationOptions: { newLocation: moveToLocation } } : {}) },
|
|
688
|
+
participantNotification: { notifyParticipants, ...(notificationMessage !== undefined ? { message: notificationMessage } : {}) }
|
|
689
|
+
} };
|
|
690
|
+
},
|
|
691
|
+
validateResult: value => validService(value?.service)
|
|
692
|
+
},
|
|
693
|
+
"bookingServices.create": {
|
|
694
|
+
scopes: [],
|
|
695
|
+
request(input, settings) {
|
|
696
|
+
const { service } = validateSchemaPayload({ schema: serviceCreateSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
697
|
+
const invalid = message => { throw new ConnectorError("connector_input_invalid", message, { statusCode: 422 }); };
|
|
698
|
+
if (!guid(settings.siteId)) invalid("Configure the Wix Site ID first.");
|
|
699
|
+
if (!["APPOINTMENT", "CLASS", "COURSE"].includes(service.type) || typeof service.name !== "string" || !service.name.trim() || service.name.length > 400) invalid("Provide a service type and name of up to 400 characters.");
|
|
700
|
+
if (["id", "revision", "createdByAppId", "createdDate", "updatedDate", "addOnGroups"].some(key => Object.hasOwn(service, key))) invalid("Do not send generated service identity fields or existing add-on groups when creating a service.");
|
|
701
|
+
if (!Number.isInteger(service.defaultCapacity) || service.defaultCapacity < 1 || service.defaultCapacity > 1000 || service.type === "APPOINTMENT" && service.defaultCapacity !== 1) invalid("Set appointment capacity to 1, or class/course capacity from 1 to 1000.");
|
|
702
|
+
const online = service.onlineBooking;
|
|
703
|
+
if (!online || typeof online.enabled !== "boolean" || ["requireManualApproval", "allowMultipleRequests"].some(key => online[key] !== undefined && typeof online[key] !== "boolean")) invalid("Provide explicit online-booking settings.");
|
|
704
|
+
const payment = service.payment;
|
|
705
|
+
if (!payment || !["FIXED", "VARIED", "CUSTOM", "NO_FEE"].includes(payment.rateType)) invalid("Use FIXED, VARIED, CUSTOM or NO_FEE service payment settings.");
|
|
706
|
+
const options = payment.options;
|
|
707
|
+
if (!options || typeof options !== "object" || ["online", "inPerson", "deposit", "pricingPlan"].some(key => options[key] !== undefined && typeof options[key] !== "boolean") || !(options.online === true || options.inPerson === true)) invalid("Select in-person or online payment, including for a free service.");
|
|
708
|
+
if (online.requireManualApproval && options.pricingPlan || !online.enabled && options.online) invalid("Manual approval cannot use pricing plans; online payment requires online booking.");
|
|
709
|
+
const priced = ["FIXED", "VARIED"].includes(payment.rateType);
|
|
710
|
+
if ((options.online || options.deposit) && !priced) invalid("Online and deposit payments require fixed or varied pricing.");
|
|
711
|
+
const rate = payment.rateType === "FIXED" ? payment.fixed : payment.varied;
|
|
712
|
+
const money = value => value && typeof value.value === "string" && value.value.length <= 30 && /^\d+(?:\.\d{1,2})?$/u.test(value.value) && /[1-9]/u.test(value.value) && /^[A-Z]{3}$/u.test(value.currency);
|
|
713
|
+
if (priced && !money(payment.rateType === "FIXED" ? rate?.price : rate?.defaultPrice)) invalid("Provide a positive decimal price string and three-letter currency code.");
|
|
714
|
+
if (payment.rateType === "CUSTOM" && (typeof payment.custom?.description !== "string" || !payment.custom.description.length || payment.custom.description.length > 50)) invalid("Describe the custom payment in up to 50 characters.");
|
|
715
|
+
if (options.deposit && (!options.online || options.inPerson || !money(rate?.deposit))) invalid("A deposit requires its amount, online payment and no in-person payment.");
|
|
716
|
+
const durations = service.schedule?.availabilityConstraints?.sessionDurations;
|
|
717
|
+
if (service.type === "APPOINTMENT") {
|
|
718
|
+
if (!Array.isArray(service.staffMemberIds) || !service.staffMemberIds.length || !service.staffMemberIds.every(guid)) invalid("Select at least one staff resource ID for the appointment service.");
|
|
719
|
+
if (!Array.isArray(durations) || !durations.length || !durations.every(value => Number.isInteger(value) && value > 0)) invalid("Provide appointment session durations in minutes.");
|
|
720
|
+
} else if (durations !== undefined) invalid("Class/course session durations belong to their sessions, not service availability constraints.");
|
|
721
|
+
return { method: "POST", url: "https://www.wixapis.com/_api/bookings/v2/services", headers: { "wix-site-id": settings.siteId }, body: { service } };
|
|
722
|
+
},
|
|
723
|
+
validateResult: value => validService(value?.service)
|
|
724
|
+
},
|
|
725
|
+
"cmsItems.get": {
|
|
726
|
+
scopes: [],
|
|
727
|
+
request(input, settings) {
|
|
728
|
+
const { collectionId: dataCollectionId, itemId, consistentRead } = validateSchemaPayload({ schema: cmsItemGetSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
729
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
730
|
+
const url = new URL(`https://www.wixapis.com/wix-data/v2/items/${encodeURIComponent(itemId)}`);
|
|
731
|
+
url.searchParams.set("dataCollectionId", dataCollectionId); url.searchParams.set("consistentRead", String(consistentRead));
|
|
732
|
+
return { method: "GET", url: url.href, headers: { "wix-site-id": settings.siteId } };
|
|
733
|
+
},
|
|
734
|
+
validateResult: value => value?.dataItem?.data && typeof value.dataItem.data === "object" && !Array.isArray(value.dataItem.data)
|
|
735
|
+
},
|
|
736
|
+
"cmsItems.patch": {
|
|
737
|
+
scopes: [],
|
|
738
|
+
request(input, settings) {
|
|
739
|
+
const { collectionId: dataCollectionId, itemId, fieldModifications, conditionFilter } = validateSchemaPayload({ schema: cmsItemPatchSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
740
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
741
|
+
return { method: "PATCH", url: `https://www.wixapis.com/wix-data/v2/items/${encodeURIComponent(itemId)}`, headers: { "wix-site-id": settings.siteId },
|
|
742
|
+
body: { dataCollectionId, patch: { dataItemId: itemId, fieldModifications }, ...(conditionFilter !== undefined ? { condition: { filter: conditionFilter } } : {}) } };
|
|
743
|
+
},
|
|
744
|
+
validateResult: value => value?.dataItem?.data && typeof value.dataItem.data === "object" && !Array.isArray(value.dataItem.data)
|
|
745
|
+
},
|
|
746
|
+
"cmsItems.list": {
|
|
747
|
+
scopes: [],
|
|
748
|
+
request(input, settings) {
|
|
749
|
+
const { collectionId: dataCollectionId, consistentRead, filter, sort, fields, returnTotalCount, includeReferences, ...paging } = validateSchemaPayload({ schema: cmsItemQuerySchema, mode: "replace" }, input, { statusCode: 422 });
|
|
750
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
751
|
+
return { method: "POST", url: "https://www.wixapis.com/wix-data/v2/items/query", headers: { "wix-site-id": settings.siteId },
|
|
752
|
+
body: { dataCollectionId, consistentRead,
|
|
753
|
+
...(returnTotalCount !== undefined ? { returnTotalCount } : {}), ...(includeReferences !== undefined ? { includeReferences } : {}),
|
|
754
|
+
query: { paging, ...(filter !== undefined ? { filter } : {}), ...(sort !== undefined ? { sort } : {}), ...(fields !== undefined ? { fields } : {}) } } };
|
|
755
|
+
},
|
|
756
|
+
validateResult: value => Array.isArray(value?.dataItems) && value.dataItems.length <= 100 && value.dataItems.every(item =>
|
|
757
|
+
typeof item?.id === "string" && item.id.length > 0 && collectionId(item.dataCollectionId) && item.data && typeof item.data === "object" && !Array.isArray(item.data)) &&
|
|
758
|
+
(value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata))
|
|
759
|
+
},
|
|
760
|
+
"cmsCollections.list": {
|
|
761
|
+
scopes: [],
|
|
762
|
+
request(input, settings) {
|
|
763
|
+
const value = validateSchemaPayload({ schema: collectionListSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
764
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
765
|
+
const url = new URL("https://www.wixapis.com/wix-data/v2/collections");
|
|
766
|
+
url.searchParams.set("paging.limit", String(value.limit)); url.searchParams.set("paging.offset", String(value.offset));
|
|
767
|
+
url.searchParams.set("consistentRead", String(value.consistentRead));
|
|
768
|
+
return { method: "GET", url: url.href, headers: { "wix-site-id": settings.siteId } };
|
|
769
|
+
},
|
|
770
|
+
validateResult: value => Array.isArray(value?.collections) && value.collections.length <= 100 && value.collections.every(validCollection) &&
|
|
771
|
+
(value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata))
|
|
772
|
+
},
|
|
773
|
+
"cmsCollections.get": {
|
|
774
|
+
scopes: [],
|
|
775
|
+
request(input, settings) {
|
|
776
|
+
const value = validateSchemaPayload({ schema: collectionGetSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
777
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
778
|
+
const id = value.collectionId.split("/").map(encodeURIComponent).join("/");
|
|
779
|
+
return { method: "GET", url: `https://www.wixapis.com/wix-data/v2/collections/${id}?consistentRead=${value.consistentRead}`, headers: { "wix-site-id": settings.siteId } };
|
|
780
|
+
},
|
|
781
|
+
validateResult: value => validCollection(value?.collection)
|
|
782
|
+
},
|
|
783
|
+
"locations.archive": locationActionOperation("archive"),
|
|
784
|
+
"locations.setDefault": locationActionOperation("set-default"),
|
|
785
|
+
"locations.create": locationWriteOperation("create"),
|
|
786
|
+
"locations.update": locationWriteOperation("update"),
|
|
787
|
+
"locations.list": {
|
|
788
|
+
scopes: [],
|
|
789
|
+
request(input, settings) {
|
|
790
|
+
const { archived, ...paging } = validateSchemaPayload({ schema: locationQuerySchema, mode: "replace" }, input, { statusCode: 422 });
|
|
791
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
792
|
+
return { method: "POST", url: "https://www.wixapis.com/locations/v1/locations/query", headers: { "wix-site-id": settings.siteId },
|
|
793
|
+
body: { query: { filter: { archived }, paging }, filterAuthorizedLocationEntities: true } };
|
|
794
|
+
},
|
|
795
|
+
validateResult: value => Array.isArray(value?.locations) && value.locations.length <= 100 && value.locations.every(validLocation) &&
|
|
796
|
+
(value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata) &&
|
|
797
|
+
["count", "offset", "total"].every(key => value.pagingMetadata[key] === undefined || Number.isSafeInteger(value.pagingMetadata[key]) && value.pagingMetadata[key] >= 0))
|
|
798
|
+
},
|
|
799
|
+
"locations.get": {
|
|
800
|
+
scopes: [],
|
|
801
|
+
request(input, settings) {
|
|
802
|
+
const { locationId } = validateSchemaPayload({ schema: locationGetSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
803
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
804
|
+
return { method: "GET", url: `https://www.wixapis.com/locations/v1/locations/${locationId}`, headers: { "wix-site-id": settings.siteId } };
|
|
805
|
+
},
|
|
806
|
+
validateResult: value => validLocation(value?.location)
|
|
807
|
+
},
|
|
808
|
+
"productsV1.list": productReadOperation(1, "list"),
|
|
809
|
+
"productsV1.get": productReadOperation(1, "get"),
|
|
810
|
+
"productsV1.update": {
|
|
811
|
+
scopes: [],
|
|
812
|
+
request(input, settings) {
|
|
813
|
+
const { productId, price, ...changes } = validateSchemaPayload({ schema: productV1UpdateSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
814
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
815
|
+
if (!Object.keys(changes).length && price === undefined) throw new ConnectorError("connector_input_invalid", "Provide a product name, visibility or base-price change.", { statusCode: 422 });
|
|
816
|
+
return { method: "PATCH", url: `https://www.wixapis.com/stores/v1/products/${productId}`,
|
|
817
|
+
headers: { "wix-site-id": settings.siteId }, body: { product: { ...changes, ...(price === undefined ? {} : { priceData: { price } }) } } };
|
|
818
|
+
},
|
|
819
|
+
validateResult: value => guid(value?.product?.id) && typeof value.product.name === "string" && value.product.name.length > 0
|
|
820
|
+
},
|
|
821
|
+
"productsV3.list": productReadOperation(3, "list"),
|
|
822
|
+
"productsV3.get": productReadOperation(3, "get"),
|
|
823
|
+
"productsV3.update": {
|
|
824
|
+
scopes: [],
|
|
825
|
+
request(input, settings) {
|
|
826
|
+
const { productId, revision, ...changes } = validateSchemaPayload({ schema: productV3UpdateSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
827
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
828
|
+
if (!Object.keys(changes).length) throw new ConnectorError("connector_input_invalid", "Provide a product name or visibility change.", { statusCode: 422 });
|
|
829
|
+
return { method: "PATCH", url: `https://www.wixapis.com/stores/v3/products/${productId}`,
|
|
830
|
+
headers: { "wix-site-id": settings.siteId }, body: { product: { id: productId, revision, ...changes } } };
|
|
831
|
+
},
|
|
832
|
+
validateResult: value => guid(value?.product?.id) && typeof value.product.name === "string" && value.product.name.length > 0 &&
|
|
833
|
+
typeof value.product.revision === "string" && /^\d{1,19}$/u.test(value.product.revision) && BigInt(value.product.revision) <= 9223372036854775807n
|
|
834
|
+
},
|
|
835
|
+
"catalog.version": {
|
|
836
|
+
scopes: [],
|
|
837
|
+
request(input, settings) {
|
|
838
|
+
validateSchemaPayload({ schema: catalogVersionSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
839
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
840
|
+
return { method: "GET", url: "https://www.wixapis.com/stores/v3/provision/version", headers: { "wix-site-id": settings.siteId } };
|
|
841
|
+
}, validateResult: value => ["V1_CATALOG", "V3_CATALOG", "STORES_NOT_INSTALLED"].includes(value?.catalogVersion)
|
|
842
|
+
},
|
|
843
|
+
"orders.list": {
|
|
844
|
+
scopes: [],
|
|
845
|
+
request(input, settings) {
|
|
846
|
+
const { status, contactId, ...cursorPaging } = validateSchemaPayload({ schema: orderSearchSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
847
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
848
|
+
if (cursorPaging.cursor && (status || contactId)) throw new ConnectorError("connector_input_invalid", "Use only cursor and limit for the next order page.", { statusCode: 422 });
|
|
849
|
+
const filter = { ...(status ? { status } : {}), ...(contactId ? { "buyerInfo.contactId": contactId } : {}) };
|
|
850
|
+
return { method: "POST", url: "https://www.wixapis.com/ecom/v1/orders/search", headers: { "wix-site-id": settings.siteId },
|
|
851
|
+
body: { search: { cursorPaging, ...(Object.keys(filter).length ? { filter } : {}) } } };
|
|
852
|
+
},
|
|
853
|
+
validateResult: value => Array.isArray(value?.orders) && value.orders.length <= 100 && value.orders.every(validOrder) &&
|
|
854
|
+
(value.metadata === undefined || value.metadata && typeof value.metadata === "object" && !Array.isArray(value.metadata) &&
|
|
855
|
+
(value.metadata.count === undefined || value.metadata.count === value.orders.length) &&
|
|
856
|
+
(value.metadata.hasNext === undefined || typeof value.metadata.hasNext === "boolean") &&
|
|
857
|
+
(value.metadata.cursors === undefined || value.metadata.cursors && typeof value.metadata.cursors === "object" && !Array.isArray(value.metadata.cursors) &&
|
|
858
|
+
["next", "prev"].every(key => value.metadata.cursors[key] === undefined || cursor(value.metadata.cursors[key]))))
|
|
859
|
+
},
|
|
860
|
+
"orders.get": {
|
|
861
|
+
scopes: [],
|
|
862
|
+
request(input, settings) {
|
|
863
|
+
const { orderId } = validateSchemaPayload({ schema: orderIdSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
864
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
865
|
+
return { method: "GET", url: `https://www.wixapis.com/ecom/v1/orders/${encodeURIComponent(orderId)}`, headers: { "wix-site-id": settings.siteId } };
|
|
866
|
+
}, validateResult: value => validOrder(value?.order)
|
|
867
|
+
},
|
|
868
|
+
"orderFulfillments.create": fulfillmentWriteOperation(false),
|
|
869
|
+
"orderFulfillments.update": fulfillmentWriteOperation(true),
|
|
870
|
+
"orderFulfillments.list": {
|
|
871
|
+
scopes: [],
|
|
872
|
+
request(input, settings) {
|
|
873
|
+
const { orderId } = validateSchemaPayload({ schema: orderIdSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
874
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
875
|
+
return { method: "GET", url: `https://www.wixapis.com/ecom/v1/fulfillments/orders/${encodeURIComponent(orderId)}`, headers: { "wix-site-id": settings.siteId } };
|
|
876
|
+
},
|
|
877
|
+
validateResult: value => guid(value?.orderWithFulfillments?.orderId) && Array.isArray(value.orderWithFulfillments.fulfillments) &&
|
|
878
|
+
value.orderWithFulfillments.fulfillments.every(item => guid(item?.id) && Array.isArray(item.lineItems) && item.lineItems.length <= 300 &&
|
|
879
|
+
item.lineItems.every(line => guid(line?.id) && Number.isInteger(line.quantity) && line.quantity >= 1 && line.quantity <= 100000))
|
|
880
|
+
},
|
|
881
|
+
"classAvailability.list": availabilityOperation("list", true),
|
|
882
|
+
"classAvailability.get": {
|
|
883
|
+
scopes: [],
|
|
884
|
+
request(input, settings) {
|
|
885
|
+
const { eventId, timeZone } = validateSchemaPayload({ schema: eventSlotSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
886
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
887
|
+
const url = new URL(`https://www.wixapis.com/_api/service-availability/v2/time-slots/event/${encodeURIComponent(eventId)}`);
|
|
888
|
+
url.searchParams.set("timeZone", timeZone);
|
|
889
|
+
return { method: "GET", url: url.href, headers: { "wix-site-id": settings.siteId } };
|
|
890
|
+
}, validateResult: value => validSlot(value?.timeSlot) && typeof value.timeSlot.eventInfo?.eventId === "string" && typeof value.timeZone === "string"
|
|
891
|
+
},
|
|
892
|
+
"forms.summary": {
|
|
893
|
+
scopes: [],
|
|
894
|
+
request(input, settings) {
|
|
895
|
+
const { formId } = validateSchemaPayload({ schema: formSummarySchema, mode: "replace" }, input, { statusCode: 422 });
|
|
896
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
897
|
+
return { method: "GET", url: `https://www.wixapis.com/form-schema-service/v4/forms/${formId}/summary`, headers: { "wix-site-id": settings.siteId } };
|
|
898
|
+
},
|
|
899
|
+
validateResult: value => guid(value?.formSummary?.id) && Array.isArray(value.formSummary.fields) && value.formSummary.fields.length <= 500 &&
|
|
900
|
+
value.formSummary.fields.every(field => guid(field?.id) && typeof field.target === "string" && field.target.length > 0 && field.target.length <= 200 &&
|
|
901
|
+
typeof field.type === "string" && field.type.length > 0 && (field.label === undefined || typeof field.label === "string" && field.label.length <= 350) &&
|
|
902
|
+
(field.deleted === undefined || typeof field.deleted === "boolean") && (field.options === undefined || Array.isArray(field.options) && field.options.length <= 400))
|
|
903
|
+
},
|
|
904
|
+
"bookings.create": {
|
|
905
|
+
scopes: [],
|
|
906
|
+
request(input, settings) {
|
|
907
|
+
const value = validateSchemaPayload({ schema: bookingCreateSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
908
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
909
|
+
const { kind, serviceId, timeZone, startDate, endDate, eventId, scheduleId, resourceId, location, totalParticipants, participantsChoices, bookedAddOns, formSubmission, notifyParticipants, sendSmsReminder } = value;
|
|
910
|
+
if (participantsChoices) {
|
|
911
|
+
if (totalParticipants !== undefined) throw new ConnectorError("connector_input_invalid", "Use participant choices or a fixed participant total, not both.", { statusCode: 422 });
|
|
912
|
+
if (participantsChoices.serviceChoices.reduce((total, group) => total + group.numberOfParticipants, 0) > 1000 ||
|
|
913
|
+
participantsChoices.serviceChoices.some(group => new Set(group.choices.map(choice => choice.optionId)).size !== group.choices.length ||
|
|
914
|
+
group.choices.some(choice => (choice.custom !== undefined) === (choice.duration !== undefined)))) {
|
|
915
|
+
throw new ConnectorError("connector_input_invalid", "Use at most 1000 participants, distinct options per group and exactly one custom or duration value per choice.", { statusCode: 422 });
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
if (kind === "APPOINTMENT" ? !startDate || !endDate || startDate >= endDate || !resourceId || !location || eventId || scheduleId :
|
|
919
|
+
kind === "CLASS" ? !eventId || startDate || endDate || resourceId || location || scheduleId : !scheduleId || startDate || endDate || resourceId || location || eventId) {
|
|
920
|
+
throw new ConnectorError("connector_input_invalid", "Use appointment dates/resource/location, a class event ID, or a course schedule ID, matching the selected service type.", { statusCode: 422 });
|
|
921
|
+
}
|
|
922
|
+
if (location?.locationType === "OWNER_BUSINESS" && !location.id) throw new ConnectorError("connector_input_invalid", "Choose the business location ID.", { statusCode: 422 });
|
|
923
|
+
const bookedEntity = kind === "COURSE" ? { schedule: { scheduleId, serviceId, timezone: timeZone } } :
|
|
924
|
+
{ slot: { serviceId, timezone: timeZone, ...(kind === "CLASS" ? { eventId } : { startDate, endDate, resource: { id: resourceId }, location }) } };
|
|
925
|
+
return { method: "POST", url: "https://www.wixapis.com/_api/bookings-service/v2/bookings", headers: { "wix-site-id": settings.siteId },
|
|
926
|
+
body: { booking: { bookedEntity, ...(participantsChoices ? { participantsChoices } : { totalParticipants: totalParticipants ?? 1 }),
|
|
927
|
+
...(bookedAddOns ? { bookedAddOns } : {}) }, formSubmission, participantNotification: { notifyParticipants }, sendSmsReminder } };
|
|
928
|
+
},
|
|
929
|
+
validateResult: value => guid(value?.booking?.id) && typeof value.booking.revision === "string" && /^\d{1,19}$/u.test(value.booking.revision) &&
|
|
930
|
+
BigInt(value.booking.revision) <= 9223372036854775807n && typeof value.booking.status === "string" && value.booking.status.length > 0
|
|
931
|
+
},
|
|
932
|
+
"availability.list": availabilityOperation("list"),
|
|
933
|
+
"availability.get": availabilityOperation("get"),
|
|
934
|
+
"bookings.list": {
|
|
935
|
+
scopes: [],
|
|
936
|
+
request(input, settings) {
|
|
937
|
+
const { bookingId, ...paging } = validateSchemaPayload({ schema: bookingQuerySchema, mode: "replace" }, input, { statusCode: 422 });
|
|
938
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
939
|
+
return { method: "POST", url: "https://www.wixapis.com/_api/bookings-reader/v2/extended-bookings/query",
|
|
940
|
+
headers: { "wix-site-id": settings.siteId }, body: { query: { paging, ...(bookingId ? { filter: { id: bookingId } } : {}) }, withBookingAllowedActions: true } };
|
|
941
|
+
},
|
|
942
|
+
validateResult: value => Array.isArray(value?.extendedBookings) && value.extendedBookings.length <= 100 && value.extendedBookings.every(record =>
|
|
943
|
+
guid(record?.booking?.id) && typeof record.booking.revision === "string" && /^\d{1,19}$/u.test(record.booking.revision) && BigInt(record.booking.revision) <= 9223372036854775807n &&
|
|
944
|
+
typeof record.booking.status === "string" && record.booking.status.length > 0 &&
|
|
945
|
+
(record.allowedActions === undefined || record.allowedActions && typeof record.allowedActions === "object" && !Array.isArray(record.allowedActions) &&
|
|
946
|
+
["cancel", "reschedule"].every(key => record.allowedActions[key] === undefined || typeof record.allowedActions[key] === "boolean"))) &&
|
|
947
|
+
(value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata) &&
|
|
948
|
+
["count", "offset", "total"].every(key => value.pagingMetadata[key] === undefined || Number.isSafeInteger(value.pagingMetadata[key]) && value.pagingMetadata[key] >= 0) &&
|
|
949
|
+
(value.pagingMetadata.count === undefined || value.pagingMetadata.count === value.extendedBookings.length))
|
|
950
|
+
},
|
|
951
|
+
"bookings.cancel": {
|
|
952
|
+
scopes: [],
|
|
953
|
+
request(input, settings) {
|
|
954
|
+
const { bookingId, revision, notifyParticipants, message, waiveCharges } = validateSchemaPayload({ schema: bookingCancelSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
955
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
956
|
+
if (message && !notifyParticipants) throw new ConnectorError("connector_input_invalid", "Enable participant notification to send a cancellation message.", { statusCode: 422 });
|
|
957
|
+
return { method: "POST", url: `https://www.wixapis.com/_api/bookings-service/v2/bookings/${bookingId}/cancel`,
|
|
958
|
+
headers: { "wix-site-id": settings.siteId }, body: { revision, waiveCharges,
|
|
959
|
+
participantNotification: { notifyParticipants, ...(message === undefined ? {} : { message }) } } };
|
|
960
|
+
},
|
|
961
|
+
validateResult: value => guid(value?.booking?.id) && value.booking.status === "CANCELED" &&
|
|
962
|
+
typeof value.booking.revision === "string" && /^\d{1,19}$/u.test(value.booking.revision) && BigInt(value.booking.revision) <= 9223372036854775807n
|
|
963
|
+
},
|
|
964
|
+
"bookingServices.list": {
|
|
965
|
+
scopes: [],
|
|
966
|
+
request(input, settings) {
|
|
967
|
+
const paging = validateSchemaPayload({ schema: servicePagingSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
968
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
969
|
+
return { method: "POST", url: "https://www.wixapis.com/_api/bookings/v2/services/query", headers: { "wix-site-id": settings.siteId }, body: { query: { paging } } };
|
|
970
|
+
},
|
|
971
|
+
validateResult: value => Array.isArray(value?.services) && value.services.length <= 100 && value.services.every(validService) &&
|
|
972
|
+
(value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata) &&
|
|
973
|
+
["count", "offset", "total"].every(key => value.pagingMetadata[key] === undefined || Number.isSafeInteger(value.pagingMetadata[key]) && value.pagingMetadata[key] >= 0) &&
|
|
974
|
+
(value.pagingMetadata.count === undefined || value.pagingMetadata.count === value.services.length))
|
|
975
|
+
},
|
|
976
|
+
"bookingServices.get": {
|
|
977
|
+
scopes: [],
|
|
978
|
+
request(input, settings) {
|
|
979
|
+
const { serviceId } = validateSchemaPayload({ schema: serviceIdSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
980
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
981
|
+
return { method: "GET", url: `https://www.wixapis.com/_api/bookings/v2/services/${serviceId}`, headers: { "wix-site-id": settings.siteId } };
|
|
982
|
+
}, validateResult: value => validService(value?.service)
|
|
983
|
+
},
|
|
984
|
+
"bookingServices.update": {
|
|
985
|
+
scopes: [],
|
|
986
|
+
request(input, settings) {
|
|
987
|
+
const { serviceId, ...service } = validateSchemaPayload({ schema: serviceUpdateSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
988
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
|
|
989
|
+
if (Object.keys(service).length < 2) throw new ConnectorError("connector_input_invalid", "Choose a service field to update.", { statusCode: 422 });
|
|
990
|
+
if (["payment", "onlineBooking"].some(key => service[key] !== undefined && !Object.keys(service[key]).length) ||
|
|
991
|
+
service.schedule && !Object.keys(service.schedule.availabilityConstraints).length) {
|
|
992
|
+
throw new ConnectorError("connector_input_invalid", "Provide the selected service settings instead of an empty object.", { statusCode: 422 });
|
|
993
|
+
}
|
|
994
|
+
return { method: "PATCH", url: `https://www.wixapis.com/_api/bookings/v2/services/${serviceId}`, headers: { "wix-site-id": settings.siteId }, body: { service: { id: serviceId, ...service } } };
|
|
995
|
+
}, validateResult: value => validService(value?.service)
|
|
996
|
+
},
|
|
997
|
+
"contacts.get": contactOperation("get"),
|
|
998
|
+
"contacts.create": contactOperation("create"),
|
|
999
|
+
"contacts.update": contactOperation("update"),
|
|
1000
|
+
"contacts.list": {
|
|
1001
|
+
scopes: [],
|
|
1002
|
+
request(input, settings) {
|
|
1003
|
+
const { search, ...paging } = validateSchemaPayload({ schema: contactsSchema, mode: "replace" }, input, { statusCode: 422 });
|
|
1004
|
+
if (!guid(settings.siteId)) throw new ConnectorError("connector_input_invalid", "Configure the project's Wix Site ID before querying contacts.", { statusCode: 422 });
|
|
1005
|
+
return { method: "POST", url: "https://www.wixapis.com/contacts/v4/contacts/query",
|
|
1006
|
+
headers: { "wix-site-id": settings.siteId },
|
|
1007
|
+
body: { query: { paging }, ...(search === undefined ? {} : { search }) } };
|
|
1008
|
+
},
|
|
1009
|
+
validateResult: value => Array.isArray(value?.contacts) && value.contacts.length <= 100 &&
|
|
1010
|
+
value.contacts.every(contact => guid(contact?.id) && (contact.info === undefined || contact.info && typeof contact.info === "object" && !Array.isArray(contact.info))) &&
|
|
1011
|
+
(value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata) &&
|
|
1012
|
+
["count", "offset", "total"].every(key => value.pagingMetadata[key] === undefined || Number.isSafeInteger(value.pagingMetadata[key]) && value.pagingMetadata[key] >= 0) &&
|
|
1013
|
+
(value.pagingMetadata.count === undefined || value.pagingMetadata.count === value.contacts.length))
|
|
1014
|
+
},
|
|
1015
|
+
"sites.list": {
|
|
1016
|
+
scopes: [],
|
|
1017
|
+
request(input) {
|
|
1018
|
+
const paging = validateSchemaPayload({ schema: querySchema, mode: "replace" }, input, { statusCode: 422 });
|
|
1019
|
+
return { method: "POST", url: "https://www.wixapis.com/site-list/v2/sites/query", body: { query: { cursorPaging: paging } } };
|
|
1020
|
+
},
|
|
1021
|
+
validateResult: (value) => Array.isArray(value?.sites) && value.sites.length <= 100 && value.sites.every((site) => guid(site?.id) &&
|
|
1022
|
+
["name", "displayName", "viewUrl", "editUrl", "thumbnail"].every((key) => site[key] === undefined || typeof site[key] === "string") &&
|
|
1023
|
+
["published", "premium", "domainConnected"].every((key) => site[key] === undefined || typeof site[key] === "boolean")) &&
|
|
1024
|
+
(value.metadata === undefined || value.metadata && typeof value.metadata === "object" && !Array.isArray(value.metadata) &&
|
|
1025
|
+
(value.metadata.count === undefined || Number.isSafeInteger(value.metadata.count) && value.metadata.count === value.sites.length) &&
|
|
1026
|
+
(value.metadata.cursors === undefined || value.metadata.cursors && typeof value.metadata.cursors === "object" && !Array.isArray(value.metadata.cursors) &&
|
|
1027
|
+
["next", "prev"].every((key) => value.metadata.cursors[key] === undefined || cursor(value.metadata.cursors[key]))))
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
function wixSiteOperation(operation) {
|
|
1032
|
+
return {
|
|
1033
|
+
...operation,
|
|
1034
|
+
request(input, settings) {
|
|
1035
|
+
if (!input || !Object.hasOwn(input, "siteId")) return operation.request(input, settings);
|
|
1036
|
+
const { siteId, ...values } = input;
|
|
1037
|
+
if (!guid(siteId)) throw new ConnectorError("connector_input_invalid", "Choose a valid Wix Site ID.", { statusCode: 422 });
|
|
1038
|
+
const allowed = (settings.selectableSiteIds || "").split(",").map(id => id.trim().toLowerCase());
|
|
1039
|
+
if (!allowed.includes(siteId.toLowerCase())) throw new ConnectorError("connector_input_invalid", "This site is not enabled in the project's Selectable Site IDs.", { statusCode: 422 });
|
|
1040
|
+
return operation.request(values, { ...settings, siteId });
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
const wixProvider = Object.freeze({ ...wixRuntime,
|
|
1045
|
+
operations: Object.fromEntries(Object.entries(wixRuntime.operations).map(([name, operation]) =>
|
|
1046
|
+
[name, name === "sites.list" ? operation : wixSiteOperation(operation)]))
|
|
1047
|
+
});
|
|
1048
|
+
export { wixProvider };
|