shopify_app 7.2.0 → 7.2.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 47504ce67fc91bae133ac9451f21eb46b2307f6b
4
- data.tar.gz: 1c9430ae87f14834389188ea630a43574e1d31c4
3
+ metadata.gz: a6802e72897eb705326a9cc6639506aa5eefb912
4
+ data.tar.gz: 76ae92534abcbbc635adcab460e6d7b72898a5ca
5
5
  SHA512:
6
- metadata.gz: dce462b93f79c591d00801bf8eaedec76be7fcee1aa65184d6d2cad28480c438c92b2b4e1239382225f7782eb62bf735f941ca1c6bed166b0a016a4b7ca9d055
7
- data.tar.gz: f5e656f7f0c720daa423d20dea3bddc47439e556713d6bfa8cca030ef12617ac64bbdeedb4541dd85dc311b229231662635f1ea6994b18ed761806d18a24207a
6
+ metadata.gz: 22cb00e95696ddfce4f64cd77cbe422de626e17e654b151d89c83a2d0813469a4565f51725f848c557e70b58275f2dfb1f40e0600d2bd03f787fa97d38325ae7
7
+ data.tar.gz: fffd5d0b94281792e7911091060654f06192387b9a5d08f336a6ec18725af3094a65c4ff5d9e05771dd0fba7d326d04b17572f305d68a2f6cef93fe0cb30b49b
@@ -1,3 +1,13 @@
1
+ 7.2.2
2
+ -----
3
+ * Use postMessage to redirect parent iframe during authentication
4
+ https://github.com/Shopify/shopify_app/pull/366
5
+
6
+ 7.2.1
7
+ -----
8
+ * Add support for dynamically generating scripttag URLs
9
+ * Bug-fix: `--application_name` and `--scope` generates proper Configuration even when options supplied to them contain whitespaces.
10
+
1
11
  7.2.0
2
12
  -----
3
13
  * Disable application layout rendering for the `/login` page
data/README.md CHANGED
@@ -127,8 +127,10 @@ $ rails generate shopify_app:install --api_key <your_api_key> --secret <your_app
127
127
  ```
128
128
 
129
129
  Other options include:
130
- * `application_name` - the name of your app
131
- * `scope` - the Oauth access scope required for your app, eg 'read_products, write_orders'. For more information read the [docs](http://docs.shopify.com/api/tutorials/oauth)
130
+ * `application_name` - the name of your app, it can be supplied with or without double-quotes if a whitespace is present. (e.g. `--application_name Example App` or `--application_name "Example App"`)
131
+ * `scope` - the Oauth access scope required for your app, eg **read_products, write_orders**. *Multiple options* need to be delimited by a comma-space, and can be supplied with or without double-quotes
132
+ (e.g. `--scope read_products, write_orders, write_products` or `--scope "read_products, write_orders, write_products"`)
133
+ For more information, refer the [docs](http://docs.shopify.com/api/tutorials/oauth).
132
134
  * `embedded` - the default is to generate an [embedded app](http://docs.shopify.com/embedded-app-sdk), if you want a legacy non-embedded app then set this to false, `--embedded false`
133
135
 
134
136
  You can update any of these settings later on easily, the arguments are simply for convenience.
@@ -225,7 +227,7 @@ ShopifyApp can manage your app's webhooks for you by setting which webhooks you
225
227
  ```ruby
226
228
  ShopifyApp.configure do |config|
227
229
  config.webhooks = [
228
- {topic: 'carts/update', address: 'example-app.com/webhooks/carts_update'}
230
+ {topic: 'carts/update', address: 'https://example-app.com/webhooks/carts_update'}
229
231
  ]
230
232
  end
