shopify_app 18.1.2 → 19.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +2 -2
  3. data/.gitignore +1 -0
  4. data/CHANGELOG.md +5 -0
  5. data/Gemfile +3 -2
  6. data/Gemfile.lock +120 -134
  7. data/Rakefile +4 -3
  8. data/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb +1 -1
  9. data/app/controllers/shopify_app/authenticated_controller.rb +1 -0
  10. data/app/controllers/shopify_app/callback_controller.rb +35 -147
  11. data/app/controllers/shopify_app/sessions_controller.rb +25 -137
  12. data/app/controllers/shopify_app/webhooks_controller.rb +5 -23
  13. data/config/routes.rb +6 -12
  14. data/docs/Troubleshooting.md +0 -3
  15. data/docs/Upgrading.md +87 -5
  16. data/docs/shopify_app/webhooks.md +1 -1
  17. data/lib/generators/shopify_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb +10 -9
  18. data/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb +1 -0
  19. data/lib/generators/shopify_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +4 -3
  20. data/lib/generators/shopify_app/add_webhook/add_webhook_generator.rb +13 -12
  21. data/lib/generators/shopify_app/add_webhook/templates/webhook_job.rb.tt +9 -1
  22. data/lib/generators/shopify_app/app_proxy_controller/app_proxy_controller_generator.rb +7 -6
  23. data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_controller.rb +2 -1
  24. data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_route.rb +1 -1
  25. data/lib/generators/shopify_app/authenticated_controller/authenticated_controller_generator.rb +3 -3
  26. data/lib/generators/shopify_app/controllers/controllers_generator.rb +4 -3
  27. data/lib/generators/shopify_app/home_controller/home_controller_generator.rb +11 -15
  28. data/lib/generators/shopify_app/home_controller/templates/home_controller.rb +2 -2
  29. data/lib/generators/shopify_app/home_controller/templates/index.html.erb +3 -3
  30. data/lib/generators/shopify_app/install/install_generator.rb +25 -74
  31. data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +1 -1
  32. data/lib/generators/shopify_app/install/templates/session_store.rb +2 -1
  33. data/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +20 -5
  34. data/lib/generators/shopify_app/products_controller/products_controller_generator.rb +3 -3
  35. data/lib/generators/shopify_app/products_controller/templates/products_controller.rb +1 -1
  36. data/lib/generators/shopify_app/rotate_shopify_token_job/rotate_shopify_token_job_generator.rb +4 -4
  37. data/lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token.rake +1 -0
  38. data/lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token_job.rb +1 -1
  39. data/lib/generators/shopify_app/routes/routes_generator.rb +6 -5
  40. data/lib/generators/shopify_app/routes/templates/routes.rb +5 -5
  41. data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +11 -10
  42. data/lib/generators/shopify_app/shop_model/templates/shop.rb +1 -0
  43. data/lib/generators/shopify_app/shopify_app_generator.rb +4 -3
  44. data/lib/generators/shopify_app/user_model/templates/user.rb +1 -0
  45. data/lib/generators/shopify_app/user_model/user_model_generator.rb +11 -10
  46. data/lib/generators/shopify_app/views/views_generator.rb +4 -3
  47. data/lib/shopify_app/access_scopes/shop_strategy.rb +2 -2
  48. data/lib/shopify_app/access_scopes/user_strategy.rb +4 -4
  49. data/lib/shopify_app/configuration.rb +5 -17
  50. data/lib/shopify_app/controller_concerns/app_proxy_verification.rb +4 -3
  51. data/lib/shopify_app/controller_concerns/csrf_protection.rb +2 -1
  52. data/lib/shopify_app/controller_concerns/embedded_app.rb +4 -3
  53. data/lib/shopify_app/controller_concerns/itp.rb +3 -3
  54. data/lib/shopify_app/controller_concerns/localization.rb +1 -0
  55. data/lib/shopify_app/controller_concerns/login_protection.rb +50 -70
  56. data/lib/shopify_app/controller_concerns/payload_verification.rb +3 -2
  57. data/lib/shopify_app/controller_concerns/webhook_verification.rb +2 -1
  58. data/lib/shopify_app/engine.rb +7 -15
  59. data/lib/shopify_app/jobs/scripttags_manager_job.rb +2 -2
  60. data/lib/shopify_app/jobs/webhooks_manager_job.rb +4 -5
  61. data/lib/shopify_app/managers/scripttags_manager.rb +11 -4
  62. data/lib/shopify_app/managers/webhooks_manager.rb +42 -44
  63. data/lib/shopify_app/middleware/jwt_middleware.rb +5 -4
  64. data/lib/shopify_app/session/in_memory_session_store.rb +1 -0
  65. data/lib/shopify_app/session/in_memory_shop_session_store.rb +2 -1
  66. data/lib/shopify_app/session/in_memory_user_session_store.rb +1 -0
  67. data/lib/shopify_app/session/jwt.rb +9 -8
  68. data/lib/shopify_app/session/null_user_session_store.rb +2 -1
  69. data/lib/shopify_app/session/session_repository.rb +37 -0
  70. data/lib/shopify_app/session/session_storage.rb +4 -6
  71. data/lib/shopify_app/session/shop_session_storage.rb +6 -6
  72. data/lib/shopify_app/session/shop_session_storage_with_scopes.rb +7 -8
  73. data/lib/shopify_app/session/user_session_storage.rb +19 -6
  74. data/lib/shopify_app/session/user_session_storage_with_scopes.rb +21 -8
  75. data/lib/shopify_app/test_helpers/all.rb +2 -1
  76. data/lib/shopify_app/test_helpers/webhook_verification_helper.rb +4 -3
  77. data/lib/shopify_app/utils.rb +2 -9
  78. data/lib/shopify_app/version.rb +2 -1
  79. data/lib/shopify_app.rb +35 -40
  80. data/package.json +1 -1
  81. data/shopify_app.gemspec +21 -20
  82. data/yarn.lock +6 -6
  83. metadata +43 -48
  84. data/lib/generators/shopify_app/install/templates/omniauth.rb +0 -4
  85. data/lib/generators/shopify_app/install/templates/shopify_provider.rb.tt +0 -8
  86. data/lib/generators/shopify_app/install/templates/user_agent.rb +0 -6
  87. data/lib/shopify_app/middleware/same_site_cookie_middleware.rb +0 -34
  88. 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!('X-Frame-Options')
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('.logged_out')
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
- set_user_tokens_option
69
-
70
- if user_agent_can_partition_cookies
71
- authenticate_with_partitioning
39
+ if top_level?
40
+ start_oauth
72
41
  else
