Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GDPR and Uninstall Jobs to Generator #1597

Merged
merged 11 commits into from
Dec 1, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/shopify_app/webhooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#### Table of contents

[Manage webhooks using `ShopifyApp::WebhooksManager`](#manage-webhooks-using-shopifyappwebhooksmanager)
[Mandatory GDPR Webhooks](#mandatory-gdpr-webhooks)

## Manage webhooks using `ShopifyApp::WebhooksManager`

Expand Down Expand Up @@ -70,3 +71,15 @@ rails g shopify_app:add_webhook --topic carts/update --path webhooks/carts_updat
```

Where `--topic` is the topic and `--path` is the path the webhook should be sent to.

## Mandatory GDPR Webhooks

We have three mandatory GDPR webhooks

1. `customers/data_request`
2. `customer/redact`
3. `shop/redact`

The `generate shopify_app` command generated three job templates corresponding to all three of these webhooks.
To pass our approval process you will need to set these webhooks in your partner dashboard.
You can read more about that [here](https://shopify.dev/apps/webhooks/configuration/mandatory-webhooks).
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

require "rails/generators/base"

module ShopifyApp
module Generators
class AddAppUninstalledJobGenerator < Rails::Generators::Base
source_root File.expand_path("../templates", __FILE__)

def create_job
template("app_uninstalled_job.rb", "app/jobs/app_uninstalled_job.rb")
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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:)
shop = Shop.find_by(shopify_domain: shop_domain)

if shop.nil?
logger.error("#{self.class} failed: cannot find shop with domain '#{shop_domain}'")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should be raising an error and failing jobs if we can't find their shop domain 🤔

Sidekiq and Active Job have cool tooling for jobs that fail. If devs aren't watching logs, they might be missing out on key information that jobs are failing.

Raising an error will be available with exception handing software (bugsnag, airbake, rollbar, etc) as well as job monitoring dashboards like sidekiq's.

Copy link
Contributor

@nelsonwittwer nelsonwittwer Dec 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern seems to be in other jobs as well. I'd recommend logging in addition to failing the job. Failing a job means the job raised an exception during its run.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize you are probably boosting these from templates, but this is a good opportunity for improvement 😄 😬

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I just added a raise StandardError I thought I remember you saying to raise NotFound error? I couldn't find it anywhere maybe you said a different error, my bad for forgetting.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@nelsonwittwer nelsonwittwer Dec 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, you could use find_by! instead of find_by whiches raise an active record not found error automagically by the power of rails. We'd lose the logging with that approach though

return
end

logger.info("#{self.class} started for shop '#{shop_domain}'")
shop.destroy
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

require "rails/generators/base"

module ShopifyApp
module Generators
class AddGdprJobsGenerator < Rails::Generators::Base
source_root File.expand_path("../templates", __FILE__)

def add_customer_data_request_job
template("customer_data_request_job.rb", "app/jobs/customer_data_request_job.rb")
end

def add_shop_redact_job
template("shop_redact_job.rb", "app/jobs/shop_redact_job.rb")
end

def add_customer_redact_job
template("customer_redact_job.rb", "app/jobs/customer_redact_job.rb")
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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:)
shop = Shop.find_by(shopify_domain: shop_domain)

if shop.nil?
logger.error("#{self.class} failed: cannot find shop with domain '#{shop_domain}'")
return
end

shop.with_shopify_session do
logger.info("#{self.class} started for shop '#{shop_domain}'")
Copy link
Contributor Author

@klenotiw klenotiw Dec 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im not sure if anyone has any context on why this log message is here but not in the others? (In case you missed it in the description this was ripped right out of the ruby template)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My guess is oversight. Can we use our new shopify app logger for all of these? We should be logging all of these and allowing users to turn log levels down if they are too loud.

end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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:)
shop = Shop.find_by(shopify_domain: shop_domain)

if shop.nil?
logger.error("#{self.class} failed: cannot find shop with domain '#{shop_domain}'")
return
end

shop.with_shopify_session do
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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:)
shop = Shop.find_by(shopify_domain: shop_domain)

if shop.nil?
logger.error("#{self.class} failed: cannot find shop with domain '#{shop_domain}'")
return
end

shop.with_shopify_session do
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ ShopifyApp.configure do |config|
config.shop_session_repository = 'Shop'
config.log_level = :info
config.reauth_on_access_scope_changes = true
config.webhooks = [
{ topic: "app/uninstalled", address: "webhooks/app_uninstalled"}
]
klenotiw marked this conversation as resolved.
Show resolved Hide resolved

config.api_key = ENV.fetch('SHOPIFY_API_KEY', '').presence
config.secret = ENV.fetch('SHOPIFY_API_SECRET', '').presence
Expand Down
2 changes: 2 additions & 0 deletions lib/generators/shopify_app/shopify_app_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ def initialize(args, *options)
end

def run_all_generators
generate("shopify_app:add_app_uninstalled_job")
generate("shopify_app:add_gdpr_jobs")
generate("shopify_app:install #{@opts.join(" ")}")
generate("shopify_app:shop_model #{@opts.join(" ")}")
generate("shopify_app:authenticated_controller")
Expand Down
26 changes: 26 additions & 0 deletions test/generators/add_app_uninstalled_job_generator_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

require "test_helper"
require "generators/shopify_app/add_app_uninstalled_job/add_app_uninstalled_job_generator"

class AddAppUninstalledJobGeneratorTest < Rails::Generators::TestCase
tests ShopifyApp::Generators::AddAppUninstalledJobGenerator
destination File.expand_path("../tmp", File.dirname(__FILE__))

setup do
ShopifyApp.configure do |config|
config.embedded_app = true
end

prepare_destination
provide_existing_application_file
provide_existing_routes_file
provide_existing_application_controller
end

test "creates app uninstalled job file" do
run_generator

assert_file "app/jobs/app_uninstalled_job.rb"
end
end
28 changes: 28 additions & 0 deletions test/generators/add_gdpr_jobs_generator_job_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

require "test_helper"
require "generators/shopify_app/add_gdpr_jobs/add_gdpr_jobs_generator"

class AddGdprJobsGeneratorJobTest < Rails::Generators::TestCase
tests ShopifyApp::Generators::AddGdprJobsGenerator
destination File.expand_path("../tmp", File.dirname(__FILE__))

setup do
ShopifyApp.configure do |config|
config.embedded_app = true
end

prepare_destination
provide_existing_application_file
provide_existing_routes_file
provide_existing_application_controller
end

test "creates app uninstalled job file" do
run_generator

klenotiw marked this conversation as resolved.
Show resolved Hide resolved
assert_file "app/jobs/customer_data_request_job.rb"
assert_file "app/jobs/shop_redact_job.rb"
assert_file "app/jobs/customer_redact_job.rb"
end
end