231
233
  ```
@@ -234,6 +236,16 @@ When the oauth callback is completed successfully ShopifyApp will queue a backgr
234
236
 
235
237
  ShopifyApp also provides a WebhooksController that receives webhooks and queues a job based on the webhook url. 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.
236
238
 
239
+ If you are only interested in particular fields, you can optionally filter the data sent by Shopify by specifying the `fields` parameter in `config/webhooks`. Note that you will still receive a webhook request from Shopify every time the resource is updated, but only the specified fields will be sent.
240
+
241
+ ```ruby
242
+ ShopifyApp.configure do |config|
243
+ config.webhooks = [
244
+ {topic: 'products/update', address: 'https://example-app.com/webhooks/products_update', fields: ['title', 'vendor']}
245
+ ]
246
+ end
247
+ ```
248
+
237
249
  If you'd rather implement your own controller then you'll want to use the WebhookVerfication module to verify your webhooks:
238
250
 
239
251
  ```ruby
@@ -268,12 +280,15 @@ As with webhooks, ShopifyApp can manage your app's scripttags for you by setting
268
280
  ShopifyApp.configure do |config|
269
281
  config.scripttags = [
270
282
  {event:'onload', src: 'https://my-shopifyapp.herokuapp.com/fancy.js'}
283
+ {event:'onload', src: ->(domain) { dynamic_tag_url(domain) } }
271
284
  ]
272
285
  end