73
- authenticate_normally
42
+ redirect_auth_to_top_level
74
43
  end
75
44
  end
76
45
 
77
- def authenticate_normally
78
- if request_storage_access?
79
- redirect_to_request_storage_access
80
- elsif authenticate_in_context?
81
- authenticate_in_context
82
- else
83
- authenticate_at_top_level
84
- end
85
- end
86
-
87
- def authenticate_with_partitioning
88
- if session['shopify.cookies_persist']
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
- ShopifyAPI::Session.temp(
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], '/') if 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('invalid_shop_url')
77
+ flash[:error] = I18n.t("invalid_shop_url")
144
78
  redirect_to(return_address)
145
79
  end
146
80
 
147
- def enable_cookie_access
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 request_storage_access?
173
- return false unless ShopifyApp.configuration.embedded_app?
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
- def webhook_params
17
- params.except(:controller, :action, :type)
18
- end
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 'login' => :new, :as => :login
5
- post 'login' => :create, :as => :authenticate
6
- get 'enable_cookies' => :enable_cookies, :as => :enable_cookies
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 'auth/shopify/callback' => :callback
11
+ get "auth/shopify/callback" => :callback
18
12
  end
19
13
 
20
14
  namespace :webhooks do
21
- post ':type' => :receive
15
+ post ":type" => :receive
22
16
  end
23
17
  end
