From b0d58a3dac5cb5a9708ebdbc1cd5f9c9c442901f Mon Sep 17 00:00:00 2001 From: Alberto Vena Date: Wed, 20 Nov 2024 21:45:17 +0100 Subject: [PATCH 1/2] Extend webhook registration to support filters With this change, we can register webhooks specifying the filter parameter that allows to define some conditions on the topic fields to reduce the number of webhooks received. Other than adding this feature for already possible registrations, this extends the number of webhooks that can be registered with this library. In fact, there are some webhook registrations that require the filter parameter to be passed to allow the registrationi (this is partly an assumption because I couldn't find proper documentation around this topic). An example is METAOBJECTS_CREATE, which, without the filter param set, ``` mutation webhookSubscriptionCreate($topic: WebhookSubscriptionTopic!, $webhookSubscription: WebhookSubscriptionInput!) { webhookSubscriptionCreate(topic: $topic, webhookSubscription: $webhookSubscription) { webhookSubscription { topic filter } userErrors { field message } } } { "topic": "METAOBJECTS_CREATE", "webhookSubscription": { "callbackUrl": "https://test5.com" } } ``` gives the following error: ``` { "data": { "webhookSubscriptionCreate": { "webhookSubscription": null, "userErrors": [ { "field": [ "webhookSubscription" ], "message": "The specified filter is invalid, please ensure you specify the field(s) you wish to filter on." } ] } } } ``` While with the filter on, works as expected. --- This change has been made available to 2024-07 and 2024-10, which are the versions with the filter parameter available, according to the GraphQL documentation. --- Refs: - https://shopify.dev/docs/apps/build/webhooks/customize/filters - https://shopify.dev/docs/api/admin-graphql/2024-07/input-objects/WebhookSubscriptionInput - https://shopify.dev/docs/api/admin-graphql/2024-10/input-objects/WebhookSubscriptionInput --- CHANGELOG.md | 1 + docs/usage/webhooks.md | 11 ++ .../rest/resources/2024_07/webhook.rb | 1 + .../rest/resources/2024_10/webhook.rb | 1 + lib/shopify_api/webhooks/registration.rb | 11 +- .../webhooks/registrations/event_bridge.rb | 9 +- .../webhooks/registrations/http.rb | 9 +- .../webhooks/registrations/pub_sub.rb | 8 +- lib/shopify_api/webhooks/registry.rb | 14 +- test/webhooks/registry_test.rb | 26 ++- test/webhooks/registry_test_queries.rb | 176 +++++++++++++++++- 11 files changed, 249 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e58208e..b7d17277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Note: For changes to the API, see https://shopify.dev/changelog?filter=api ## Unreleased +- [#1347](https://github.com/Shopify/shopify-api-ruby/pull/1347) Extend webhook registration to support filters - [#1344](https://github.com/Shopify/shopify-api-ruby/pull/1344) Allow ShopifyAPI::Webhooks::Registry to update a webhook when fields or metafield_namespaces are changed. - [#1343](https://github.com/Shopify/shopify-api-ruby/pull/1343) Make ShopifyAPI::Context::scope parameter optional. `scope` defaults to empty list `[]`. diff --git a/docs/usage/webhooks.md b/docs/usage/webhooks.md index 55665ebe..32b940e0 100644 --- a/docs/usage/webhooks.md +++ b/docs/usage/webhooks.md @@ -82,6 +82,17 @@ registration = ShopifyAPI::Webhooks::Registry.add_registration( ) ``` +If you need to filter the webhooks you want to receive, you can use a [webhooks filter](https://shopify.dev/docs/apps/build/webhooks/customize/filters), which can be specified on registration through the `filter` parameter. + +```ruby +registration = ShopifyAPI::Webhooks::Registry.add_registration( + topic: "products/update", + delivery_method: :http, + handler: WebhookHandler, + filter: "variants.price:>=10.00" +) +``` + **Note**: The webhooks you register with Shopify are saved in the Shopify platform, but the local `ShopifyAPI::Webhooks::Registry` needs to be reloaded whenever your server restarts. ### EventBridge and PubSub Webhooks diff --git a/lib/shopify_api/rest/resources/2024_07/webhook.rb b/lib/shopify_api/rest/resources/2024_07/webhook.rb index c78827ae..21f5bbf0 100644 --- a/lib/shopify_api/rest/resources/2024_07/webhook.rb +++ b/lib/shopify_api/rest/resources/2024_07/webhook.rb @@ -23,6 +23,7 @@ def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) @api_version = T.let(nil, T.nilable(String)) @created_at = T.let(nil, T.nilable(String)) @fields = T.let(nil, T.nilable(T::Array[T.untyped])) + @filter = T.let(nil, T.nilable(String)) @format = T.let(nil, T.nilable(String)) @id = T.let(nil, T.nilable(Integer)) @metafield_namespaces = T.let(nil, T.nilable(T::Array[T.untyped])) diff --git a/lib/shopify_api/rest/resources/2024_10/webhook.rb b/lib/shopify_api/rest/resources/2024_10/webhook.rb index c78827ae..21f5bbf0 100644 --- a/lib/shopify_api/rest/resources/2024_10/webhook.rb +++ b/lib/shopify_api/rest/resources/2024_10/webhook.rb @@ -23,6 +23,7 @@ def initialize(session: ShopifyAPI::Context.active_session, from_hash: nil) @api_version = T.let(nil, T.nilable(String)) @created_at = T.let(nil, T.nilable(String)) @fields = T.let(nil, T.nilable(T::Array[T.untyped])) + @filter = T.let(nil, T.nilable(String)) @format = T.let(nil, T.nilable(String)) @id = T.let(nil, T.nilable(Integer)) @metafield_namespaces = T.let(nil, T.nilable(T::Array[T.untyped])) diff --git a/lib/shopify_api/webhooks/registration.rb b/lib/shopify_api/webhooks/registration.rb index 8cd18d54..b406aea6 100644 --- a/lib/shopify_api/webhooks/registration.rb +++ b/lib/shopify_api/webhooks/registration.rb @@ -22,18 +22,23 @@ class Registration sig { returns(T.nilable(T::Array[String])) } attr_reader :metafield_namespaces + sig { returns(T.nilable(String)) } + attr_reader :filter + sig do params(topic: String, path: String, handler: T.nilable(T.any(Handler, WebhookHandler)), fields: T.nilable(T.any(String, T::Array[String])), - metafield_namespaces: T.nilable(T::Array[String])).void + metafield_namespaces: T.nilable(T::Array[String]), + filter: T.nilable(String)).void end - def initialize(topic:, path:, handler: nil, fields: nil, metafield_namespaces: nil) + def initialize(topic:, path:, handler: nil, fields: nil, metafield_namespaces: nil, filter: nil) @topic = T.let(topic.gsub("/", "_").upcase, String) @path = path @handler = handler fields_array = fields.is_a?(String) ? fields.split(FIELDS_DELIMITER) : fields @fields = T.let(fields_array&.map(&:strip)&.compact, T.nilable(T::Array[String])) @metafield_namespaces = T.let(metafield_namespaces&.map(&:strip)&.compact, T.nilable(T::Array[String])) + @filter = filter end sig { abstract.returns(String) } @@ -54,6 +59,7 @@ def build_check_query; end current_address: T.nilable(String), fields: T::Array[String], metafield_namespaces: T::Array[String], + filter: T.nilable(String), }) end def parse_check_result(body); end @@ -88,6 +94,7 @@ def subscription_response_attributes attributes = ["id"] attributes << "includeFields" if @fields attributes << "metafieldNamespaces" if @metafield_namespaces + attributes << "filter" if @filter attributes end end diff --git a/lib/shopify_api/webhooks/registrations/event_bridge.rb b/lib/shopify_api/webhooks/registrations/event_bridge.rb index 8a3653f4..f5f02afe 100644 --- a/lib/shopify_api/webhooks/registrations/event_bridge.rb +++ b/lib/shopify_api/webhooks/registrations/event_bridge.rb @@ -14,7 +14,8 @@ def callback_address sig { override.returns(T::Hash[Symbol, String]) } def subscription_args - { arn: callback_address, includeFields: fields, metafieldNamespaces: metafield_namespaces }.compact + { arn: callback_address, includeFields: fields, + metafieldNamespaces: metafield_namespaces, filter: filter, }.compact end sig { override.params(webhook_id: T.nilable(String)).returns(String) } @@ -32,6 +33,7 @@ def build_check_query id includeFields metafieldNamespaces + filter endpoint { __typename ... on WebhookEventBridgeEndpoint { @@ -51,6 +53,7 @@ def build_check_query current_address: T.nilable(String), fields: T::Array[String], metafield_namespaces: T::Array[String], + filter: T.nilable(String), }) end def parse_check_result(body) @@ -58,6 +61,7 @@ def parse_check_result(body) webhook_id = nil fields = [] metafield_namespaces = [] + filter = nil current_address = nil unless edges.empty? node = edges[0]["node"] @@ -65,9 +69,10 @@ def parse_check_result(body) current_address = node["endpoint"]["arn"].to_s fields = node["includeFields"] || [] metafield_namespaces = node["metafieldNamespaces"] || [] + filter = node["filter"].to_s end { webhook_id: webhook_id, current_address: current_address, fields: fields, - metafield_namespaces: metafield_namespaces, } + metafield_namespaces: metafield_namespaces, filter: filter, } end end end diff --git a/lib/shopify_api/webhooks/registrations/http.rb b/lib/shopify_api/webhooks/registrations/http.rb index a3c0cf48..4e4459b8 100644 --- a/lib/shopify_api/webhooks/registrations/http.rb +++ b/lib/shopify_api/webhooks/registrations/http.rb @@ -20,7 +20,8 @@ def callback_address sig { override.returns(T::Hash[Symbol, String]) } def subscription_args - { callbackUrl: callback_address, includeFields: fields, metafieldNamespaces: metafield_namespaces }.compact + { callbackUrl: callback_address, includeFields: fields, + metafieldNamespaces: metafield_namespaces, filter: filter, }.compact end sig { override.params(webhook_id: T.nilable(String)).returns(String) } @@ -38,6 +39,7 @@ def build_check_query id includeFields metafieldNamespaces + filter endpoint { __typename ... on WebhookHttpEndpoint { @@ -57,6 +59,7 @@ def build_check_query current_address: T.nilable(String), fields: T::Array[String], metafield_namespaces: T::Array[String], + filter: T.nilable(String), }) end def parse_check_result(body) @@ -64,6 +67,7 @@ def parse_check_result(body) webhook_id = nil fields = [] metafield_namespaces = [] + filter = nil current_address = nil unless edges.empty? node = edges[0]["node"] @@ -76,9 +80,10 @@ def parse_check_result(body) end fields = node["includeFields"] || [] metafield_namespaces = node["metafieldNamespaces"] || [] + filter = node["filter"].to_s end { webhook_id: webhook_id, current_address: current_address, fields: fields, - metafield_namespaces: metafield_namespaces, } + metafield_namespaces: metafield_namespaces, filter: filter, } end end end diff --git a/lib/shopify_api/webhooks/registrations/pub_sub.rb b/lib/shopify_api/webhooks/registrations/pub_sub.rb index 183ba890..dc657964 100644 --- a/lib/shopify_api/webhooks/registrations/pub_sub.rb +++ b/lib/shopify_api/webhooks/registrations/pub_sub.rb @@ -18,7 +18,7 @@ def subscription_args project = project_topic_pair[0] topic = project_topic_pair[1] { pubSubProject: project, pubSubTopic: topic, includeFields: fields, - metafieldNamespaces: metafield_namespaces, }.compact + metafieldNamespaces: metafield_namespaces, filter: filter, }.compact end sig { override.params(webhook_id: T.nilable(String)).returns(String) } @@ -36,6 +36,7 @@ def build_check_query id includeFields metafieldNamespaces + filter endpoint { __typename ... on WebhookPubSubEndpoint { @@ -56,6 +57,7 @@ def build_check_query current_address: T.nilable(String), fields: T::Array[String], metafield_namespaces: T::Array[String], + filter: T.nilable(String), }) end def parse_check_result(body) @@ -63,6 +65,7 @@ def parse_check_result(body) webhook_id = nil fields = [] metafield_namespaces = [] + filter = nil current_address = nil unless edges.empty? node = edges[0]["node"] @@ -71,9 +74,10 @@ def parse_check_result(body) "pubsub://#{node["endpoint"]["pubSubProject"]}:#{node["endpoint"]["pubSubTopic"]}" fields = node["includeFields"] || [] metafield_namespaces = node["metafieldNamespaces"] || [] + filter = node["filter"].to_s end { webhook_id: webhook_id, current_address: current_address, fields: fields, - metafield_namespaces: metafield_namespaces, } + metafield_namespaces: metafield_namespaces, filter: filter, } end end end diff --git a/lib/shopify_api/webhooks/registry.rb b/lib/shopify_api/webhooks/registry.rb index d6abb8d4..8cc26aab 100644 --- a/lib/shopify_api/webhooks/registry.rb +++ b/lib/shopify_api/webhooks/registry.rb @@ -19,23 +19,25 @@ class << self path: String, handler: T.nilable(T.any(Handler, WebhookHandler)), fields: T.nilable(T.any(String, T::Array[String])), + filter: T.nilable(String), metafield_namespaces: T.nilable(T::Array[String])).void end - def add_registration(topic:, delivery_method:, path:, handler: nil, fields: nil, metafield_namespaces: nil) + def add_registration(topic:, delivery_method:, path:, handler: nil, fields: nil, filter: nil, + metafield_namespaces: nil) @registry[topic] = case delivery_method when :pub_sub Registrations::PubSub.new(topic: topic, path: path, fields: fields, - metafield_namespaces: metafield_namespaces) + metafield_namespaces: metafield_namespaces, filter: filter) when :event_bridge Registrations::EventBridge.new(topic: topic, path: path, fields: fields, - metafield_namespaces: metafield_namespaces) + metafield_namespaces: metafield_namespaces, filter: filter) when :http unless handler raise Errors::InvalidWebhookRegistrationError, "Cannot create an Http registration without a handler." end Registrations::Http.new(topic: topic, path: path, handler: handler, - fields: fields, metafield_namespaces: metafield_namespaces) + fields: fields, metafield_namespaces: metafield_namespaces, filter: filter) else raise Errors::InvalidWebhookRegistrationError, "Unsupported delivery method #{delivery_method}. Allowed values: {:http, :pub_sub, :event_bridge}." @@ -223,10 +225,12 @@ def webhook_registration_needed?(client, registration) parsed_check_result = registration.parse_check_result(T.cast(check_response.body, T::Hash[String, T.untyped])) registration_fields = registration.fields || [] registration_metafield_namespaces = registration.metafield_namespaces || [] + registration_filter = registration.filter must_register = parsed_check_result[:current_address] != registration.callback_address || parsed_check_result[:fields].sort != registration_fields.sort || - parsed_check_result[:metafield_namespaces].sort != registration_metafield_namespaces.sort + parsed_check_result[:metafield_namespaces].sort != registration_metafield_namespaces.sort || + parsed_check_result[:filter] != registration_filter { webhook_id: parsed_check_result[:webhook_id], must_register: must_register } end diff --git a/test/webhooks/registry_test.rb b/test/webhooks/registry_test.rb index 6a852a9d..a2c0dec8 100644 --- a/test/webhooks/registry_test.rb +++ b/test/webhooks/registry_test.rb @@ -53,6 +53,7 @@ def setup address, fields: "field1, field2", metafield_namespaces: ["namespace1", "namespace2"], + filter: "id:*", ) # Then @@ -72,6 +73,7 @@ def setup address, fields: ["field1", "field2"], metafield_namespaces: ["namespace1", "namespace2"], + filter: "id:*", ) # Then @@ -156,6 +158,27 @@ def setup update_registration_response.body) end + define_method("test_#{protocol}_update_registration_filter_with_address_#{address}") do + # Given + setup_queries_and_responses( + [queries[protocol][:check_query], queries[protocol][:register_update_query_with_filter]], + [queries[protocol][:check_existing_response], + queries[protocol][:register_update_with_filter_response],], + ) + + # When + update_registration_response = add_and_register_webhook( + protocol, + address, + filter: "id:*", + ) + + # Then + assert(update_registration_response.success) + assert_equal(queries[protocol][:register_update_with_filter_response], + update_registration_response.body) + end + define_method("test_raises_on_#{protocol}_registration_check_error_with_address_#{address}") do # Given ShopifyAPI::Webhooks::Registry.clear @@ -412,7 +435,7 @@ def setup_queries_and_responses(queries, responses) end end - def add_and_register_webhook(protocol, address, fields: nil, metafield_namespaces: nil) + def add_and_register_webhook(protocol, address, fields: nil, metafield_namespaces: nil, filter: nil) ShopifyAPI::Webhooks::Registry.add_registration( topic: @topic, delivery_method: protocol, @@ -423,6 +446,7 @@ def add_and_register_webhook(protocol, address, fields: nil, metafield_namespace ), fields: fields, metafield_namespaces: metafield_namespaces, + filter: filter, ) update_registration_response = ShopifyAPI::Webhooks::Registry.register_all( session: @session, diff --git a/test/webhooks/registry_test_queries.rb b/test/webhooks/registry_test_queries.rb index ddb18631..c2bebe65 100644 --- a/test/webhooks/registry_test_queries.rb +++ b/test/webhooks/registry_test_queries.rb @@ -16,6 +16,7 @@ def queries id includeFields metafieldNamespaces + filter endpoint { __typename ... on WebhookHttpEndpoint { @@ -38,7 +39,7 @@ def queries register_add_query: <<~QUERY, mutation webhookSubscription { - webhookSubscriptionCreate(topic: SOME_TOPIC, webhookSubscription: {callbackUrl: "https://app-address.com/test-webhooks", includeFields: ["field1", "field2"], metafieldNamespaces: ["namespace1", "namespace2"]}) { + webhookSubscriptionCreate(topic: SOME_TOPIC, webhookSubscription: {callbackUrl: "https://app-address.com/test-webhooks", includeFields: ["field1", "field2"], metafieldNamespaces: ["namespace1", "namespace2"], filter: "id:*"}) { userErrors { field message @@ -47,6 +48,7 @@ def queries id includeFields metafieldNamespaces + filter } } } @@ -82,6 +84,22 @@ def queries } QUERY + register_add_query_with_filter: + <<~QUERY, + mutation webhookSubscription { + webhookSubscriptionCreate(topic: SOME_TOPIC, webhookSubscription: {callbackUrl: "https://app-address.com/test-webhooks", filter: "id:*"}) { + userErrors { + field + message + } + webhookSubscription { + id + filter + } + } + } + QUERY + register_add_response: { "data" => { "webhookSubscriptionCreate" => { @@ -90,6 +108,7 @@ def queries "id" => "gid://shopify/WebhookSubscription/12345", "includeFields" => ["field1", "field2"], "metafieldNamespaces" => ["namespace1", "namespace2"], + "filter" => "id:*", }, }, }, @@ -116,6 +135,17 @@ def queries }, }, }, + register_add_with_filter_response: { + "data" => { + "webhookSubscriptionCreate" => { + "userErrors" => [], + "webhookSubscription" => { + "id" => "gid://shopify/WebhookSubscription/12345", + "filter" => "id:*", + }, + }, + }, + }, check_existing_response: { "data" => { "webhookSubscriptions" => { @@ -139,6 +169,7 @@ def queries "id" => "gid://shopify/WebhookSubscription/12345", "includeFields" => ["field1", "field2"], "metafieldNamespaces" => ["namespace1", "namespace2"], + "filter" => "id:*", "endpoint" => { "typename" => "WebhookHttpEndpoint", "callbackUrl" => "https://app-address.com/test-webhooks", @@ -192,6 +223,21 @@ def queries } } QUERY + register_update_query_with_filter: + <<~QUERY, + mutation webhookSubscription { + webhookSubscriptionUpdate(id: "gid://shopify/WebhookSubscription/12345", webhookSubscription: {callbackUrl: "https://app-address.com/test-webhooks", filter: "id:*"}) { + userErrors { + field + message + } + webhookSubscription { + id + filter + } + } + } + QUERY register_update_response: { "data" => { "webhookSubscriptionUpdate" => { @@ -223,6 +269,17 @@ def queries }, }, }, + register_update_with_filter_response: { + "data" => { + "webhookSubscriptionUpdate" => { + "userErrors" => [], + "webhookSubscription" => { + "id" => "gid://shopify/WebhookSubscription/12345", + "filter" => "id:*", + }, + }, + }, + }, }, event_bridge: { check_query: @@ -234,6 +291,7 @@ def queries id includeFields metafieldNamespaces + filter endpoint { __typename ... on WebhookEventBridgeEndpoint { @@ -255,7 +313,7 @@ def queries register_add_query: <<~QUERY, mutation webhookSubscription { - eventBridgeWebhookSubscriptionCreate(topic: SOME_TOPIC, webhookSubscription: {arn: "test-webhooks", includeFields: ["field1", "field2"], metafieldNamespaces: ["namespace1", "namespace2"]}) { + eventBridgeWebhookSubscriptionCreate(topic: SOME_TOPIC, webhookSubscription: {arn: "test-webhooks", includeFields: ["field1", "field2"], metafieldNamespaces: ["namespace1", "namespace2"], filter: "id:*"}) { userErrors { field message @@ -264,6 +322,7 @@ def queries id includeFields metafieldNamespaces + filter } } } @@ -298,6 +357,21 @@ def queries } } QUERY + register_add_query_with_filter: + <<~QUERY, + mutation webhookSubscription { + eventBridgeWebhookSubscriptionCreate(topic: SOME_TOPIC, webhookSubscription: {arn: "test-webhooks", filter: "id:*"}) { + userErrors { + field + message + } + webhookSubscription { + id + filter + } + } + } + QUERY register_add_response: { "data" => { "eventBridgeWebhookSubscriptionCreate" => { @@ -306,6 +380,7 @@ def queries "id" => "gid://shopify/WebhookSubscription/12345", "includeFields" => ["field1", "field2"], "metafieldNamespaces" => ["namespace1", "namespace2"], + "filter" => "id:*", }, }, }, @@ -332,6 +407,17 @@ def queries }, }, }, + register_add_with_filter_response: { + "data" => { + "eventBridgeWebhookSubscriptionCreate" => { + "userErrors" => [], + "webhookSubscription" => { + "id" => "gid://shopify/WebhookSubscription/12345", + "filter" => "id:*", + }, + }, + }, + }, check_existing_response: { "data" => { "webhookSubscriptions" => { @@ -359,6 +445,7 @@ def queries }, "includeFields" => ["field2", "field1"], "metafieldNamespaces" => ["namespace2", "namespace1"], + "filter" => "id:*", }, ], }, @@ -408,6 +495,21 @@ def queries } } QUERY + register_update_query_with_filter: + <<~QUERY, + mutation webhookSubscription { + eventBridgeWebhookSubscriptionUpdate(id: "gid://shopify/WebhookSubscription/12345", webhookSubscription: {arn: "test-webhooks", filter: "id:*"}) { + userErrors { + field + message + } + webhookSubscription { + id + filter + } + } + } + QUERY register_update_response: { "data" => { "eventBridgeWebhookSubscriptionUpdate" => { @@ -438,6 +540,17 @@ def queries }, }, }, + register_update_with_filter_response: { + "data" => { + "eventBridgeWebhookSubscriptionUpdate" => { + "userErrors" => [], + "webhookSubscription" => { + "id" => "gid://shopify/WebhookSubscription/12345", + "filter" => "id:*", + }, + }, + }, + }, }, pub_sub: { check_query: @@ -449,6 +562,7 @@ def queries id includeFields metafieldNamespaces + filter endpoint { __typename ... on WebhookPubSubEndpoint { @@ -471,7 +585,7 @@ def queries register_add_query: <<~QUERY, mutation webhookSubscription { - pubSubWebhookSubscriptionCreate(topic: SOME_TOPIC, webhookSubscription: {pubSubProject: "my-project-id", pubSubTopic: "my-topic-id", includeFields: ["field1", "field2"], metafieldNamespaces: ["namespace1", "namespace2"]}) { + pubSubWebhookSubscriptionCreate(topic: SOME_TOPIC, webhookSubscription: {pubSubProject: "my-project-id", pubSubTopic: "my-topic-id", includeFields: ["field1", "field2"], metafieldNamespaces: ["namespace1", "namespace2"], filter: "id:*"}) { userErrors { field message @@ -480,6 +594,7 @@ def queries id includeFields metafieldNamespaces + filter } } } @@ -514,6 +629,21 @@ def queries } } QUERY + register_add_query_with_filter: + <<~QUERY, + mutation webhookSubscription { + pubSubWebhookSubscriptionCreate(topic: SOME_TOPIC, webhookSubscription: {pubSubProject: "my-project-id", pubSubTopic: "my-topic-id", filter: "id:*"}) { + userErrors { + field + message + } + webhookSubscription { + id + filter + } + } + } + QUERY register_add_response: { "data" => { "pubSubWebhookSubscriptionCreate" => { @@ -522,6 +652,7 @@ def queries "id" => "gid://shopify/WebhookSubscription/12345", "includeFields" => ["field1", "field2"], "metafieldNamespaces" => ["namespace1", "namespace2"], + "filter" => "id:*", }, }, }, @@ -548,6 +679,17 @@ def queries }, }, }, + register_add_with_filter_response: { + "data" => { + "pubSubWebhookSubscriptionCreate" => { + "userErrors" => [], + "webhookSubscription" => { + "id" => "gid://shopify/WebhookSubscription/12345", + "filter": "id:*", + }, + }, + }, + }, check_existing_response: { "data" => { "webhookSubscriptions" => { @@ -577,6 +719,7 @@ def queries }, "includeFields" => ["field1", "field2"], "metafieldNamespaces" => ["namespace1", "namespace2"], + "filter" => "id:*", }, ], }, @@ -626,7 +769,21 @@ def queries } } QUERY - + register_update_query_with_filter: + <<~QUERY, + mutation webhookSubscription { + pubSubWebhookSubscriptionUpdate(id: "gid://shopify/WebhookSubscription/12345", webhookSubscription: {pubSubProject: "my-project-id", pubSubTopic: "my-topic-id", filter: "id:*"}) { + userErrors { + field + message + } + webhookSubscription { + id + filter + } + } + } + QUERY register_update_response: { "data" => { "pubSubWebhookSubscriptionUpdate" => { @@ -657,6 +814,17 @@ def queries }, }, }, + register_update_with_filter_response: { + "data" => { + "pubSubWebhookSubscriptionUpdate" => { + "userErrors" => [], + "webhookSubscription" => { + "id" => "gid://shopify/WebhookSubscription/12345", + "filter" => "id:*", + }, + }, + }, + }, }, fetch_id_query: <<~QUERY, From 240be2825d5e958bfa18155926aee93d1bb0cc42 Mon Sep 17 00:00:00 2001 From: Matteo Depalo Date: Thu, 28 Nov 2024 11:31:44 +0000 Subject: [PATCH 2/2] Packaging for release 14.7.0 --- CHANGELOG.md | 2 ++ Gemfile.lock | 19 ++++++++++++------- lib/shopify_api/version.rb | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7d17277..dc17bfdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Note: For changes to the API, see https://shopify.dev/changelog?filter=api ## Unreleased +## 14.7.0 + - [#1347](https://github.com/Shopify/shopify-api-ruby/pull/1347) Extend webhook registration to support filters - [#1344](https://github.com/Shopify/shopify-api-ruby/pull/1344) Allow ShopifyAPI::Webhooks::Registry to update a webhook when fields or metafield_namespaces are changed. - [#1343](https://github.com/Shopify/shopify-api-ruby/pull/1343) Make ShopifyAPI::Context::scope parameter optional. `scope` defaults to empty list `[]`. diff --git a/Gemfile.lock b/Gemfile.lock index c896d185..63f7e16c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - shopify_api (14.6.0) + shopify_api (14.7.0) activesupport concurrent-ruby hash_diff @@ -16,20 +16,24 @@ PATH GEM remote: https://rubygems.org/ specs: - activesupport (7.1.4) + activesupport (7.1.5) base64 + benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) mutex_m + securerandom (>= 0.3) tzinfo (~> 2.0) addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) ast (2.4.2) base64 (0.2.0) + benchmark (0.4.0) bigdecimal (3.1.8) byebug (11.1.3) coderay (1.1.3) @@ -50,21 +54,22 @@ GEM i18n (1.14.6) concurrent-ruby (~> 1.0) json (2.7.1) - jwt (2.9.1) + jwt (2.9.3) base64 language_server-protocol (3.17.0.3) + logger (1.6.1) method_source (1.0.0) mini_mime (1.1.5) minitest (5.15.0) mocha (1.13.0) multi_xml (0.6.0) - mutex_m (0.2.0) + mutex_m (0.3.0) netrc (0.11.0) - oj (3.16.6) + oj (3.16.7) bigdecimal (>= 3.0) ostruct (>= 0.2) openssl (3.2.0) - ostruct (0.6.0) + ostruct (0.6.1) parallel (1.24.0) parser (3.3.0.5) ast (~> 2.4.1) @@ -104,7 +109,7 @@ GEM rubocop-sorbet (0.6.11) rubocop (>= 0.90.0) ruby-progressbar (1.13.0) - securerandom (0.3.1) + securerandom (0.3.2) sorbet (0.5.11230) sorbet-static (= 0.5.11230) sorbet-runtime (0.5.11230) diff --git a/lib/shopify_api/version.rb b/lib/shopify_api/version.rb index c2b1cb8a..9c307dce 100644 --- a/lib/shopify_api/version.rb +++ b/lib/shopify_api/version.rb @@ -2,5 +2,5 @@ # frozen_string_literal: true module ShopifyAPI - VERSION = "14.6.0" + VERSION = "14.7.0" end