shopify_app 22.3.0 → 22.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +1 -1
- data/CHANGELOG.md +10 -0
- data/Gemfile.lock +2 -2
- data/app/views/shopify_app/shared/redirect.html.erb +5 -0
- data/docs/Quickstart.md +9 -2
- data/docs/shopify_app/webhooks.md +13 -0
- data/lib/generators/shopify_app/add_declarative_webhook/add_declarative_webhook_generator.rb +53 -0
- data/lib/generators/shopify_app/add_declarative_webhook/templates/webhook_controller.rb.tt +13 -0
- data/lib/generators/shopify_app/add_declarative_webhook/templates/webhook_job.rb.tt +15 -0
- data/lib/generators/shopify_app/add_webhook/templates/webhook_job.rb.tt +1 -0
- data/lib/shopify_app/configuration.rb +4 -0
- data/lib/shopify_app/controller_concerns/frame_ancestors.rb +1 -1
- data/lib/shopify_app/controller_concerns/login_protection.rb +1 -1
- data/lib/shopify_app/controller_concerns/sanitized_params.rb +4 -0
- data/lib/shopify_app/controller_concerns/token_exchange.rb +1 -5
- data/lib/shopify_app/test_helpers/shopify_session_helper.rb +1 -0
- data/lib/shopify_app/utils.rb +5 -1
- data/lib/shopify_app/version.rb +1 -1
- data/package.json +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7fa0c48b506596cf235c8c32fcdb0e39c3aaa7d8ffd9b284dee69a7979d76fc7
|
4
|
+
data.tar.gz: 745fbc9dd772dd70838d18092323ff72845fb55b77750642461c8e77de0d34da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec0279dff3206dd9b23586c3a483f4a6f6d098a8ceaf92a3f817ea02def91d4fb306b54c25cc2d2125eb0e30d8058a09b65cac7d553f5dad8546dc1df5018259
|
7
|
+
data.tar.gz: 73a5bdac727b368a98062c31a42959f5d62bbca48ea1535a0643986f2c34ced20975707c6773834bbe567cc91372652edd24ccc9535ae1a981b55e39b3659a87
|
data/.github/workflows/build.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
Unreleased
|
2
2
|
----------
|
3
3
|
|
4
|
+
22.4.0 (August 22, 2024)
|
5
|
+
----------
|
6
|
+
- Add the `unified_admin_domain` configuration option for the unified admin domain.
|
7
|
+
- Add new generators for webhook subscriptions defined in the `shopify.app.toml` file [1882](https://github.com/Shopify/shopify_app/pull/1882)
|
8
|
+
- Fix test stubbing for Token Exchange auth [1897](https://github.com/Shopify/shopify_app/pull/1897)
|
9
|
+
|
10
|
+
22.3.1 (July 26, 2024)
|
11
|
+
----------
|
12
|
+
- Handle edge case where we attempted to redirect to login when already at the top level [#1887](https://github.com/Shopify/shopify_app/pull/1887)
|
13
|
+
|
4
14
|
22.3.0 (July 24, 2024)
|
5
15
|
----------
|
6
16
|
- Deprecate `ShopifyApp::JWTMiddleware`. And remove internal usage. Any existing app code relying on decoded JWT contents set from `request.env` should instead include the `WithShopifyIdToken` concern and call its respective methods. [#1861](https://github.com/Shopify/shopify_app/pull/1861) [Migration Guide](/docs/Upgrading.md#v2300---removed-shopifyappjwtmiddleware)
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
shopify_app (22.
|
4
|
+
shopify_app (22.4.0)
|
5
5
|
activeresource
|
6
6
|
addressable (~> 2.7)
|
7
7
|
jwt (>= 2.2.3)
|
@@ -196,7 +196,7 @@ GEM
|
|
196
196
|
redirect_safely (1.0.0)
|
197
197
|
activemodel
|
198
198
|
regexp_parser (2.9.0)
|
199
|
-
rexml (3.3.
|
199
|
+
rexml (3.3.3)
|
200
200
|
strscan
|
201
201
|
rubocop (1.62.1)
|
202
202
|
json (~> 2.3)
|
@@ -6,8 +6,13 @@
|
|
6
6
|
<base target="_top">
|
7
7
|
<title>Redirecting…</title>
|
8
8
|
|
9
|
+
<%
|
10
|
+
is_iframe = local_assigns.key?(:is_iframe) ? is_iframe : true
|
11
|
+
if is_iframe
|
12
|
+
%>
|
9
13
|
<meta name="shopify-api-key" content="<%= ShopifyApp.configuration.api_key %>">
|
10
14
|
<%= javascript_include_tag "https://cdn.shopify.com/shopifycloud/app-bridge.js" %>
|
15
|
+
<% end %>
|
11
16
|
|
12
17
|
<%= javascript_include_tag('shopify_app/redirect', crossorigin: 'anonymous', integrity: true) %>
|
13
18
|
</head>
|
data/docs/Quickstart.md
CHANGED
@@ -34,8 +34,15 @@ HOST='https://some-random-words.trycloudflare.com/'
|
|
34
34
|
|
35
35
|
## Use Shopify App Bridge to embed your app in the Shopify Admin
|
36
36
|
|
37
|
-
A basic example of using [*Shopify App Bridge*](https://shopify.dev/tools/app-bridge) is included in the install generator. An instance Shopify App Bridge is automatically initialized in [shopify_app.js](https://github.com/Shopify/shopify_app/blob/master/lib/generators/shopify_app/install/templates/shopify_app.js).
|
37
|
+
A basic example of using [*Shopify App Bridge*](https://shopify.dev/tools/app-bridge) is included in the install generator. An instance Shopify App Bridge is automatically initialized in [shopify_app.js](https://github.com/Shopify/shopify_app/blob/master/lib/generators/shopify_app/install/templates/shopify_app.js).
|
38
38
|
|
39
|
-
|
39
|
+
If you are using the `shopify_app` gem **without** the [frontend react template](https://github.com/Shopify/shopify-frontend-template-react), the [flash_messages.js](https://github.com/Shopify/shopify_app/blob/master/lib/generators/shopify_app/install/templates/flash_messages.js) file converts Rails [flash messages](https://api.rubyonrails.org/classes/ActionDispatch/Flash.html) to App Bridge Toast actions automatically. If your app is embedded and you want to display flash messages you will need to update the session storage to allow for 3rd party cookies. So that the flash messages can be save in the session cookie.
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
#session_store.rb
|
43
|
+
Rails.application.config.session_store(:cookie_store, key: '_example_session', expire_after: 14.days, secure: true, same_site: 'None')
|
44
|
+
```
|
45
|
+
|
46
|
+
By default, this library is included via [unpkg in the embedded_app layout](https://github.com/Shopify/shopify_app/blob/master/lib/generators/shopify_app/install/templates/embedded_app.html.erb#L27).
|
40
47
|
|
41
48
|
For more advanced uses it is recommended to [install App Bridge via npm or yarn](https://help.shopify.com/en/api/embedded-apps/app-bridge/getting-started#set-up-shopify-app-bridge-in-your-app).
|
@@ -2,9 +2,22 @@
|
|
2
2
|
|
3
3
|
#### Table of contents
|
4
4
|
|
5
|
+
[Webhooks subscription in the app config](#subscribing-to-webhooks-in-the-app-configuration-file)
|
5
6
|
[Manage webhooks using `ShopifyApp::WebhooksManager`](#manage-webhooks-using-shopifyappwebhooksmanager)
|
6
7
|
[Mandatory Privacy Webhooks](#mandatory-privacy-webhooks)
|
7
8
|
|
9
|
+
## Subscribing to webhooks in the app configuration file
|
10
|
+
You can now specify the webhooks you want to subscribe to in the `shopify.app.toml` file. Mandatory privacy webhooks and other webhooks can be specified in the `webhooks` section of the configuration file. Learn more about the different way to subscribe to webhooks in the [documentation](https://shopify.dev/docs/apps/build/webhooks/subscribe).
|
11
|
+
|
12
|
+
### Consuming webhooks events
|
13
|
+
To consume webhooks events where the subscription is done in the `shopify.app.toml` file you can scaffold the necessary files by running the following generator.
|
14
|
+
|
15
|
+
```bash
|
16
|
+
rails g shopify_app:add_declarative_webhook --topic carts/update --path webhooks/carts_update
|
17
|
+
```
|
18
|
+
|
19
|
+
This will add a new controller, job, and route to your application. The controller will verify the webhook and queue the job to process the webhook. The job will be responsible for processing the webhook data.
|
20
|
+
|
8
21
|
## Manage webhooks using `ShopifyApp::WebhooksManager`
|
9
22
|
|
10
23
|
See [`ShopifyApp::WebhooksManager`](/lib/shopify_app/managers/webhooks_manager.rb)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators/base"
|
4
|
+
|
5
|
+
module ShopifyApp
|
6
|
+
module Generators
|
7
|
+
class AddDeclarativeWebhookGenerator < Rails::Generators::Base
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
9
|
+
class_option :topic, type: :string, aliases: "-t", required: true
|
10
|
+
class_option :path, type: :string, aliases: "-p", required: true
|
11
|
+
|
12
|
+
hook_for :test_framework, as: :job, in: :rails do |instance, generator|
|
13
|
+
instance.invoke(generator, [instance.send(:file_name)])
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_webhook_job
|
17
|
+
namespace = ShopifyApp.configuration.webhook_jobs_namespace
|
18
|
+
@job_file_name = if namespace.present?
|
19
|
+
"#{namespace}/#{file_name}_job"
|
20
|
+
else
|
21
|
+
"#{file_name}_job"
|
22
|
+
end
|
23
|
+
@job_class_name = @job_file_name.classify
|
24
|
+
template("webhook_job.rb", "app/jobs/#{@job_file_name}.rb")
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_webhook_controller
|
28
|
+
@controller_file_name = "#{file_name}_controller"
|
29
|
+
@controller_class_name = @controller_file_name.classify
|
30
|
+
template("webhook_controller.rb", "app/controllers/webhooks/#{@controller_file_name}.rb")
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_webhook_route
|
34
|
+
route = "\t\t\tpost '#{file_name}', to: '#{file_name}#receive'\n"
|
35
|
+
inject_into_file("config/routes.rb", route, after: /namespace :webhooks do\n/)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def file_name
|
41
|
+
path.split("/").last
|
42
|
+
end
|
43
|
+
|
44
|
+
def topic
|
45
|
+
options["topic"]
|
46
|
+
end
|
47
|
+
|
48
|
+
def path
|
49
|
+
options["path"]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Webhooks
|
4
|
+
class <%= @controller_class_name %> < ApplicationController
|
5
|
+
include ShopifyApp::WebhookVerification
|
6
|
+
|
7
|
+
def receive
|
8
|
+
webhook_request = ShopifyAPI::Webhooks::Request.new(raw_body: request.raw_post, headers: request.headers.to_h)
|
9
|
+
<%= @job_class_name %>.perform_later(shop_domain: webhook_request.shop, webhook: webhook_request.parsed_body)
|
10
|
+
head(:no_content)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class <%= @job_class_name %> < ActiveJob::Base
|
2
|
+
|
3
|
+
def perform(shop_domain:, webhook:)
|
4
|
+
shop = Shop.find_by(shopify_domain: shop_domain)
|
5
|
+
|
6
|
+
if shop.nil?
|
7
|
+
logger.error("#{self.class} failed: cannot find shop with domain '#{shop_domain}'")
|
8
|
+
|
9
|
+
raise ActiveRecord::RecordNotFound, "Shop Not Found"
|
10
|
+
end
|
11
|
+
|
12
|
+
shop.with_shopify_session do |session|
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -39,6 +39,9 @@ module ShopifyApp
|
|
39
39
|
# configure myshopify domain for local shopify development
|
40
40
|
attr_accessor :myshopify_domain
|
41
41
|
|
42
|
+
# configure the unified admin domain for local shopify development
|
43
|
+
attr_accessor :unified_admin_domain
|
44
|
+
|
42
45
|
# ability to have webpacker installed but not used in this gem and the generators
|
43
46
|
attr_accessor :disable_webpacker
|
44
47
|
|
@@ -54,6 +57,7 @@ module ShopifyApp
|
|
54
57
|
def initialize
|
55
58
|
@root_url = "/"
|
56
59
|
@myshopify_domain = "myshopify.com"
|
60
|
+
@unified_admin_domain = "shopify.com"
|
57
61
|
@scripttags_manager_queue_name = Rails.application.config.active_job.queue_name
|
58
62
|
@webhooks_manager_queue_name = Rails.application.config.active_job.queue_name
|
59
63
|
@disable_webpacker = ENV["SHOPIFY_APP_DISABLE_WEBPACKER"].present?
|
@@ -8,7 +8,7 @@ module ShopifyApp
|
|
8
8
|
content_security_policy do |policy|
|
9
9
|
policy.frame_ancestors(-> do
|
10
10
|
domain_host = current_shopify_domain || "*.#{::ShopifyApp.configuration.myshopify_domain}"
|
11
|
-
"#{ShopifyAPI::Context.host_scheme}://#{domain_host} https://admin.
|
11
|
+
"#{ShopifyAPI::Context.host_scheme}://#{domain_host} https://admin.#{::ShopifyApp.configuration.unified_admin_domain}"
|
12
12
|
end)
|
13
13
|
end
|
14
14
|
end
|
@@ -204,7 +204,7 @@ module ShopifyApp
|
|
204
204
|
render(
|
205
205
|
"shopify_app/shared/redirect",
|
206
206
|
layout: false,
|
207
|
-
locals: { url: url, current_shopify_domain: current_shopify_domain },
|
207
|
+
locals: { url: url, current_shopify_domain: current_shopify_domain, is_iframe: embedded? },
|
208
208
|
)
|
209
209
|
else
|
210
210
|
redirect_to(url)
|
@@ -94,10 +94,6 @@ module ShopifyApp
|
|
94
94
|
)
|
95
95
|
end
|
96
96
|
|
97
|
-
def embedded?
|
98
|
-
params[:embedded] == "1" || request.env["HTTP_SEC_FETCH_DEST"] == "iframe"
|
99
|
-
end
|
100
|
-
|
101
97
|
def online_token_configured?
|
102
98
|
ShopifyApp.configuration.online_token_configured?
|
103
99
|
end
|
@@ -108,7 +104,7 @@ module ShopifyApp
|
|
108
104
|
render(
|
109
105
|
"shopify_app/shared/redirect",
|
110
106
|
layout: false,
|
111
|
-
locals: { url: url, current_shopify_domain: current_shopify_domain },
|
107
|
+
locals: { url: url, current_shopify_domain: current_shopify_domain, is_iframe: embedded? },
|
112
108
|
)
|
113
109
|
end
|
114
110
|
end
|
@@ -7,6 +7,7 @@ module ShopifyApp
|
|
7
7
|
ShopifyAPI::Auth::Session.new(id: session_id, shop: shop_domain).tap do |session|
|
8
8
|
ShopifyApp::SessionRepository.stubs(:load_session).returns(session)
|
9
9
|
ShopifyAPI::Utils::SessionUtils.stubs(:current_session_id).returns(session.id)
|
10
|
+
ShopifyAPI::Utils::SessionUtils.stubs(:session_id_from_shopify_id_token).returns(session.id)
|
10
11
|
ShopifyAPI::Context.activate_session(session)
|
11
12
|
end
|
12
13
|
end
|
data/lib/shopify_app/utils.rb
CHANGED
@@ -44,7 +44,7 @@ module ShopifyApp
|
|
44
44
|
if spin_env
|
45
45
|
"https://admin.web.#{spin_env}/store/#{shop}"
|
46
46
|
else
|
47
|
-
"https://admin
|
47
|
+
"https://admin.#{unified_admin_domain}/store/#{shop}"
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
@@ -54,6 +54,10 @@ module ShopifyApp
|
|
54
54
|
ShopifyApp.configuration.myshopify_domain
|
55
55
|
end
|
56
56
|
|
57
|
+
def unified_admin_domain
|
58
|
+
ShopifyApp.configuration.unified_admin_domain
|
59
|
+
end
|
60
|
+
|
57
61
|
def trusted_domains
|
58
62
|
trusted_domains = TRUSTED_SHOPIFY_DOMAINS.dup
|
59
63
|
trusted_domains.append(myshopify_domain).uniq! if myshopify_domain
|
data/lib/shopify_app/version.rb
CHANGED
data/package.json
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shopify_app
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 22.
|
4
|
+
version: 22.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activeresource
|
@@ -363,6 +363,9 @@ files:
|
|
363
363
|
- lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb
|
364
364
|
- lib/generators/shopify_app/add_app_uninstalled_job/add_app_uninstalled_job_generator.rb
|
365
365
|
- lib/generators/shopify_app/add_app_uninstalled_job/templates/app_uninstalled_job.rb.tt
|
366
|
+
- lib/generators/shopify_app/add_declarative_webhook/add_declarative_webhook_generator.rb
|
367
|
+
- lib/generators/shopify_app/add_declarative_webhook/templates/webhook_controller.rb.tt
|
368
|
+
- lib/generators/shopify_app/add_declarative_webhook/templates/webhook_job.rb.tt
|
366
369
|
- lib/generators/shopify_app/add_privacy_jobs/add_privacy_jobs_generator.rb
|
367
370
|
- lib/generators/shopify_app/add_privacy_jobs/templates/customers_data_request_job.rb.tt
|
368
371
|
- lib/generators/shopify_app/add_privacy_jobs/templates/customers_redact_job.rb.tt
|
@@ -478,7 +481,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
478
481
|
- !ruby/object:Gem::Version
|
479
482
|
version: '0'
|
480
483
|
requirements: []
|
481
|
-
rubygems_version: 3.5.
|
484
|
+
rubygems_version: 3.5.17
|
482
485
|
signing_key:
|
483
486
|
specification_version: 4
|
484
487
|
summary: This gem is used to get quickly started with the Shopify API
|