@@ -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,10 +1,12 @@
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 `v18.1.1`](#upgrading-to-v1811)
7
+ [Upgrading to `v19.0.0`](#upgrading-to-v1900)
8
+
9
+ [Upgrading to `v18.1.2`](#upgrading-to-v1812)
8
10
 
9
11
  [Upgrading to `v17.2.0`](#upgrading-to-v1720)
10
12
 
@@ -14,10 +16,90 @@ 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
17
60
 
18
- ## Upgrading to `v18.1.1`
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
+ ```
100
+ ## Upgrading to `v18.1.2`
19
101
 
20
- Version 18.1.1 replaces the deprecated EASDK redirect with an App Bridge 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.
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.
21
103
 
22
104
  ## Upgrading to `v17.2.0`
23
105
 
@@ -127,7 +209,7 @@ is changed to
127
209
 
128
210
  ### ShopifyAPI changes
129
211
 
130
- 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.
131
213
 
132
214
  [dashboard]:https://partners.shopify.com
133
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 https://example.com/webhooks/carts_update
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.
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
- require 'rails/generators/base'
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('../templates', __FILE__)
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
- "{ job: \"Shopify::AfterAuthenticateJob\", inline: false }\n"
19
+ "{ job: \"Shopify::AfterAuthenticateJob\", inline: false }\n"
19
20
 
20
21
  inject_into_file(
21
- 'config/initializers/shopify_app.rb',
22
+ "config/initializers/shopify_app.rb",
22
23
  after_authenticate_job_config,
23
- before: 'end'
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
- "#{after_authenticate_job_config}", :red)
29
+ "#{after_authenticate_job_config}", :red)
29
30
  end
30
31
  end
31
32
 
32
33
  def add_after_authenticate_job
33
- template('after_authenticate_job.rb', "app/jobs/#{job_file_name}_job.rb")
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, 'config/initializers/shopify_app.rb'))
40
+ File.read(File.join(destination_root, "config/initializers/shopify_app.rb"))
40
41
  end
41
42
 
42
43
  def job_file_name
43
- 'shopify/after_authenticate'
44
+ "shopify/after_authenticate"
44
45
  end
45
46
  end
46
47
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Shopify
3
4
  class AfterAuthenticateJob < ActiveJob::Base
4
5
  def perform(shop_domain:)
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
- require 'rails/generators/base'
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('../templates', __FILE__)
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
- 'config/routes.rb',
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
- require 'rails/generators/base'
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('../templates', __FILE__)
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
- 'config/initializers/shopify_app.rb',
21
+ "config/initializers/shopify_app.rb",
21
22
  " config.webhooks = [\n ]\n",
22
- before: 'end'
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
- 'config/initializers/shopify_app.rb',
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 + '_job'
42
+ @job_file_name = job_file_name + "_job"
42
43
  @job_class_name = @job_file_name.classify
43
- template('webhook_job.rb', "app/jobs/#{@job_file_name}.rb")
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('/').last
50
+ address.split("/").last
50
51
  end
51
52
 
52
53
  def load_initializer
53
- File.read(File.join(destination_root, 'config/initializers/shopify_app.rb'))
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: '#{topic}', address: '#{address}', format: 'json'},"
58
+ "\n { topic: \"#{topic}\", address: \"#{address}\" },"
58
59
  end
59
60
 
60
61
  def topic
61
- options['topic']
62
+ options["topic"]
62
63
  end
63
64
 
64
65
  def address
65
- options['address']
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
- def perform(shop_domain:, webhook:)
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
- require 'rails/generators/base'
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('../templates', __FILE__)
8
+ source_root File.expand_path("../templates", __FILE__)
8
9
 
9
10
  def create_app_proxy_controller
10
- template('app_proxy_controller.rb', 'app/controllers/app_proxy_controller.rb')
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('index.html.erb', 'app/views/app_proxy/index.html.erb')
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
- 'config/routes.rb',
20
- File.read(File.expand_path(find_in_source_paths('app_proxy_route.rb'))),
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: 'application/liquid')
7
+ render(layout: false, content_type: "application/liquid")
7
8
  end
8
9
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  namespace :app_proxy do
4
- root action: 'index'
4
+ root action: "index"
5
5
  # simple routes without a specified controller will go to AppProxyController
6
6
 
7
7
  # more complex routes will go to controllers in the AppProxy namespace
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rails/generators/base'
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('../templates', __FILE__)
8
+ source_root File.expand_path("../templates", __FILE__)
9
9
 
10
10
  def create_authenticated_controller
11
- template('authenticated_controller.rb', 'app/controllers/authenticated_controller.rb')
11
+ template("authenticated_controller.rb", "app/controllers/authenticated_controller.rb")
12
12
  end
13
13
  end
14
14
  end