Skip to content

Commit

Permalink
Add declarative webhooks processing
Browse files Browse the repository at this point in the history
The webhook registry in the package assumes that all webhooks to be processed will be defined in the config

It also couples subscribing to processing

This PR removes the registration loggic from the config.

It uses the provided webhook verification concern with new controllers
  • Loading branch information
lizkenyon committed Jul 17, 2024
1 parent 141398d commit df12694
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 76 deletions.
8 changes: 4 additions & 4 deletions shopify.app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ scopes = "write_products"
api_version = "2024-07"

[[webhooks.subscriptions]]
uri = "/api/webhooks/app_uninstalled"
uri = "/api/shopify_webhooks/app_uninstalled"
topics = [ "app/uninstalled" ]

[[webhooks.subscriptions]]
uri = "/api/webhooks/customers_data_request"
uri = "/api/shopify_webhooks/customers_data_request"
compliance_topics = [ "customers/data_request"]

[[webhooks.subscriptions]]
uri = "/api/webhooks/customers_redact"
uri = "/api/shopify_webhooks/customers_redact"
compliance_topics = [ "customers/redact" ]

[[webhooks.subscriptions]]
uri = "/api/webhooks/shop_redact"
uri = "/api/shopify_webhooks/shop_redact"
compliance_topics = [ "shop/redact" ]
13 changes: 13 additions & 0 deletions web/app/controllers/shopify_webhooks/app_uninstalled_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module ShopifyWebhooks
class AppUninstalledController < ApplicationController
include ShopifyApp::WebhookVerification

def receive
webhook_request = ShopifyAPI::Webhooks::Request.new(raw_body: request.raw_post, headers: request.headers.to_h)
AppUninstalledJob.perform_later(shop_domain: webhook_request.shop, webhook: webhook_request.parsed_body)
head(:no_content)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module ShopifyWebhooks
class CustomerDataRequestController < ApplicationController
include ShopifyApp::WebhookVerification

def receive
webhook_request = ShopifyAPI::Webhooks::Request.new(raw_body: request.raw_post, headers: request.headers.to_h)
CustomerDataRequestJob.perform_later(shop_domain: webhook_request.shop, webhook: webhook_request.parsed_body)
head(:no_content)
end
end
end
13 changes: 13 additions & 0 deletions web/app/controllers/shopify_webhooks/customer_redact_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module ShopifyWebhooks
class CustomerRedactController < ApplicationController
include ShopifyApp::WebhookVerification

def receive
webhook_request = ShopifyAPI::Webhooks::Request.new(raw_body: request.raw_post, headers: request.headers.to_h)
CustomerRedactJob.perform_later(shop_domain: webhook_request.shop, webhook: webhook_request.parsed_body)
head(:no_content)
end
end
end
13 changes: 13 additions & 0 deletions web/app/controllers/shopify_webhooks/shop_redact_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module ShopifyWebhooks
class ShopRedactController < ApplicationController
include ShopifyApp::WebhookVerification

def receive
webhook_request = ShopifyAPI::Webhooks::Request.new(raw_body: request.raw_post, headers: request.headers.to_h)
ShopRedactJob.perform_later(shop_domain: webhook_request.shop, webhook: webhook_request.parsed_body)
head(:no_content)
end
end
end
10 changes: 1 addition & 9 deletions web/app/jobs/app_uninstalled_job.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
# frozen_string_literal: true

class AppUninstalledJob < ActiveJob::Base
extend ShopifyAPI::Webhooks::Handler

class << self
def handle(topic:, shop:, body:)
perform_later(topic: topic, shop_domain: shop, webhook: body)
end
end

def perform(topic:, shop_domain:, webhook:)
def perform(shop_domain:, webhook:)
shop = Shop.find_by(shopify_domain: shop_domain)

if shop.nil?
Expand Down
10 changes: 1 addition & 9 deletions web/app/jobs/customers_data_request_job.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
# frozen_string_literal: true

class CustomersDataRequestJob < ActiveJob::Base
extend ShopifyAPI::Webhooks::Handler

class << self
def handle(topic:, shop:, body:)
perform_later(topic: topic, shop_domain: shop, webhook: body)
end
end

