shopify_app 22.3.1 → 22.5.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 +11 -0
- data/Gemfile.lock +74 -76
- data/README.md +3 -2
- data/docs/Quickstart.md +9 -2
- data/docs/shopify_app/authentication.md +31 -0
- data/docs/shopify_app/webhooks.md +34 -5
- 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 +5 -0
- data/lib/shopify_app/controller_concerns/frame_ancestors.rb +1 -1
- data/lib/shopify_app/managers/webhooks_manager.rb +1 -0
- 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 +2 -2
- data/shopify_app.gemspec +1 -1
- data/yarn.lock +209 -185
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8a8d6fb5b227bbe3070fba9050497eb007e4191a782bc89b12a84546470ed65
|
4
|
+
data.tar.gz: 5f8d8e4486585b07a93ad7f6abddaebde7a263568dc78e757627476aba37d38b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a0b7ad073c42b6fbe5033aa686fa365050d8d56305507746b15f938a1511e979eecd49dcaefbe2793114c6ca4c4ef4f986085b743651115dc816faab404da15
|
7
|
+
data.tar.gz: a8896ed834816ef8fd35f17db67aef3efcd401fa2a46900e2a2bce8d5b96f863db3eaa0335e73d63b6c48e6382d52d9103c46fce7a7252568e1abe7e503d2075
|
data/.github/workflows/build.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,17 @@
|
|
1
1
|
Unreleased
|
2
2
|
----------
|
3
3
|
|
4
|
+
22.5.0 (November 28, 2024)
|
5
|
+
----------
|
6
|
+
- Add support for filters in webhook registration [1923](https://github.com/Shopify/shopify_app/pull/1923)
|
7
|
+
- Make `ShopifyApp.configuration.scope` default to empty list `[]` [1913](https://github.com/Shopify/shopify_app/pull/1913)
|
8
|
+
|
9
|
+
22.4.0 (August 22, 2024)
|
10
|
+
----------
|
11
|
+
- Add the `unified_admin_domain` configuration option for the unified admin domain.
|
12
|
+
- Add new generators for webhook subscriptions defined in the `shopify.app.toml` file [1882](https://github.com/Shopify/shopify_app/pull/1882)
|
13
|
+
- Fix test stubbing for Token Exchange auth [1897](https://github.com/Shopify/shopify_app/pull/1897)
|
14
|
+
|
4
15
|
22.3.1 (July 26, 2024)
|
5
16
|
----------
|
6
17
|
- 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)
|
data/Gemfile.lock
CHANGED
@@ -1,80 +1,80 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
shopify_app (22.
|
4
|
+
shopify_app (22.5.0)
|
5
5
|
activeresource
|
6
6
|
addressable (~> 2.7)
|
7
7
|
jwt (>= 2.2.3)
|
8
8
|
rails (> 5.2.1)
|
9
9
|
redirect_safely (~> 1.0)
|
10
|
-
shopify_api (>= 14.
|
10
|
+
shopify_api (>= 14.7.0, < 15.0)
|
11
11
|
sprockets-rails (>= 2.0.0)
|
12
12
|
|
13
13
|
GEM
|
14
14
|
remote: https://rubygems.org/
|
15
15
|
specs:
|
16
|
-
actioncable (6.1.7.
|
17
|
-
actionpack (= 6.1.7.
|
18
|
-
activesupport (= 6.1.7.
|
16
|
+
actioncable (6.1.7.9)
|
17
|
+
actionpack (= 6.1.7.9)
|
18
|
+
activesupport (= 6.1.7.9)
|
19
19
|
nio4r (~> 2.0)
|
20
20
|
websocket-driver (>= 0.6.1)
|
21
|
-
actionmailbox (6.1.7.
|
22
|
-
actionpack (= 6.1.7.
|
23
|
-
activejob (= 6.1.7.
|
24
|
-
activerecord (= 6.1.7.
|
25
|
-
activestorage (= 6.1.7.
|
26
|
-
activesupport (= 6.1.7.
|
21
|
+
actionmailbox (6.1.7.9)
|
22
|
+
actionpack (= 6.1.7.9)
|
23
|
+
activejob (= 6.1.7.9)
|
24
|
+
activerecord (= 6.1.7.9)
|
25
|
+
activestorage (= 6.1.7.9)
|
26
|
+
activesupport (= 6.1.7.9)
|
27
27
|
mail (>= 2.7.1)
|
28
|
-
actionmailer (6.1.7.
|
29
|
-
actionpack (= 6.1.7.
|
30
|
-
actionview (= 6.1.7.
|
31
|
-
activejob (= 6.1.7.
|
32
|
-
activesupport (= 6.1.7.
|
28
|
+
actionmailer (6.1.7.9)
|
29
|
+
actionpack (= 6.1.7.9)
|
30
|
+
actionview (= 6.1.7.9)
|
31
|
+
activejob (= 6.1.7.9)
|
32
|
+
activesupport (= 6.1.7.9)
|
33
33
|
mail (~> 2.5, >= 2.5.4)
|
34
34
|
rails-dom-testing (~> 2.0)
|
35
|
-
actionpack (6.1.7.
|
36
|
-
actionview (= 6.1.7.
|
37
|
-
activesupport (= 6.1.7.
|
35
|
+
actionpack (6.1.7.9)
|
36
|
+
actionview (= 6.1.7.9)
|
37
|
+
activesupport (= 6.1.7.9)
|
38
38
|
rack (~> 2.0, >= 2.0.9)
|
39
39
|
rack-test (>= 0.6.3)
|
40
40
|
rails-dom-testing (~> 2.0)
|
41
41
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
42
|
-
actiontext (6.1.7.
|
43
|
-
actionpack (= 6.1.7.
|
44
|
-
activerecord (= 6.1.7.
|
45
|
-
activestorage (= 6.1.7.
|
46
|
-
activesupport (= 6.1.7.
|
42
|
+
actiontext (6.1.7.9)
|
43
|
+
actionpack (= 6.1.7.9)
|
44
|
+
activerecord (= 6.1.7.9)
|
45
|
+
activestorage (= 6.1.7.9)
|
46
|
+
activesupport (= 6.1.7.9)
|
47
47
|
nokogiri (>= 1.8.5)
|
48
|
-
actionview (6.1.7.
|
49
|
-
activesupport (= 6.1.7.
|
48
|
+
actionview (6.1.7.9)
|
49
|
+
activesupport (= 6.1.7.9)
|
50
50
|
builder (~> 3.1)
|
51
51
|
erubi (~> 1.4)
|
52
52
|
rails-dom-testing (~> 2.0)
|
53
53
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
54
|
-
activejob (6.1.7.
|
55
|
-
activesupport (= 6.1.7.
|
54
|
+
activejob (6.1.7.9)
|
55
|
+
activesupport (= 6.1.7.9)
|
56
56
|
globalid (>= 0.3.6)
|
57
|
-
activemodel (6.1.7.
|
58
|
-
activesupport (= 6.1.7.
|
57
|
+
activemodel (6.1.7.9)
|
58
|
+
activesupport (= 6.1.7.9)
|
59
59
|
activemodel-serializers-xml (1.0.2)
|
60
60
|
activemodel (> 5.x)
|
61
61
|
activesupport (> 5.x)
|
62
62
|
builder (~> 3.1)
|
63
|
-
activerecord (6.1.7.
|
64
|
-
activemodel (= 6.1.7.
|
65
|
-
activesupport (= 6.1.7.
|
66
|
-
activeresource (6.1.
|
63
|
+
activerecord (6.1.7.9)
|
64
|
+
activemodel (= 6.1.7.9)
|
65
|
+
activesupport (= 6.1.7.9)
|
66
|
+
activeresource (6.1.3)
|
67
67
|
activemodel (>= 6.0)
|
68
68
|
activemodel-serializers-xml (~> 1.0)
|
69
69
|
activesupport (>= 6.0)
|
70
|
-
activestorage (6.1.7.
|
71
|
-
actionpack (= 6.1.7.
|
72
|
-
activejob (= 6.1.7.
|
73
|
-
activerecord (= 6.1.7.
|
74
|
-
activesupport (= 6.1.7.
|
70
|
+
activestorage (6.1.7.9)
|
71
|
+
actionpack (= 6.1.7.9)
|
72
|
+
activejob (= 6.1.7.9)
|
73
|
+
activerecord (= 6.1.7.9)
|
74
|
+
activesupport (= 6.1.7.9)
|
75
75
|
marcel (~> 1.0)
|
76
76
|
mini_mime (>= 1.1.0)
|
77
|
-
activesupport (6.1.7.
|
77
|
+
activesupport (6.1.7.9)
|
78
78
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
79
79
|
i18n (>= 1.6, < 2)
|
80
80
|
minitest (>= 5.1)
|
@@ -88,21 +88,21 @@ GEM
|
|
88
88
|
builder (3.3.0)
|
89
89
|
byebug (11.1.3)
|
90
90
|
coderay (1.1.3)
|
91
|
-
concurrent-ruby (1.3.
|
91
|
+
concurrent-ruby (1.3.4)
|
92
92
|
crack (0.4.5)
|
93
93
|
rexml
|
94
94
|
crass (1.0.6)
|
95
|
-
date (3.3.
|
95
|
+
date (3.3.4)
|
96
96
|
debug_inspector (1.1.0)
|
97
97
|
erubi (1.13.0)
|
98
|
-
globalid (1.1
|
99
|
-
activesupport (>=
|
98
|
+
globalid (1.2.1)
|
99
|
+
activesupport (>= 6.1)
|
100
100
|
hash_diff (1.1.1)
|
101
101
|
hashdiff (1.0.1)
|
102
102
|
httparty (0.21.0)
|
103
103
|
mini_mime (>= 1.0.0)
|
104
104
|
multi_xml (>= 0.5.2)
|
105
|
-
i18n (1.14.
|
105
|
+
i18n (1.14.6)
|
106
106
|
concurrent-ruby (~> 1.0)
|
107
107
|
json (2.7.2)
|
108
108
|
jwt (2.7.0)
|
@@ -117,26 +117,26 @@ GEM
|
|
117
117
|
net-smtp
|
118
118
|
marcel (1.0.2)
|
119
119
|
method_source (1.0.0)
|
120
|
-
mini_mime (1.1.
|
120
|
+
mini_mime (1.1.5)
|
121
121
|
minitest (5.18.0)
|
122
122
|
mocha (2.0.2)
|
123
123
|
ruby2_keywords (>= 0.0.5)
|
124
124
|
multi_xml (0.6.0)
|
125
|
-
net-imap (0.
|
125
|
+
net-imap (0.4.17)
|
126
126
|
date
|
127
127
|
net-protocol
|
128
128
|
net-pop (0.1.2)
|
129
129
|
net-protocol
|
130
|
-
net-protocol (0.2.
|
130
|
+
net-protocol (0.2.2)
|
131
131
|
timeout
|
132
|
-
net-smtp (0.
|
132
|
+
net-smtp (0.5.0)
|
133
133
|
net-protocol
|
134
134
|
nio4r (2.5.9)
|
135
|
-
nokogiri (1.16.
|
135
|
+
nokogiri (1.16.7-arm64-darwin)
|
136
136
|
racc (~> 1.4)
|
137
|
-
nokogiri (1.16.
|
137
|
+
nokogiri (1.16.7-x86_64-darwin)
|
138
138
|
racc (~> 1.4)
|
139
|
-
nokogiri (1.16.
|
139
|
+
nokogiri (1.16.7-x86_64-linux)
|
140
140
|
racc (~> 1.4)
|
141
141
|
oj (3.14.3)
|
142
142
|
openssl (3.1.0)
|
@@ -154,24 +154,24 @@ GEM
|
|
154
154
|
binding_of_caller (~> 1.0)
|
155
155
|
pry (~> 0.13)
|
156
156
|
public_suffix (5.0.1)
|
157
|
-
racc (1.8.
|
158
|
-
rack (2.2.
|
157
|
+
racc (1.8.1)
|
158
|
+
rack (2.2.10)
|
159
159
|
rack-test (2.1.0)
|
160
160
|
rack (>= 1.3)
|
161
|
-
rails (6.1.7.
|
162
|
-
actioncable (= 6.1.7.
|
163
|
-
actionmailbox (= 6.1.7.
|
164
|
-
actionmailer (= 6.1.7.
|
165
|
-
actionpack (= 6.1.7.
|
166
|
-
actiontext (= 6.1.7.
|
167
|
-
actionview (= 6.1.7.
|
168
|
-
activejob (= 6.1.7.
|
169
|
-
activemodel (= 6.1.7.
|
170
|
-
activerecord (= 6.1.7.
|
171
|
-
activestorage (= 6.1.7.
|
172
|
-
activesupport (= 6.1.7.
|
161
|
+
rails (6.1.7.9)
|
162
|
+
actioncable (= 6.1.7.9)
|
163
|
+
actionmailbox (= 6.1.7.9)
|
164
|
+
actionmailer (= 6.1.7.9)
|
165
|
+
actionpack (= 6.1.7.9)
|
166
|
+
actiontext (= 6.1.7.9)
|
167
|
+
actionview (= 6.1.7.9)
|
168
|
+
activejob (= 6.1.7.9)
|
169
|
+
activemodel (= 6.1.7.9)
|
170
|
+
activerecord (= 6.1.7.9)
|
171
|
+
activestorage (= 6.1.7.9)
|
172
|
+
activesupport (= 6.1.7.9)
|
173
173
|
bundler (>= 1.15.0)
|
174
|
-
railties (= 6.1.7.
|
174
|
+
railties (= 6.1.7.9)
|
175
175
|
sprockets-rails (>= 2.0.0)
|
176
176
|
rails-controller-testing (1.0.5)
|
177
177
|
actionpack (>= 5.0.1.rc1)
|
@@ -184,9 +184,9 @@ GEM
|
|
184
184
|
rails-html-sanitizer (1.6.0)
|
185
185
|
loofah (~> 2.21)
|
186
186
|
nokogiri (~> 1.14)
|
187
|
-
railties (6.1.7.
|
188
|
-
actionpack (= 6.1.7.
|
189
|
-
activesupport (= 6.1.7.
|
187
|
+
railties (6.1.7.9)
|
188
|
+
actionpack (= 6.1.7.9)
|
189
|
+
activesupport (= 6.1.7.9)
|
190
190
|
method_source
|
191
191
|
rake (>= 12.2)
|
192
192
|
thor (~> 1.0)
|
@@ -196,8 +196,7 @@ GEM
|
|
196
196
|
redirect_safely (1.0.0)
|
197
197
|
activemodel
|
198
198
|
regexp_parser (2.9.0)
|
199
|
-
rexml (3.3.
|
200
|
-
strscan
|
199
|
+
rexml (3.3.9)
|
201
200
|
rubocop (1.62.1)
|
202
201
|
json (~> 2.3)
|
203
202
|
language_server-protocol (>= 3.17.0)
|
@@ -220,7 +219,7 @@ GEM
|
|
220
219
|
ruby-progressbar (1.13.0)
|
221
220
|
ruby2_keywords (0.0.5)
|
222
221
|
securerandom (0.2.2)
|
223
|
-
shopify_api (14.
|
222
|
+
shopify_api (14.7.0)
|
224
223
|
activesupport
|
225
224
|
concurrent-ruby
|
226
225
|
hash_diff
|
@@ -242,11 +241,10 @@ GEM
|
|
242
241
|
sqlite3 (1.7.3-arm64-darwin)
|
243
242
|
sqlite3 (1.7.3-x86_64-darwin)
|
244
243
|
sqlite3 (1.7.3-x86_64-linux)
|
245
|
-
strscan (3.1.0)
|
246
244
|
syntax_tree (6.1.1)
|
247
245
|
prettier_print (>= 1.2.0)
|
248
246
|
thor (1.2.2)
|
249
|
-
timeout (0.
|
247
|
+
timeout (0.4.1)
|
250
248
|
tzinfo (2.0.6)
|
251
249
|
concurrent-ruby (~> 1.0)
|
252
250
|
unicode-display_width (2.5.0)
|
@@ -257,7 +255,7 @@ GEM
|
|
257
255
|
websocket-driver (0.7.5)
|
258
256
|
websocket-extensions (>= 0.1.0)
|
259
257
|
websocket-extensions (0.1.5)
|
260
|
-
zeitwerk (2.6.
|
258
|
+
zeitwerk (2.6.18)
|
261
259
|
|
262
260
|
PLATFORMS
|
263
261
|
arm64-darwin-21
|
data/README.md
CHANGED
@@ -169,10 +169,11 @@ ShopifyApp.configure do |config|
|
|
169
169
|
end
|
170
170
|
|
171
171
|
```
|
172
|
-
3. Handle special callback logic. If your app has overridden the OAuth CallbackController to run special tasks post authorization,
|
172
|
+
3. Handle special callback logic. If your app has overridden the OAuth CallbackController to run special tasks post authorization,
|
173
173
|
you'll need to create and configure a custom PostAuthenticateTasks class to run these tasks after the token exchange. The original
|
174
174
|
OAuth CallbackController will not be triggered anymore. See [Post Authenticate Tasks documentation](/docs/shopify_app/authentication.md#post-authenticate-tasks) for more information.
|
175
|
-
4.
|
175
|
+
4. Make sure your `embedded_app` layout is correct. If your app has any controller which includes `ShopifyApp::EnsureInstalled`, they will now also include the `ShopifyApp::EmbeddedApp` concern, which sets `layout 'embedded_app'` for the current controller by default. In cases where the controller originally looked for another layout file, this can cause unexpected behavior. See [`EmbeddedApp` concern's documentation](/docs/shopify_app/controller-concerns.md#embeddedapp) for more information on the effects of this concern and how to disable the layout change if needed.
|
176
|
+
5. Enjoy a smoother and faster app installation process.
|
176
177
|
|
177
178
|
### API Versioning
|
178
179
|
|
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).
|
@@ -63,6 +63,26 @@ Using token exchange will ensure that the access token retrieved will always hav
|
|
63
63
|
Authorization code grant flow is the OAuth flow that requires the app to redirect the user
|
64
64
|
to Shopify for installation/authorization of the app to access the shop's data. It is still required for apps that are not embedded.
|
65
65
|
|
66
|
+
If your app is not using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation) with declared scopes in your `.toml` file, you can change the requested access scopes during OAuth flow
|
67
|
+
by adding the `scope` to your configurations - `ShopifyApp.configuration` & `ShopifyAPI::Context.setup`.
|
68
|
+
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
# config/initializers/shopify_app.rb
|
72
|
+
|
73
|
+
ShopifyApp.configure do |config|
|
74
|
+
...
|
75
|
+
config.scope = ["read_discounts", "write_products"]
|
76
|
+
...
|
77
|
+
end
|
78
|
+
|
79
|
+
ShopifyAPI::Context.setup(
|
80
|
+
...
|
81
|
+
scope: ShopifyApp.configuration.scope,
|
82
|
+
...
|
83
|
+
)
|
84
|
+
```
|
85
|
+
|
66
86
|
To perform [authorization code grant flow](https://shopify.dev/docs/apps/auth/get-access-tokens/authorization-code-grant), you app will need to handle
|
67
87
|
[begin OAuth](#begin-oauth) and [OAuth callback](#oauth-callback) routes.
|
68
88
|
|
@@ -195,6 +215,17 @@ config.secret = Rails.application.secrets.shopify_secret
|
|
195
215
|
config.old_secret = Rails.application.secrets.old_shopify_secret
|
196
216
|
```
|
197
217
|
|
218
|
+
Also make sure the old secret is specified when setting up `ShopifyAPI::Context` as well:
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
ShopifyAPI::Context.setup(
|
222
|
+
api_key: ShopifyApp.configuration.api_key,
|
223
|
+
api_secret_key: ShopifyApp.configuration.secret,
|
224
|
+
# ...
|
225
|
+
old_api_secret_key: ShopifyApp.configuration.old_secret,
|
226
|
+
)
|
227
|
+
```
|
228
|
+
|
198
229
|
We've provided a generator which creates the job and an example rake task:
|
199
230
|
|
200
231
|
```sh
|
@@ -2,13 +2,26 @@
|
|
2
2
|
|
3
3
|
#### Table of contents
|
4
4
|
|
5
|
-
[
|
5
|
+
[App-specific webhooks in shopify.app.toml (recommended)](#subscribing-to-webhooks-in-the-app-configuration-file)
|
6
|
+
[Manage shop-specific webhooks using `ShopifyApp::WebhooksManager`](#manage-webhooks-using-shopifyappwebhooksmanager)
|
6
7
|
[Mandatory Privacy Webhooks](#mandatory-privacy-webhooks)
|
7
8
|
|
8
|
-
##
|
9
|
+
## App-specific webhooks in shopify.app.toml (recommended)
|
10
|
+
You can specify app-specific webhooks to subscribe to in the `shopify.app.toml` file. These subscriptions are easier to manage because they are kept up to date by Shopify. In many cases they will be sufficient. Please read [app-specific vs shop-specific subscriptions](https://shopify.dev/docs/apps/build/webhooks/subscribe#app-specific-vs-shop-specific-subscriptions) to understand when you might need shop-specific webhooks.
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
+
### Consuming app-specific webhooks events
|
13
|
+
To consume app-specific webhooks events from 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
|
+
|
21
|
+
## Manage shop-specific webhooks using `ShopifyApp::WebhooksManager`
|
22
|
+
|
23
|
+
See [`ShopifyApp::WebhooksManager`](/lib/shopify_app/managers/webhooks_manager.rb).
|
24
|
+
ShopifyApp can manage your app's shop-specific webhooks for you if you set which webhooks you require in the initializer:
|
12
25
|
|
13
26
|
```ruby
|
14
27
|
ShopifyApp.configure do |config|
|
@@ -18,6 +31,8 @@ ShopifyApp.configure do |config|
|
|
18
31
|
end
|
19
32
|
```
|
20
33
|
|
34
|
+
This method should only be used if you have a good reason not to use app-specific webhooks (such as requiring different topics for different shops).
|
35
|
+
|
21
36
|
When the [OAuth callback](/docs/shopify_app/authentication.md#oauth-callback) or token exchange is completed successfully, ShopifyApp will queue a background job which will ensure all the specified webhooks exist for that shop. Because this runs on every OAuth callback, it means your app will always have the webhooks it needs even if the user uninstalls and re-installs the app.
|
22
37
|
|
23
38
|
ShopifyApp also provides a [WebhooksController](/app/controllers/shopify_app/webhooks_controller.rb) that receives webhooks and queues a job based on the received topic. For example, if you register the webhook from above, then all you need to do is create a job called `CartsUpdateJob`. The job will be queued with 2 params: `shop_domain` and `webhook` (which is the webhook body).
|
@@ -47,13 +62,27 @@ ShopifyApp.configure do |config|
|
|
47
62
|
config.webhooks = [
|
48
63
|
{
|
49
64
|
topic: 'orders/create',
|
50
|
-
path: 'api/webhooks/
|
65
|
+
path: 'api/webhooks/orders_create',
|
51
66
|
metafield_namespaces: ['app-namespace'],
|
52
67
|
},
|
53
68
|
]
|
54
69
|
end
|
55
70
|
```
|
56
71
|
|
72
|
+
If you need to filter by webhook fields, you can register a webhook with a `filter` parameter. The documentation for Webhook filters can be found [here](https://shopify.dev/docs/apps/build/webhooks/customize/filters).
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
ShopifyApp.configure do |config|
|
76
|
+
config.webhooks = [
|
77
|
+
{
|
78
|
+
topic: 'products/update',
|
79
|
+
path: 'api/webhooks/products_update',
|
80
|
+
filter: "variants.price:>=10.00",
|
81
|
+
},
|
82
|
+
]
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
57
86
|
If you'd rather implement your own controller then you'll want to use the [`ShopifyApp::WebhookVerification`](/lib/shopify_app/controller_concerns/webhook_verification.rb) module to verify your webhooks, example:
|
58
87
|
|
59
88
|
```ruby
|
@@ -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,9 +57,11 @@ 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?
|
64
|
+
@scope = []
|
60
65
|
|
61
66
|
log_v23_deprecations
|
62
67
|
end
|
@@ -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
|
@@ -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
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "shopify_app",
|
3
|
-
"version": "22.
|
3
|
+
"version": "22.5.0",
|
4
4
|
"repository": "git@github.com:Shopify/shopify_app.git",
|
5
5
|
"author": "Shopify",
|
6
6
|
"license": "MIT",
|
@@ -18,7 +18,7 @@
|
|
18
18
|
"mocha": "^10.2.0",
|
19
19
|
"sinon": "^9.0.3",
|
20
20
|
"sinon-chai": "^3.2.0",
|
21
|
-
"webpack": "^5.
|
21
|
+
"webpack": "^5.94.0"
|
22
22
|
},
|
23
23
|
"scripts": {
|
24
24
|
"test": "./node_modules/.bin/karma start --browsers ChromeHeadless --single-run"
|