shopify_app 18.1.3 → 19.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +2 -2
- data/.gitignore +1 -0
- data/CHANGELOG.md +3 -2
- data/Gemfile +3 -2
- data/Gemfile.lock +122 -136
- data/Rakefile +4 -3
- data/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb +1 -1
- data/app/controllers/shopify_app/authenticated_controller.rb +1 -0
- data/app/controllers/shopify_app/callback_controller.rb +35 -147
- data/app/controllers/shopify_app/sessions_controller.rb +25 -137
- data/app/controllers/shopify_app/webhooks_controller.rb +5 -23
- data/config/routes.rb +6 -12
- data/docs/Troubleshooting.md +0 -3
- data/docs/Upgrading.md +85 -2
- data/docs/shopify_app/webhooks.md +1 -1
- data/lib/generators/shopify_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb +10 -9
- data/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb +1 -0
- data/lib/generators/shopify_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +4 -3
- data/lib/generators/shopify_app/add_webhook/add_webhook_generator.rb +13 -12
- data/lib/generators/shopify_app/add_webhook/templates/webhook_job.rb.tt +9 -1
- data/lib/generators/shopify_app/app_proxy_controller/app_proxy_controller_generator.rb +7 -6
- data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_controller.rb +2 -1
- data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_route.rb +1 -1
- data/lib/generators/shopify_app/authenticated_controller/authenticated_controller_generator.rb +3 -3
- data/lib/generators/shopify_app/controllers/controllers_generator.rb +4 -3
- data/lib/generators/shopify_app/home_controller/home_controller_generator.rb +11 -15
- data/lib/generators/shopify_app/home_controller/templates/home_controller.rb +2 -2
- data/lib/generators/shopify_app/home_controller/templates/index.html.erb +3 -3
- data/lib/generators/shopify_app/install/install_generator.rb +25 -74
- data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +1 -1
- data/lib/generators/shopify_app/install/templates/session_store.rb +2 -1
- data/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +20 -5
- data/lib/generators/shopify_app/products_controller/products_controller_generator.rb +3 -3
- data/lib/generators/shopify_app/products_controller/templates/products_controller.rb +1 -1
- data/lib/generators/shopify_app/rotate_shopify_token_job/rotate_shopify_token_job_generator.rb +4 -4
- data/lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token.rake +1 -0
- data/lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token_job.rb +1 -1
- data/lib/generators/shopify_app/routes/routes_generator.rb +6 -5
- data/lib/generators/shopify_app/routes/templates/routes.rb +5 -5
- data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +11 -10
- data/lib/generators/shopify_app/shop_model/templates/shop.rb +1 -0
- data/lib/generators/shopify_app/shopify_app_generator.rb +4 -3
- data/lib/generators/shopify_app/user_model/templates/user.rb +1 -0
- data/lib/generators/shopify_app/user_model/user_model_generator.rb +11 -10
- data/lib/generators/shopify_app/views/views_generator.rb +4 -3
- data/lib/shopify_app/access_scopes/shop_strategy.rb +2 -2
- data/lib/shopify_app/access_scopes/user_strategy.rb +4 -4
- data/lib/shopify_app/configuration.rb +5 -17
- data/lib/shopify_app/controller_concerns/app_proxy_verification.rb +4 -3
- data/lib/shopify_app/controller_concerns/csrf_protection.rb +2 -1
- data/lib/shopify_app/controller_concerns/embedded_app.rb +4 -3
- data/lib/shopify_app/controller_concerns/itp.rb +3 -3
- data/lib/shopify_app/controller_concerns/localization.rb +1 -0
- data/lib/shopify_app/controller_concerns/login_protection.rb +50 -70
- data/lib/shopify_app/controller_concerns/payload_verification.rb +3 -2
- data/lib/shopify_app/controller_concerns/webhook_verification.rb +2 -1
- data/lib/shopify_app/engine.rb +7 -15
- data/lib/shopify_app/jobs/scripttags_manager_job.rb +2 -2
- data/lib/shopify_app/jobs/webhooks_manager_job.rb +4 -5
- data/lib/shopify_app/managers/scripttags_manager.rb +11 -4
- data/lib/shopify_app/managers/webhooks_manager.rb +42 -44
- data/lib/shopify_app/middleware/jwt_middleware.rb +5 -4
- data/lib/shopify_app/session/in_memory_session_store.rb +1 -0
- data/lib/shopify_app/session/in_memory_shop_session_store.rb +2 -1
- data/lib/shopify_app/session/in_memory_user_session_store.rb +1 -0
- data/lib/shopify_app/session/jwt.rb +9 -8
- data/lib/shopify_app/session/null_user_session_store.rb +2 -1
- data/lib/shopify_app/session/session_repository.rb +37 -0
- data/lib/shopify_app/session/session_storage.rb +4 -6
- data/lib/shopify_app/session/shop_session_storage.rb +6 -6
- data/lib/shopify_app/session/shop_session_storage_with_scopes.rb +7 -8
- data/lib/shopify_app/session/user_session_storage.rb +19 -6
- data/lib/shopify_app/session/user_session_storage_with_scopes.rb +21 -8
- data/lib/shopify_app/test_helpers/all.rb +2 -1
- data/lib/shopify_app/test_helpers/webhook_verification_helper.rb +4 -3
- data/lib/shopify_app/utils.rb +2 -9
- data/lib/shopify_app/version.rb +2 -1
- data/lib/shopify_app.rb +35 -40
- data/package.json +1 -1
- data/shopify_app.gemspec +21 -20
- data/yarn.lock +6 -6
- metadata +45 -50
- data/lib/generators/shopify_app/install/templates/omniauth.rb +0 -4
- data/lib/generators/shopify_app/install/templates/shopify_provider.rb.tt +0 -8
- data/lib/generators/shopify_app/install/templates/user_agent.rb +0 -6
- data/lib/shopify_app/middleware/same_site_cookie_middleware.rb +0 -34
- data/lib/shopify_app/omniauth/omniauth_configuration.rb +0 -64
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
module ShopifyApp
|
|
3
4
|
class SessionsController < ActionController::Base
|
|
4
5
|
include ShopifyApp::LoginProtection
|
|
@@ -6,7 +7,7 @@ module ShopifyApp
|
|
|
6
7
|
layout false, only: :new
|
|
7
8
|
|
|
8
9
|
after_action only: [:new, :create] do |controller|
|
|
9
|
-
controller.response.headers.except!(
|
|
10
|
+
controller.response.headers.except!("X-Frame-Options")
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def new
|
|
@@ -17,43 +18,14 @@ module ShopifyApp
|
|
|
17
18
|
authenticate
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
def enable_cookies
|
|
21
|
-
return unless validate_shop_presence
|
|
22
|
-
|
|
23
|
-
render(:enable_cookies, layout: false, locals: {
|
|
24
|
-
does_not_have_storage_access_url: top_level_interaction_path(
|
|
25
|
-
shop: sanitized_shop_name,
|
|
26
|
-
host: host,
|
|
27
|
-
return_to: params[:return_to]
|
|
28
|
-
),
|
|
29
|
-
has_storage_access_url: login_url_with_optional_shop(top_level: true),
|
|
30
|
-
app_target_url: granted_storage_access_path(
|
|
31
|
-
shop: sanitized_shop_name,
|
|
32
|
-
host: host,
|
|
33
|
-
return_to: params[:return_to]
|
|
34
|
-
),
|
|
35
|
-
current_shopify_domain: current_shopify_domain,
|
|
36
|
-
})
|
|
37
|
-
end
|
|
38
|
-
|
|
39
21
|
def top_level_interaction
|
|
40
22
|
@url = login_url_with_optional_shop(top_level: true)
|
|
41
23
|
validate_shop_presence
|
|
42
24
|
end
|
|
43
25
|
|
|
44
|
-
def granted_storage_access
|
|
45
|
-
return unless validate_shop_presence
|
|
46
|
-
|
|
47
|
-
session['shopify.granted_storage_access'] = true
|
|
48
|
-
|
|
49
|
-
copy_return_to_param_to_session
|
|
50
|
-
|
|
51
|
-
redirect_to(return_address_with_params({ shop: @shop }))
|
|
52
|
-
end
|
|
53
|
-
|
|
54
26
|
def destroy
|
|
55
27
|
reset_session
|
|
56
|
-
flash[:notice] = I18n.t(
|
|
28
|
+
flash[:notice] = I18n.t(".logged_out")
|
|
57
29
|
redirect_to(login_url_with_optional_shop)
|
|
58
30
|
end
|
|
59
31
|
|
|
@@ -61,69 +33,31 @@ module ShopifyApp
|
|
|
61
33
|
|
|
62
34
|
def authenticate
|
|
63
35
|
return render_invalid_shop_error unless sanitized_shop_name.present?
|
|
64
|
-
session['shopify.omniauth_params'] = { shop: sanitized_shop_name }
|
|
65
36
|
|
|
66
37
|
copy_return_to_param_to_session
|
|
67
38
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if user_agent_can_partition_cookies
|
|
71
|
-
authenticate_with_partitioning
|
|
39
|
+
if top_level?
|
|
40
|
+
start_oauth
|
|
72
41
|
else
|
|
73
|
-
|
|
42
|
+
redirect_auth_to_top_level
|
|
74
43
|
end
|
|
75
44
|
end
|
|
76
45
|
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
clear_top_level_oauth_cookie
|
|
90
|
-
authenticate_in_context
|
|
91
|
-
else
|
|
92
|
-
set_top_level_oauth_cookie
|
|
93
|
-
enable_cookie_access
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Override shop_session_by_cookie from LoginProtection to bypass allow_cookie_authentication
|
|
98
|
-
# setting check because session cookies are justified at top level
|
|
99
|
-
def shop_session_by_cookie
|
|
100
|
-
return unless session[:shop_id].present?
|
|
101
|
-
ShopifyApp::SessionRepository.retrieve_shop_session(session[:shop_id])
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# rubocop:disable Lint/SuppressedException
|
|
105
|
-
def set_user_tokens_option
|
|
106
|
-
current_shop_session = shop_session
|
|
107
|
-
|
|
108
|
-
if current_shop_session.blank?
|
|
109
|
-
session[:user_tokens] = false
|
|
110
|
-
return
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
session[:user_tokens] = ShopifyApp::SessionRepository.user_storage.present?
|
|
46
|
+
def start_oauth
|
|
47
|
+
auth_attributes = ShopifyAPI::Auth::Oauth.begin_auth(
|
|
48
|
+
shop: sanitized_shop_name,
|
|
49
|
+
redirect_path: "/auth/shopify/callback",
|
|
50
|
+
is_online: user_session_expected?
|
|
51
|
+
)
|
|
52
|
+
cookies.encrypted[auth_attributes[:cookie].name] = {
|
|
53
|
+
expires: auth_attributes[:cookie].expires,
|
|
54
|
+
secure: true,
|
|
55
|
+
http_only: true,
|
|
56
|
+
value: auth_attributes[:cookie].value,
|
|
57
|
+
}
|
|
114
58
|
|
|
115
|
-
|
|
116
|
-
domain: current_shop_session.domain,
|
|
117
|
-
token: current_shop_session.token,
|
|
118
|
-
api_version: current_shop_session.api_version
|
|
119
|
-
) do
|
|
120
|
-
ShopifyAPI::Metafield.find(:token_validity_bogus_check)
|
|
121
|
-
end
|
|
122
|
-
rescue ActiveResource::UnauthorizedAccess
|
|
123
|
-
session[:user_tokens] = false
|
|
124
|
-
rescue StandardError
|
|
59
|
+
redirect_to(auth_attributes[:auth_route], allow_other_host: true)
|
|
125
60
|
end
|
|
126
|
-
# rubocop:enable Lint/SuppressedException
|
|
127
61
|
|
|
128
62
|
def validate_shop_presence
|
|
129
63
|
@shop = sanitized_shop_name
|
|
@@ -136,67 +70,21 @@ module ShopifyApp
|
|
|
136
70
|
end
|
|
137
71
|
|
|
138
72
|
def copy_return_to_param_to_session
|
|
139
|
-
session[:return_to] = RedirectSafely.make_safe(params[:return_to],
|
|
73
|
+
session[:return_to] = RedirectSafely.make_safe(params[:return_to], "/") if params[:return_to]
|
|
140
74
|
end
|
|
141
75
|
|
|
142
76
|
def render_invalid_shop_error
|
|
143
|
-
flash[:error] = I18n.t(
|
|
77
|
+
flash[:error] = I18n.t("invalid_shop_url")
|
|
144
78
|
redirect_to(return_address)
|
|
145
79
|
end
|
|
146
80
|
|
|
147
|
-
def
|
|
148
|
-
fullpage_redirect_to(enable_cookies_path(
|
|
149
|
-
shop: sanitized_shop_name,
|
|
150
|
-
host: host,
|
|
151
|
-
return_to: session[:return_to]
|
|
152
|
-
))
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def authenticate_in_context
|
|
156
|
-
post_redirect_to_auth_shopify
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def post_redirect_to_auth_shopify
|
|
160
|
-
render('shopify_app/shared/post_redirect_to_auth_shopify', layout: false)
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def authenticate_at_top_level
|
|
164
|
-
fullpage_redirect_to(login_url_with_optional_shop(top_level: true))
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def authenticate_in_context?
|
|
81
|
+
def top_level?
|
|
168
82
|
return true unless ShopifyApp.configuration.embedded_app?
|
|
169
|
-
params[:top_level]
|
|
83
|
+
!params[:top_level].nil?
|
|
170
84
|
end
|
|
171
85
|
|
|
172
|
-
def
|
|
173
|
-
|
|
174
|
-
return false if params[:top_level]
|
|
175
|
-
return false if user_agent_is_mobile
|
|
176
|
-
return false if user_agent_is_pos
|
|
177
|
-
|
|
178
|
-
!session['shopify.granted_storage_access']
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def redirect_to_request_storage_access
|
|
182
|
-
render(
|
|
183
|
-
:request_storage_access,
|
|
184
|
-
layout: false,
|
|
185
|
-
locals: {
|
|
186
|
-
does_not_have_storage_access_url: top_level_interaction_path(
|
|
187
|
-
shop: sanitized_shop_name,
|
|
188
|
-
host: host,
|
|
189
|
-
return_to: session[:return_to]
|
|
190
|
-
),
|
|
191
|
-
has_storage_access_url: login_url_with_optional_shop(top_level: true),
|
|
192
|
-
app_target_url: granted_storage_access_path(
|
|
193
|
-
shop: sanitized_shop_name,
|
|
194
|
-
host: host,
|
|
195
|
-
return_to: session[:return_to]
|
|
196
|
-
),
|
|
197
|
-
current_shopify_domain: current_shopify_domain,
|
|
198
|
-
}
|
|
199
|
-
)
|
|
86
|
+
def redirect_auth_to_top_level
|
|
87
|
+
fullpage_redirect_to(login_url_with_optional_shop(top_level: true))
|
|
200
88
|
end
|
|
201
89
|
end
|
|
202
90
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
module ShopifyApp
|
|
3
4
|
class MissingWebhookJobError < StandardError; end
|
|
4
5
|
|
|
@@ -7,30 +8,11 @@ module ShopifyApp
|
|
|
7
8
|
|
|
8
9
|
def receive
|
|
9
10
|
params.permit!
|
|
10
|
-
webhook_job_klass.perform_later(shop_domain: shop_domain, webhook: webhook_params.to_h)
|
|
11
|
-
head(:ok)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
private
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def webhook_job_klass
|
|
21
|
-
webhook_job_klass_name.safe_constantize || raise(ShopifyApp::MissingWebhookJobError)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def webhook_job_klass_name(type = webhook_type)
|
|
25
|
-
[webhook_namespace, "#{type}_job"].compact.join('/').classify
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def webhook_type
|
|
29
|
-
params[:type]
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def webhook_namespace
|
|
33
|
-
ShopifyApp.configuration.webhook_jobs_namespace
|
|
12
|
+
ShopifyAPI::Webhooks::Registry.process(
|
|
13
|
+
ShopifyAPI::Webhooks::Request.new(raw_body: request.raw_post, headers: request.headers.to_h)
|
|
14
|
+
)
|
|
15
|
+
head(:ok)
|
|
34
16
|
end
|
|
35
17
|
end
|
|
36
18
|
end
|
data/config/routes.rb
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
ShopifyApp::Engine.routes.draw do
|
|
3
4
|
controller :sessions do
|
|
4
|
-
get
|
|
5
|
-
post
|
|
6
|
-
get
|
|
7
|
-
get 'top_level_interaction' =>
|
|
8
|
-
:top_level_interaction,
|
|
9
|
-
:as => :top_level_interaction
|
|
10
|
-
get 'granted_storage_access' =>
|
|
11
|
-
:granted_storage_access,
|
|
12
|
-
:as => :granted_storage_access
|
|
13
|
-
get 'logout' => :destroy, :as => :logout
|
|
5
|
+
get "login" => :new, :as => :login
|
|
6
|
+
post "login" => :create, :as => :authenticate
|
|
7
|
+
get "logout" => :destroy, :as => :logout
|
|
14
8
|
end
|
|
15
9
|
|
|
16
10
|
controller :callback do
|
|
17
|
-
get
|
|
11
|
+
get "auth/shopify/callback" => :callback
|
|
18
12
|
end
|
|
19
13
|
|
|
20
14
|
namespace :webhooks do
|
|
21
|
-
post
|
|
15
|
+
post ":type" => :receive
|
|
22
16
|
end
|
|
23
17
|
end
|
data/docs/Troubleshooting.md
CHANGED
|
@@ -87,9 +87,6 @@ Edit `config/initializer/shopify_app.rb` and ensure the following configurations
|
|
|
87
87
|
```diff
|
|
88
88
|
+ config.embedded_app = true
|
|
89
89
|
|
|
90
|
-
+ config.allow_jwt_authentication = true
|
|
91
|
-
+ config.allow_cookie_authentication = false
|
|
92
|
-
|
|
93
90
|
# This line should already exist if you're using shopify_app gem 13.x.x+
|
|
94
91
|
+ config.shop_session_repository = 'Shop'
|
|
95
92
|
```
|
data/docs/Upgrading.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
# Upgrading
|
|
1
|
+
# Upgrading
|
|
2
2
|
|
|
3
3
|
This file documents important changes needed to upgrade your app's Shopify App version to a new major version.
|
|
4
4
|
|
|
5
5
|
#### Table of contents
|
|
6
6
|
|
|
7
|
+
[Upgrading to `v19.0.0`](#upgrading-to-v1900)
|
|
8
|
+
|
|
7
9
|
[Upgrading to `v18.1.2`](#upgrading-to-v1812)
|
|
8
10
|
|
|
9
11
|
[Upgrading to `v17.2.0`](#upgrading-to-v1720)
|
|
@@ -14,6 +16,87 @@ This file documents important changes needed to upgrade your app's Shopify App v
|
|
|
14
16
|
|
|
15
17
|
[Upgrading from `v8.6` to `v9.0.0`](#upgrading-from-v86-to-v900)
|
|
16
18
|
|
|
19
|
+
## Upgrading to `v19.0.0`
|
|
20
|
+
|
|
21
|
+
This update moves API authentication logic from this gem to the [`shopify_api`](https://github.com/Shopify/shopify_api)
|
|
22
|
+
gem.
|
|
23
|
+
|
|
24
|
+
### High-level process
|
|
25
|
+
|
|
26
|
+
* Delete `config/initializers/omniauth.rb` as apps no longer need to initialize `OmniAuth` directly.
|
|
27
|
+
* Delete `config/initializers/user_agent.rb` as `shopify_app` will set the right `User-Agent` header for interacting
|
|
28
|
+
with the Shopify API. If the app requires further information in the `User-Agent` header beyond what Shopify API
|
|
29
|
+
requires, specify this in the `ShopifyAPI::Context.user_agent_prefix` setting.
|
|
30
|
+
* Remove `allow_jwt_authentication=` and `allow_cookie_authentication=` invocations from
|
|
31
|
+
`config/initializers/shopify_app.rb` as the decision logic for which authentication method to use is now handled
|
|
32
|
+
internally by the `shopify_api` gem, using the `ShopifyAPI::Context.embedded_app` setting.
|
|
33
|
+
* `v19.0.0` updates the `shopify_api` dependency to `10.0.0`. This version of `shopify_api` has breaking changes. See
|
|
34
|
+
the documentation for addressing these breaking changes on GitHub [here](https://github.com/Shopify/shopify_api/blob/add_breaking_change_log_v10/README.md#breaking-change-notice-for-version-1000).
|
|
35
|
+
|
|
36
|
+
### Specific cases
|
|
37
|
+
|
|
38
|
+
#### Webhook Jobs
|
|
39
|
+
|
|
40
|
+
Add a new `handle` method to existing webhook jobs to go through the updated `shopify_api` gem.
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
class MyWebhookJob < ActiveJob::Base
|
|
44
|
+
extend ShopifyAPI::Webhooks::Handler
|
|
45
|
+
|
|
46
|
+
class << self
|
|
47
|
+
# new handle function
|
|
48
|
+
def handle(topic:, shop:, body:)
|
|
49
|
+
# delegate to pre-existing perform_later function
|
|
50
|
+
perform_later(topic: topic, shop_domain: shop, webhook: body)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# original perform function
|
|
55
|
+
def perform(topic:, shop_domain:, webhook:)
|
|
56
|
+
# ...
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
#### Temporary sessions
|
|
60
|
+
|
|
61
|
+
The new `shopify_api` gem offers a utility to temporarily create sessions for interacting with the API within a block.
|
|
62
|
+
This is useful for interacting with the Shopify API outside of the context of a subclass of `AuthenticatedController`.
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
ShopifyAPI::Auth::Session.temp(shop: shop_domain, access_token: shop_token) do |session|
|
|
66
|
+
# make invocations to the API
|
|
67
|
+
end
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Within a subclass of `AuthenticatedController`, the `current_shopify_session` function will return the current active
|
|
71
|
+
Shopify API session, or `nil` if no such session is available.
|
|
72
|
+
|
|
73
|
+
#### Setting up `ShopifyAPI::Context`
|
|
74
|
+
|
|
75
|
+
The `shopify_app` initializer must configure the `ShopifyAPI::Context`. The Rails generator will
|
|
76
|
+
generate a block in the `shopify_app` initializer. To do so manually, ensure the following is
|
|
77
|
+
part of the `after_initialize` block in `shopify_app.rb`.
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
Rails.application.config.after_initialize do
|
|
81
|
+
if ShopifyApp.configuration.api_key.present? && ShopifyApp.configuration.secret.present?
|
|
82
|
+
ShopifyAPI::Context.setup(
|
|
83
|
+
api_key: ShopifyApp.configuration.api_key,
|
|
84
|
+
api_secret_key: ShopifyApp.configuration.secret,
|
|
85
|
+
api_version: ShopifyApp.configuration.api_version,
|
|
86
|
+
host_name: URI(ENV.fetch('HOST', '')).host || '',
|
|
87
|
+
scope: ShopifyApp.configuration.scope,
|
|
88
|
+
is_private: !ENV.fetch('SHOPIFY_APP_PRIVATE_SHOP', '').empty?,
|
|
89
|
+
is_embedded: ShopifyApp.configuration.embedded_app,
|
|
90
|
+
session_storage: ShopifyApp::SessionRepository,
|
|
91
|
+
logger: Rails.logger,
|
|
92
|
+
private_shop: ENV.fetch('SHOPIFY_APP_PRIVATE_SHOP', nil),
|
|
93
|
+
user_agent_prefix: "ShopifyApp/#{ShopifyApp::VERSION}"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
ShopifyApp::WebhooksManager.add_registrations
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
```
|
|
17
100
|
## Upgrading to `v18.1.2`
|
|
18
101
|
|
|
19
102
|
Version 18.1.2 replaces the deprecated EASDK redirect with an App Bridge 2 redirect when attempting to break out of an iframe. This happens when an app is installed, requires new access scopes, or re-authentication because the login session is expired.
|
|
@@ -126,7 +209,7 @@ is changed to
|
|
|
126
209
|
|
|
127
210
|
### ShopifyAPI changes
|
|
128
211
|
|
|
129
|
-
You will need to also follow the ShopifyAPI [upgrade guide](https://github.com/Shopify/shopify_api/blob/master/README.md#-breaking-change-notice-for-version-700-) to ensure your app is ready to work with API versioning.
|
|
212
|
+
You will need to also follow the ShopifyAPI [upgrade guide](https://github.com/Shopify/shopify_api/blob/master/README.md#-breaking-change-notice-for-version-700-) to ensure your app is ready to work with API versioning.
|
|
130
213
|
|
|
131
214
|
[dashboard]:https://partners.shopify.com
|
|
132
215
|
[app-bridge]:https://shopify.dev/apps/tools/app-bridge
|
|
@@ -66,7 +66,7 @@ The WebhooksManager uses ActiveJob. If ActiveJob is not configured then by defau
|
|
|
66
66
|
ShopifyApp can create webhooks for you using the `add_webhook` generator. This will add the new webhook to your config and create the required job class for you.
|
|
67
67
|
|
|
68
68
|
```
|
|
69
|
-
rails g shopify_app:add_webhook -t carts/update -a
|
|
69
|
+
rails g shopify_app:add_webhook -t carts/update -a /webhooks/carts_update
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
Where `-t` is the topic and `-a` is the address the webhook should be sent to.
|
data/lib/generators/shopify_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
require "rails/generators/base"
|
|
3
4
|
|
|
4
5
|
module ShopifyApp
|
|
5
6
|
module Generators
|
|
6
7
|
class AddAfterAuthenticateJobGenerator < Rails::Generators::Base
|
|
7
|
-
source_root File.expand_path(
|
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
|
8
9
|
|
|
9
10
|
hook_for :test_framework, as: :job, in: :rails do |instance, generator|
|
|
10
11
|
instance.invoke(generator, [instance.send(:job_file_name)])
|
|
@@ -15,32 +16,32 @@ module ShopifyApp
|
|
|
15
16
|
|
|
16
17
|
after_authenticate_job_config =
|
|
17
18
|
" config.after_authenticate_job = "\
|
|
18
|
-
|
|
19
|
+
"{ job: \"Shopify::AfterAuthenticateJob\", inline: false }\n"
|
|
19
20
|
|
|
20
21
|
inject_into_file(
|
|
21
|
-
|
|
22
|
+
"config/initializers/shopify_app.rb",
|
|
22
23
|
after_authenticate_job_config,
|
|
23
|
-
before:
|
|
24
|
+
before: "end"
|
|
24
25
|
)
|
|
25
26
|
|
|
26
27
|
unless initializer.include?(after_authenticate_job_config)
|
|
27
28
|
shell.say("Error adding after_authenticate_job to config. Add this line manually: "\
|
|
28
|
-
|
|
29
|
+
"#{after_authenticate_job_config}", :red)
|
|
29
30
|
end
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
def add_after_authenticate_job
|
|
33
|
-
template(
|
|
34
|
+
template("after_authenticate_job.rb", "app/jobs/#{job_file_name}_job.rb")
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
private
|
|
37
38
|
|
|
38
39
|
def load_initializer
|
|
39
|
-
File.read(File.join(destination_root,
|
|
40
|
+
File.read(File.join(destination_root, "config/initializers/shopify_app.rb"))
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
def job_file_name
|
|
43
|
-
|
|
44
|
+
"shopify/after_authenticate"
|
|
44
45
|
end
|
|
45
46
|
end
|
|
46
47
|
end
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
require "rails/generators/base"
|
|
3
4
|
|
|
4
5
|
module ShopifyApp
|
|
5
6
|
module Generators
|
|
6
7
|
class AddMarketingActivityExtensionGenerator < Rails::Generators::Base
|
|
7
|
-
source_root File.expand_path(
|
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
|
8
9
|
|
|
9
10
|
def generate_app_extension
|
|
10
11
|
template("marketing_activities_controller.rb", "app/controllers/marketing_activities_controller.rb")
|
|
@@ -15,7 +16,7 @@ module ShopifyApp
|
|
|
15
16
|
|
|
16
17
|
def generate_routes
|
|
17
18
|
inject_into_file(
|
|
18
|
-
|
|
19
|
+
"config/routes.rb",
|
|
19
20
|
optimize_indentation(routes, 2),
|
|
20
21
|
after: "root :to => 'home#index'\n"
|
|
21
22
|
)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
require "rails/generators/base"
|
|
3
4
|
|
|
4
5
|
module ShopifyApp
|
|
5
6
|
module Generators
|
|
6
7
|
class AddWebhookGenerator < Rails::Generators::Base
|
|
7
|
-
source_root File.expand_path(
|
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
|
8
9
|
class_option :topic, type: :string, aliases: "-t", required: true
|
|
9
10
|
class_option :address, type: :string, aliases: "-a", required: true
|
|
10
11
|
|
|
@@ -17,15 +18,15 @@ module ShopifyApp
|
|
|
17
18
|
return if initializer.include?("config.webhooks")
|
|
18
19
|
|
|
19
20
|
inject_into_file(
|
|
20
|
-
|
|
21
|
+
"config/initializers/shopify_app.rb",
|
|
21
22
|
" config.webhooks = [\n ]\n",
|
|
22
|
-
|
|
23
|
+
after: /ShopifyApp\.configure.*\n/
|
|
23
24
|
)
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
def inject_webhook_to_shopify_app_initializer
|
|
27
28
|
inject_into_file(
|
|
28
|
-
|
|
29
|
+
"config/initializers/shopify_app.rb",
|
|
29
30
|
webhook_config,
|
|
30
31
|
after: "config.webhooks = ["
|
|
31
32
|
)
|
|
@@ -38,31 +39,31 @@ module ShopifyApp
|
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def add_webhook_job
|
|
41
|
-
@job_file_name = job_file_name +
|
|
42
|
+
@job_file_name = job_file_name + "_job"
|
|
42
43
|
@job_class_name = @job_file_name.classify
|
|
43
|
-
template(
|
|
44
|
+
template("webhook_job.rb", "app/jobs/#{@job_file_name}.rb")
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
private
|
|
47
48
|
|
|
48
49
|
def job_file_name
|
|
49
|
-
address.split(
|
|
50
|
+
address.split("/").last
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
def load_initializer
|
|
53
|
-
File.read(File.join(destination_root,
|
|
54
|
+
File.read(File.join(destination_root, "config/initializers/shopify_app.rb"))
|
|
54
55
|
end
|
|
55
56
|
|
|
56
57
|
def webhook_config
|
|
57
|
-
"\n {topic:
|
|
58
|
+
"\n { topic: \"#{topic}\", address: \"#{address}\" },"
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
def topic
|
|
61
|
-
options[
|
|
62
|
+
options["topic"]
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
def address
|
|
65
|
-
options[
|
|
66
|
+
options["address"]
|
|
66
67
|
end
|
|
67
68
|
end
|
|
68
69
|
end
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
class <%= @job_class_name %> < ActiveJob::Base
|
|
2
|
-
|
|
2
|
+
extend ShopifyAPI::Webhooks::Handler
|
|
3
|
+
|
|
4
|
+
class << self
|
|
5
|
+
def handle(topic:, shop:, body:)
|
|
6
|
+
perform_later(topic: topic, shop_domain: shop, webhook: body)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def perform(topic:, shop_domain:, webhook:)
|
|
3
11
|
shop = Shop.find_by(shopify_domain: shop_domain)
|
|
4
12
|
|
|
5
13
|
if shop.nil?
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
require "rails/generators/base"
|
|
3
4
|
|
|
4
5
|
module ShopifyApp
|
|
5
6
|
module Generators
|
|
6
7
|
class AppProxyControllerGenerator < Rails::Generators::Base
|
|
7
|
-
source_root File.expand_path(
|
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
|
8
9
|
|
|
9
10
|
def create_app_proxy_controller
|
|
10
|
-
template(
|
|
11
|
+
template("app_proxy_controller.rb", "app/controllers/app_proxy_controller.rb")
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def create_app_proxy_index_view
|
|
14
|
-
copy_file(
|
|
15
|
+
copy_file("index.html.erb", "app/views/app_proxy/index.html.erb")
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def add_app_proxy_route
|
|
18
19
|
inject_into_file(
|
|
19
|
-
|
|
20
|
-
File.read(File.expand_path(find_in_source_paths(
|
|
20
|
+
"config/routes.rb",
|
|
21
|
+
File.read(File.expand_path(find_in_source_paths("app_proxy_route.rb"))),
|
|
21
22
|
after: "mount ShopifyApp::Engine, at: '/'\n"
|
|
22
23
|
)
|
|
23
24
|
end
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
class AppProxyController < ApplicationController
|
|
3
4
|
include ShopifyApp::AppProxyVerification
|
|
4
5
|
|
|
5
6
|
def index
|
|
6
|
-
render(layout: false, content_type:
|
|
7
|
+
render(layout: false, content_type: "application/liquid")
|
|
7
8
|
end
|
|
8
9
|
end
|
data/lib/generators/shopify_app/authenticated_controller/authenticated_controller_generator.rb
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "rails/generators/base"
|
|
4
4
|
|
|
5
5
|
module ShopifyApp
|
|
6
6
|
module Generators
|
|
7
7
|
class AuthenticatedControllerGenerator < Rails::Generators::Base
|
|
8
|
-
source_root File.expand_path(
|
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
|
9
9
|
|
|
10
10
|
def create_authenticated_controller
|
|
11
|
-
template(
|
|
11
|
+
template("authenticated_controller.rb", "app/controllers/authenticated_controller.rb")
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
end
|