def perform(topic:, shop_domain:, webhook:)
def perform(shop_domain:, webhook:)
shop = Shop.find_by(shopify_domain: shop_domain)

if shop.nil?
Expand Down
10 changes: 1 addition & 9 deletions web/app/jobs/customers_redact_job.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
# frozen_string_literal: true

class CustomersRedactJob < ActiveJob::Base
extend ShopifyAPI::Webhooks::Handler

class << self
def handle(topic:, shop:, body:)
perform_later(topic: topic, shop_domain: shop, webhook: body)
end
end

def perform(topic:, shop_domain:, webhook:)
def perform(shop_domain:, webhook:)
shop = Shop.find_by(shopify_domain: shop_domain)

if shop.nil?
Expand Down
10 changes: 1 addition & 9 deletions web/app/jobs/shop_redact_job.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
# frozen_string_literal: true

class ShopRedactJob < ActiveJob::Base
extend ShopifyAPI::Webhooks::Handler

class << self
def handle(topic:, shop:, body:)
perform_later(topic: topic, shop_domain: shop, webhook: body)
end
end

def perform(topic:, shop_domain:, webhook:)
def perform(shop_domain:, webhook:)
shop = Shop.find_by(shopify_domain: shop_domain)

if shop.nil?
Expand Down
36 changes: 0 additions & 36 deletions web/config/initializers/shopify_app.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
# frozen_string_literal: true

ShopifyApp.configure do |config|
config.webhooks = [
# After a store owner uninstalls your app, Shopify invokes the APP_UNINSTALLED webhook
# to let your app know.
{ topic: "app/uninstalled", address: "api/webhooks/app_uninstalled" },
]
config.application_name = "My Shopify App"
config.old_secret = ""
config.scope = ENV.fetch("SCOPES", "write_products") # See shopify.app.toml for scopes
Expand Down Expand Up @@ -69,36 +64,5 @@
private_shop: ENV.fetch("SHOPIFY_APP_PRIVATE_SHOP", nil),
user_agent_prefix: "ShopifyApp/#{ShopifyApp::VERSION}",
)

add_privacy_webhooks
ShopifyApp::WebhooksManager.add_registrations
end
end

def add_privacy_webhooks
privacy_webhooks = [
# NOTE: To register the URLs for the three privacy topics that follow, please set the appropriate
# webhook endpoint in the 'Privacy webhooks' section of 'App setup' in the Partners Dashboard.
# The code that processes these webhooks is located in the `app/jobs` directory.
#
# 48 hours after a store owner uninstalls your app, Shopify invokes this SHOP_REDACT webhook.
# https://shopify.dev/docs/apps/webhooks/configuration/mandatory-webhooks#shop-redact
{ topic: "shop/redact", address: "api/webhooks/shop_redact" },

# Store owners can request that data is deleted on behalf of a customer. When this happens,
# Shopify invokes this CUSTOMERS_REDACT webhook to let your app know.
# https://shopify.dev/docs/apps/webhooks/configuration/mandatory-webhooks#customers-redact
{ topic: "customers/redact", address: "api/webhooks/customers_redact" },

# Customers can request their data from a store owner. When this happens, Shopify invokes
# this CUSTOMERS_DATA_REQUEST webhook to let your app know.
# https://shopify.dev/docs/apps/webhooks/configuration/mandatory-webhooks#customers-data_request
{ topic: "customers/data_request", address: "api/webhooks/customers_data_request" },
]

ShopifyApp.configuration.webhooks = if ShopifyApp.configuration.has_webhooks?
ShopifyApp.configuration.webhooks.concat(privacy_webhooks)
else
privacy_webhooks
end
end
6 changes: 6 additions & 0 deletions web/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
get :count
end
end
namespace :shopify_webhooks do
post "/app_uninstalled", to: "app_uninstalled#receive"
post "/customers_data_request", to: "customers_data_request#receive"
post "/customers_redact", to: "customers_redact#receive"
post "/shop_redact", to: "shop_redact#receive"
end
end

# Any other routes will just render the react app
Expand Down

0 comments on commit df12694

Please sign in to comment.