273
286
  ```
274
287
 
275
288
  Scripttags are created in the same way as the Webhooks, with a background job which will create the required scripttags.
276
289
 
290
+ If `src` responds to `call` its return value will be used as the scripttag's source. It will be called on scripttag creation and deletion.
291
+
277
292
  ShopifyApp::SessionRepository
278
293
  -----------------------------
279
294
 
@@ -310,15 +325,7 @@ Create your app proxy url in the [Shopify Partners' Dashboard](https://app.shopi
310
325
  Troubleshooting
311
326
  ---------------
312
327
 
313
- ### Generator shopify_app:install hangs
314
-
315
- Rails uses spring by default to speed up development. To run the generator, spring has to be stopped:
316
-
317
- ```sh
318
- $ bundle exec spring stop
319
- ```
320
-
321
- Run shopify_app generator again.
328
+ see [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
322
329
 
323
330
  Testing an embedded app outside the Shopify admin
324
331
  -------------------------------------------------
@@ -0,0 +1,16 @@
1
+ Troubleshooting Shopify App
2
+ ===========
3
+
4
+ ### Generator shopify_app:install hangs
5
+
6
+ Rails uses spring by default to speed up development. To run the generator, spring has to be stopped:
7
+
8
+ ```sh
9
+ $ bundle exec spring stop
10
+ ```
11
+
12
+ Run shopify_app generator again.
13
+
14
+ ### App installation fails with 'The page you’re looking for could not be found' if the app was installed before
15
+
16
+ This issue can occur when the session (the model you set as `ShopifyApp::SessionRepository.storage`) isn't deleted when the user uninstalls your app. A possible fix for this is listening to the `app/uninstalled` webhook and deleting the corresponding session in the webhook handler.
@@ -7,17 +7,17 @@ module ShopifyApp
7
7
  include Rails::Generators::Migration
8
8
  source_root File.expand_path('../templates', __FILE__)
9
9
 
10
- class_option :application_name, type: :string
10
+ class_option :application_name, type: :array, default: ['My', 'Shopify', 'App']
11
11
  class_option :api_key, type: :string, default: '<api_key>'
12
12
  class_option :secret, type: :string, default: '<secret>'
13
- class_option :scope, type: :string, default: 'read_orders, read_products'
13
+ class_option :scope, type: :array, default: ['read_orders,', 'read_products']
14
14
  class_option :embedded, type: :string, default: 'true'
15
15
 
16
16
  def create_shopify_app_initializer
17
- @application_name = options['application_name']
17
+ @application_name = format_array_argument(options['application_name'])
18
18
  @api_key = options['api_key']
19
19
  @secret = options['secret']
20
- @scope = options['scope']
20
+ @scope = format_array_argument(options['scope'])
21
21
 
22
22
  template 'shopify_app.rb', 'config/initializers/shopify_app.rb'
23
23
  end
@@ -61,6 +61,10 @@ module ShopifyApp
61
61
  def embedded_app?
62
62
  options['embedded'] == 'true'
63
63
  end
64
+
65
+ def format_array_argument(array)
66
+ array.join(' ').tr('"', '')
67
+ end
64
68
  end
65
69
  end
66
70
  end
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <% application_name = ShopifyApp.configuration.application_name %>
6
- <title><%= application_name.presence || 'Embedded Shopify App Example' %></title>
6
+ <title><%= application_name %></title>
7
7
  <%= stylesheet_link_tag 'application' %>
8
8
  <%= javascript_include_tag 'application', "data-turbolinks-track" => true %>
9
9
  <%= csrf_meta_tags %>
@@ -1,7 +1,5 @@
1
1
  ShopifyApp.configure do |config|
2
- <% if @application_name.present? %>
3
2
  config.application_name = "<%= @application_name %>"
4
- <% end %>
5
3
  config.api_key = "<%= @api_key %>"
6
4
  config.secret = "<%= @secret %>"
7
5
  config.scope = "<%= @scope %>"
@@ -39,65 +39,69 @@ module ShopifyApp
39
39
  head :unauthorized
40
40
  else
41
41
  session[:return_to] = request.fullpath if request.get?
42
- redirect_to_with_fallback main_or_engine_login_url(shop: params[:shop])
42
+ redirect_to main_or_engine_login_url(shop: params[:shop])
43
43
  end
44
44
  end
45
45
 
46
46
  def close_session
47
47
  session[:shopify] = nil
48
48
  session[:shopify_domain] = nil
49
- redirect_to_with_fallback main_or_engine_login_url(shop: params[:shop])
49
+ redirect_to main_or_engine_login_url(shop: params[:shop])
50
50
  end
51
51
 
52
52
  def main_or_engine_login_url(params = {})
53
53
  main_app.login_url(params)
54
- rescue NoMethodError => e
54
+ rescue NoMethodError
55
55
  shopify_app.login_url(params)
56
56
  end
57
57
 
58
- def redirect_to_with_fallback(url)
59
- url_json = url.to_json
60
- url_json_no_quotes = url_json.gsub(/\A"|"\Z/, '')
58
+ def fullpage_redirect_to(url)
59
+ if ShopifyApp.configuration.embedded_app?
60
+ render inline: redirection_javascript(url)
61
+ else
62
+ redirect_to url
63
+ end
64
+ end
61
65
 
62
- render inline: %Q(
66
+ def redirection_javascript(url)
67
+ %(
63
68
  <!DOCTYPE html>
64
69
  <html lang="en">
65
70
  <head>
66
71
  <meta charset="utf-8" />
72
+ <base target="_top">
67
73
  <title>Redirecting…</title>
68
74
  <script type="text/javascript">
69
- window.location.href = #{url_json};
75
+
76
+ // If the current window is the 'parent', change the URL by setting location.href
77
+ if (window.top == window.self) {
78
+ window.top.location.href = #{url.to_json};
79
+
80
+ // If the current window is the 'child', change the parent's URL with postMessage
81
+ } else {
82
+ data = JSON.stringify({
83
+ message: 'Shopify.API.remoteRedirect',
84
+ data: { location: window.location.origin + #{url.to_json} }
85
+ });
86
+ window.parent.postMessage(data, "https://#{sanitized_shop_name}");
87
+ }
88
+
70
89
  </script>
71
90
  </head>
72
91
  <body>
73
92
  </body>
74
93
  </html>
75
- ), status: 302, location: url
94
+ )
76
95
  end
77
96
 
78
- def fullpage_redirect_to(url)
79
- url_json = url.to_json
80
- url_json_no_quotes = url_json.gsub(/\A"|"\Z/, '')
97
+ def sanitized_shop_name
98
+ @sanitized_shop_name ||= sanitize_shop_param(params)
99
+ end
81
100
 
82
- if ShopifyApp.configuration.embedded_app?
83
- render inline: %Q(
84
- <!DOCTYPE html>
85
- <html lang="en">
86
- <head>
87
- <meta charset="utf-8" />
88
- <base target="_top">
89
- <title>Redirecting…</title>
90
- <script type="text/javascript">
91
- window.top.location.href = #{url_json};
92
- </script>
93
- </head>
94
- <body>
95
- </body>
96
- </html>
97
- )
98
- else
99
- redirect_to_with_fallback url
100
- end
101
+ def sanitize_shop_param(params)
102
+ return unless params[:shop].present?
103
+ ShopifyApp::Utils.sanitize_shop_domain(params[:shop])
101
104
  end
105
+
102
106
  end
103
107
  end
@@ -6,14 +6,25 @@ module ShopifyApp
6
6
  ShopifyApp::ScripttagsManagerJob.perform_later(
7
7
  shop_domain: shop_domain,
8
8
  shop_token: shop_token,
9
- scripttags: scripttags
9
+ # Procs cannot be serialized so we interpolate now, if necessary
10
+ scripttags: build_src(scripttags, shop_domain)
10
11
  )
11
12
  end
12
13
 
13
- attr_reader :required_scripttags
14
+ def self.build_src(scripttags, domain)
15
+ scripttags.map do |tag|
16
+ next tag unless tag[:src].respond_to?(:call)
17
+ tag = tag.dup
18
+ tag[:src] = tag[:src].call(domain)
19
+ tag
20
+ end
21
+ end
22
+
23
+ attr_reader :required_scripttags, :shop_domain
14
24
 
15
- def initialize(scripttags)
25
+ def initialize(scripttags, shop_domain)
16
26
  @required_scripttags = scripttags
27
+ @shop_domain = shop_domain
17
28
  end
18
29
 
19
30
  def recreate_scripttags!
@@ -24,14 +35,15 @@ module ShopifyApp
24
35
  def create_scripttags
25
36
  return unless required_scripttags.present?
26
37
 
27
- required_scripttags.each do |scripttag|
38
+ expanded_scripttags.each do |scripttag|
28
39
  create_scripttag(scripttag) unless scripttag_exists?(scripttag[:src])
29
40
  end
30
41
  end
31
42
 
32
43
  def destroy_scripttags
33
- ShopifyAPI::ScriptTag.all.each do |scripttag|
34
- ShopifyAPI::ScriptTag.delete(scripttag.id) if is_required_scripttag?(scripttag)
44
+ scripttags = expanded_scripttags
45
+ ShopifyAPI::ScriptTag.all.each do |tag|
46
+ ShopifyAPI::ScriptTag.delete(tag.id) if is_required_scripttag?(scripttags, tag)
35
47
  end
36
48
 
37
49
  @current_scripttags = nil
@@ -39,8 +51,12 @@ module ShopifyApp
39
51
 
40
52
  private
41
53
 
42
- def is_required_scripttag?(scripttag)
43
- required_scripttags.map{ |w| w[:src] }.include? scripttag.src
54
+ def expanded_scripttags
55
+ self.class.build_src(required_scripttags, shop_domain)
56
+ end
57
+
58
+ def is_required_scripttag?(scripttags, tag)
59
+ scripttags.map{ |w| w[:src] }.include? tag.src
44
60
  end
45
61
 
46
62
  def create_scripttag(attributes)
@@ -2,12 +2,12 @@ module ShopifyApp
2
2
  class ScripttagsManagerJob < ActiveJob::Base
3
3
 
4
4
  queue_as do
5
- ShopifyApp.configuration.webhooks_manager_queue_name
5
+ ShopifyApp.configuration.scripttags_manager_queue_name
6
6
  end
7
7
 
8
8
  def perform(shop_domain:, shop_token:, scripttags:)
9
9
  ShopifyAPI::Session.temp(shop_domain, shop_token) do
10
- manager = ScripttagsManager.new(scripttags)
10
+ manager = ScripttagsManager.new(scripttags, shop_domain)
11
11
  manager.create_scripttags
12
12
  end
13
13
  end
@@ -22,10 +22,10 @@ module ShopifyApp
22
22
  install_scripttags
23
23
 
24
24
  flash[:notice] = I18n.t('.logged_in')
25
- redirect_to_with_fallback return_address
25
+ redirect_to return_address
26
26
  else
27
27
  flash[:error] = I18n.t('could_not_log_in')
28
- redirect_to_with_fallback login_url
28
+ redirect_to login_url
29
29
  end
30
30
  end
31
31
 
@@ -33,16 +33,16 @@ module ShopifyApp
33
33
  session[:shopify] = nil
34
34
  session[:shopify_domain] = nil
35
35
  flash[:notice] = I18n.t('.logged_out')
36
- redirect_to_with_fallback login_url
36
+ redirect_to login_url
37
37
  end
38
38
 
39
39
  protected
40
40
 
41
41
  def authenticate
42
- if shop_name = sanitize_shop_param(params)
43
- fullpage_redirect_to "#{main_app.root_path}auth/shopify?shop=#{shop_name}"
42
+ if sanitized_shop_name.present?
43
+ fullpage_redirect_to "#{main_app.root_path}auth/shopify?shop=#{sanitized_shop_name}"
44
44
  else
45
- redirect_to_with_fallback return_address
45
+ redirect_to return_address
46
46
  end
47
47
  end
48
48
 
@@ -88,14 +88,5 @@ module ShopifyApp
88
88
  session.delete(:return_to) || main_app.root_url
89
89
  end
90
90
 
91
- def sanitized_shop_name
92
- @sanitized_shop_name ||= sanitize_shop_param(params)
93
- end
94
-
95
- def sanitize_shop_param(params)
96
- return unless params[:shop].present?
97
- ShopifyApp::Utils.sanitize_shop_domain(params[:shop])
98
- end
99
-
100
91
  end
101
92
  end
@@ -1,3 +1,3 @@
1
1
  module ShopifyApp
2
- VERSION = '7.2.0'
2
+ VERSION = '7.2.3'
3
3
  end
@@ -22,7 +22,7 @@ module ShopifyApp
22
22
  def hmac_valid?(data)
23
23
  secret = ShopifyApp.configuration.secret
24
24
  digest = OpenSSL::Digest.new('sha256')
25
- ActiveSupport::SecurityUtils.variable_size_secure_compare(
25
+ ActiveSupport::SecurityUtils.secure_compare(
26
26
  shopify_hmac,
27
27
  Base64.encode64(OpenSSL::HMAC.digest(digest, secret, data)).strip
28
28
  )
@@ -30,7 +30,7 @@ module ShopifyApp
30
30
  end
31
31
 
32
32
  def destroy_webhooks
33
- ShopifyAPI::Webhook.all.each do |webhook|
33
+ ShopifyAPI::Webhook.all.to_a.each do |webhook|
34
34
  ShopifyAPI::Webhook.delete(webhook.id) if is_required_webhook?(webhook)
35
35
  end
36
36
 
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: 7.2.0
4
+ version: 7.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-24 00:00:00.000000000 Z
11
+ date: 2017-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -141,6 +141,7 @@ files:
141
141
  - README.md
142
142
  - RELEASING
143
143
  - Rakefile
144
+ - TROUBLESHOOTING.md
144
145
  - app/controllers/shopify_app/authenticated_controller.rb
145
146
  - app/controllers/shopify_app/sessions_controller.rb
146
147
  - app/controllers/shopify_app/webhooks_controller.rb