From 90ca95422d49aefa2c2a7402cbec5e1cf31f08fc Mon Sep 17 00:00:00 2001 From: David Peacock Date: Tue, 7 May 2024 09:34:12 -0400 Subject: [PATCH] Add filter to webhooks app config --- .../app/src/cli/models/app/loader.test.ts | 57 +++++++++++++++++-- .../specifications/app_config_webhook.test.ts | 50 ++++++++++++++++ .../specifications/app_config_webhook.ts | 3 +- .../webhook_subscription_schema.ts | 1 + .../app_config_webhook_subscription.test.ts | 22 +++++++ .../app_config_webhook_subscription.ts | 1 + .../types/app_config_webhook.ts | 1 + .../validation/app_config_webhook.ts | 6 +- 8 files changed, 131 insertions(+), 10 deletions(-) diff --git a/packages/app/src/cli/models/app/loader.test.ts b/packages/app/src/cli/models/app/loader.test.ts index 4873fcdd03c..bb28d1d567c 100644 --- a/packages/app/src/cli/models/app/loader.test.ts +++ b/packages/app/src/cli/models/app/loader.test.ts @@ -2734,7 +2734,7 @@ describe('WebhooksSchema', () => { } const errorObj = { code: zod.ZodIssueCode.custom, - message: 'You can’t have duplicate subscriptions with the exact same `topic` and `uri`', + message: 'You can’t have duplicate subscriptions with the exact same `topic`, `uri` and `filter`', fatal: true, path: ['webhooks', 'subscriptions', 0, 'topics', 1, 'products/create'], } @@ -2753,7 +2753,7 @@ describe('WebhooksSchema', () => { } const errorObj = { code: zod.ZodIssueCode.custom, - message: 'You can’t have duplicate subscriptions with the exact same `topic` and `uri`', + message: 'You can’t have duplicate subscriptions with the exact same `topic`, `uri` and `filter`', fatal: true, path: ['webhooks', 'subscriptions', 1, 'topics', 0, 'products/create'], } @@ -2847,7 +2847,7 @@ describe('WebhooksSchema', () => { } const errorObj = { code: zod.ZodIssueCode.custom, - message: 'You can’t have duplicate subscriptions with the exact same `topic` and `uri`', + message: 'You can’t have duplicate subscriptions with the exact same `topic`, `uri` and `filter`', fatal: true, path: ['webhooks', 'subscriptions', 1, 'topics', 0, 'products/create'], } @@ -2872,7 +2872,7 @@ describe('WebhooksSchema', () => { } const errorObj = { code: zod.ZodIssueCode.custom, - message: 'You can’t have duplicate subscriptions with the exact same `topic` and `uri`', + message: 'You can’t have duplicate subscriptions with the exact same `topic`, `uri` and `filter`', fatal: true, path: ['webhooks', 'subscriptions', 1, 'topics', 0, 'products/create'], } @@ -2897,7 +2897,7 @@ describe('WebhooksSchema', () => { } const errorObj = { code: zod.ZodIssueCode.custom, - message: 'You can’t have duplicate subscriptions with the exact same `topic` and `uri`', + message: 'You can’t have duplicate subscriptions with the exact same `topic`, `uri` and `filter`', fatal: true, path: ['webhooks', 'subscriptions', 1, 'topics', 0, 'products/create'], } @@ -2924,7 +2924,7 @@ describe('WebhooksSchema', () => { } const errorObj = { code: zod.ZodIssueCode.custom, - message: 'You can’t have duplicate subscriptions with the exact same `topic` and `uri`', + message: 'You can’t have duplicate subscriptions with the exact same `topic`, `uri` and `filter`', fatal: true, path: ['webhooks', 'subscriptions', 1, 'topics', 0, 'metaobjects/create'], } @@ -2949,6 +2949,51 @@ describe('WebhooksSchema', () => { }, ], } + }) + + test('does not allow identical topic and uri and filter in different subscriptions', async () => { + const webhookConfig: WebhooksConfig = { + api_version: '2021-07', + subscriptions: [ + { + topics: ['products/update'], + uri: 'https://example.com', + filter: 'title:shoes', + }, + { + topics: ['products/update'], + uri: 'https://example.com', + filter: 'title:shoes', + }, + ], + } + const errorObj = { + code: zod.ZodIssueCode.custom, + message: 'You can’t have duplicate subscriptions with the exact same `topic`, `uri` and `filter`', + fatal: true, + path: ['webhooks', 'subscriptions', 1, 'topics', 0, 'products/update'], + } + + const {abortOrReport, expectedFormatted} = await setupParsing(errorObj, webhookConfig) + expect(abortOrReport).toHaveBeenCalledWith(expectedFormatted, {}, 'tmp', [errorObj]) + }) + + test('allows identical topic and uri if filter is different', async () => { + const webhookConfig: WebhooksConfig = { + api_version: '2021-07', + subscriptions: [ + { + topics: ['products/update'], + uri: 'https://example.com', + filter: 'title:shoes', + }, + { + topics: ['products/update'], + uri: 'https://example.com', + filter: 'title:shirts', + }, + ], + } const {abortOrReport, parsedConfiguration} = await setupParsing({}, webhookConfig) expect(abortOrReport).not.toHaveBeenCalled() diff --git a/packages/app/src/cli/models/extensions/specifications/app_config_webhook.test.ts b/packages/app/src/cli/models/extensions/specifications/app_config_webhook.test.ts index 53af91334aa..df03eb1fcbb 100644 --- a/packages/app/src/cli/models/extensions/specifications/app_config_webhook.test.ts +++ b/packages/app/src/cli/models/extensions/specifications/app_config_webhook.test.ts @@ -38,6 +38,11 @@ describe('webhooks', () => { uri: 'arn:aws:events:us-west-2::event-source/aws.partner/shopify.com/1234567890/SOME_PATH', sub_topic: 'type:metaobject_one', }, + { + topics: ['products/create', 'products/update'], + uri: 'https://example.com/webhooks/products', + filter: 'title:shoes', + }, ], }, } @@ -101,6 +106,16 @@ describe('webhooks', () => { topic: 'metaobjects/delete', uri: 'arn:aws:events:us-west-2::event-source/aws.partner/shopify.com/1234567890/SOME_PATH', }, + { + filter: 'title:shoes', + topic: 'products/create', + uri: 'https://example.com/webhooks/products', + }, + { + filter: 'title:shoes', + topic: 'products/update', + uri: 'https://example.com/webhooks/products', + }, ], }) }) @@ -146,6 +161,11 @@ describe('webhooks', () => { topic: 'orders/create', uri: 'https://valid-url', }, + { + filter: 'title:shoes', + topic: 'products/create', + uri: 'https://example.com/webhooks/products', + }, ], } const webhookSpec = spec @@ -176,6 +196,11 @@ describe('webhooks', () => { topics: ['orders/create'], uri: 'https://valid-url', }, + { + filter: 'title:shoes', + topics: ['products/create'], + uri: 'https://example.com/webhooks/products', + }, ], }, }) @@ -233,6 +258,21 @@ describe('webhooks', () => { sub_topic: 'subtopic', uri: 'https://example.com/webhooks', }, + { + topics: ['products/create'], + uri: 'https://example.com/webhooks', + filter: 'title:shoes', + }, + { + topics: ['products/update'], + uri: 'https://example.com/webhooks', + filter: 'title:shoes', + }, + { + topics: ['products/update'], + uri: 'https://example.com/webhooks', + filter: 'title:shirts', + }, ], privacy_compliance: undefined, }, @@ -268,6 +308,16 @@ describe('webhooks', () => { sub_topic: 'subtopic', uri: 'https://example.com/webhooks', }, + { + topics: ['products/create', 'products/update'], + uri: 'https://example.com/webhooks', + filter: 'title:shoes', + }, + { + topics: ['products/update'], + uri: 'https://example.com/webhooks', + filter: 'title:shirts', + }, ], privacy_compliance: undefined, }, diff --git a/packages/app/src/cli/models/extensions/specifications/app_config_webhook.ts b/packages/app/src/cli/models/extensions/specifications/app_config_webhook.ts index 3c3a2f71b24..9afedc51107 100644 --- a/packages/app/src/cli/models/extensions/specifications/app_config_webhook.ts +++ b/packages/app/src/cli/models/extensions/specifications/app_config_webhook.ts @@ -37,7 +37,8 @@ function mergeAllWebhooks(subscriptions: WebhookSubscription[]): WebhookSubscrip (sub) => sub.uri === subscription.uri && sub.sub_topic === subscription.sub_topic && - sub.include_fields === subscription.include_fields, + sub.include_fields === subscription.include_fields && + sub.filter === subscription.filter, ) if (existingSubscription) { if (subscription.compliance_topics) { diff --git a/packages/app/src/cli/models/extensions/specifications/app_config_webhook_schemas/webhook_subscription_schema.ts b/packages/app/src/cli/models/extensions/specifications/app_config_webhook_schemas/webhook_subscription_schema.ts index 95a007378a0..dcdd2083064 100644 --- a/packages/app/src/cli/models/extensions/specifications/app_config_webhook_schemas/webhook_subscription_schema.ts +++ b/packages/app/src/cli/models/extensions/specifications/app_config_webhook_schemas/webhook_subscription_schema.ts @@ -16,6 +16,7 @@ export const WebhookSubscriptionSchema = zod.object({ uri: zod.preprocess(removeTrailingSlash, UriValidation, {required_error: 'Missing value at'}), sub_topic: zod.string({invalid_type_error: 'Value must be a string'}).optional(), include_fields: zod.array(zod.string({invalid_type_error: 'Value must be a string'})).optional(), + filter: zod.string({invalid_type_error: 'Value must be a string'}).optional(), compliance_topics: zod .array( zod.enum([ComplianceTopic.CustomersRedact, ComplianceTopic.CustomersDataRequest, ComplianceTopic.ShopRedact]), diff --git a/packages/app/src/cli/models/extensions/specifications/app_config_webhook_subscription.test.ts b/packages/app/src/cli/models/extensions/specifications/app_config_webhook_subscription.test.ts index 2498e5e8799..5eb01c6157d 100644 --- a/packages/app/src/cli/models/extensions/specifications/app_config_webhook_subscription.test.ts +++ b/packages/app/src/cli/models/extensions/specifications/app_config_webhook_subscription.test.ts @@ -37,6 +37,11 @@ describe('webhook_subscription', () => { uri: 'arn:aws:events:us-west-2::event-source/aws.partner/shopify.com/1234567890/SOME_PATH', sub_topic: 'type:metaobject_one', }, + { + topics: ['products/update'], + uri: 'https://example.com/webhooks/products', + filter: 'title:shoes', + }, ], }, } @@ -110,6 +115,12 @@ describe('webhook_subscription', () => { topic: 'metaobjects/delete', uri: 'arn:aws:events:us-west-2::event-source/aws.partner/shopify.com/1234567890/SOME_PATH', }, + { + api_version: '2024-01', + filter: 'title:shoes', + topic: 'products/update', + uri: 'https://example.com/webhooks/products', + }, ], }) }) @@ -160,6 +171,12 @@ describe('webhook_subscription', () => { topic: 'orders/create', uri: 'https://valid-url', }, + { + api_version: '2024-01', + filter: 'title:shoes', + topic: 'products/update', + uri: 'https://example.com/webhooks/products', + }, ], } const webhookSpec = spec @@ -189,6 +206,11 @@ describe('webhook_subscription', () => { topics: ['orders/create'], uri: 'https://valid-url', }, + { + filter: 'title:shoes', + topics: ['products/update'], + uri: 'https://example.com/webhooks/products', + }, ], }, }) diff --git a/packages/app/src/cli/models/extensions/specifications/app_config_webhook_subscription.ts b/packages/app/src/cli/models/extensions/specifications/app_config_webhook_subscription.ts index 498d50f0e5b..c194871e11e 100644 --- a/packages/app/src/cli/models/extensions/specifications/app_config_webhook_subscription.ts +++ b/packages/app/src/cli/models/extensions/specifications/app_config_webhook_subscription.ts @@ -13,6 +13,7 @@ interface TransformedWebhookSubscription { compliance_topics?: string[] sub_topic?: string include_fields?: string[] + filter?: string } /* this transforms webhooks from the TOML config to be parsed remotely diff --git a/packages/app/src/cli/models/extensions/specifications/types/app_config_webhook.ts b/packages/app/src/cli/models/extensions/specifications/types/app_config_webhook.ts index 576e687efae..e7318136989 100644 --- a/packages/app/src/cli/models/extensions/specifications/types/app_config_webhook.ts +++ b/packages/app/src/cli/models/extensions/specifications/types/app_config_webhook.ts @@ -4,6 +4,7 @@ export interface WebhookSubscription { compliance_topics?: string[] sub_topic?: string include_fields?: string[] + filter?: string } interface PrivacyComplianceConfig { diff --git a/packages/app/src/cli/models/extensions/specifications/validation/app_config_webhook.ts b/packages/app/src/cli/models/extensions/specifications/validation/app_config_webhook.ts index e23ff4792be..5e861869dc5 100644 --- a/packages/app/src/cli/models/extensions/specifications/validation/app_config_webhook.ts +++ b/packages/app/src/cli/models/extensions/specifications/validation/app_config_webhook.ts @@ -38,7 +38,7 @@ function validateSubscriptions(webhookConfig: WebhooksConfig) { } // eslint-disable-next-line @typescript-eslint/naming-convention - for (const [i, {uri, topics = [], compliance_topics = [], sub_topic = ''}] of subscriptions.entries()) { + for (const [i, {uri, topics = [], compliance_topics = [], sub_topic = '', filter = ''}] of subscriptions.entries()) { const path = ['subscriptions', i] if (!topics.length && !compliance_topics.length) { @@ -50,12 +50,12 @@ function validateSubscriptions(webhookConfig: WebhooksConfig) { } for (const [j, topic] of topics.entries()) { - const key = `${topic}::${sub_topic}::${uri}` + const key = `${topic}::${sub_topic}::${uri}::${filter}` if (uniqueSubscriptionSet.has(key)) { return { code: zod.ZodIssueCode.custom, - message: 'You can’t have duplicate subscriptions with the exact same `topic` and `uri`', + message: 'You can’t have duplicate subscriptions with the exact same `topic`, `uri` and `filter`', fatal: true, path: [...path, 'topics', j, topic], }