shopify_app 7.2.0 → 7.2.3

Sign up to get free protection for your applications and to get access to all the features.
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