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 +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +19 -12
- data/TROUBLESHOOTING.md +16 -0
- data/lib/generators/shopify_app/install/install_generator.rb +8 -4
- data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +1 -1
- data/lib/generators/shopify_app/install/templates/shopify_app.rb +0 -2
- data/lib/shopify_app/login_protection.rb +35 -31
- data/lib/shopify_app/scripttags_manager.rb +24 -8
- data/lib/shopify_app/scripttags_manager_job.rb +2 -2
- data/lib/shopify_app/sessions_concern.rb +6 -15
- data/lib/shopify_app/version.rb +1 -1
- data/lib/shopify_app/webhook_verification.rb +1 -1
- data/lib/shopify_app/webhooks_manager.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6802e72897eb705326a9cc6639506aa5eefb912
|
4
|
+
data.tar.gz: 76ae92534abcbbc635adcab460e6d7b72898a5ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22cb00e95696ddfce4f64cd77cbe422de626e17e654b151d89c83a2d0813469a4565f51725f848c557e70b58275f2dfb1f40e0600d2bd03f787fa97d38325ae7
|
7
|
+
data.tar.gz: fffd5d0b94281792e7911091060654f06192387b9a5d08f336a6ec18725af3094a65c4ff5d9e05771dd0fba7d326d04b17572f305d68a2f6cef93fe0cb30b49b
|
data/CHANGELOG.md
CHANGED
@@ -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
|
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
|
-
|
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
|
-------------------------------------------------
|
data/TROUBLESHOOTING.md
ADDED
@@ -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: :
|
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: :
|
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
|
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 %>
|
@@ -39,65 +39,69 @@ module ShopifyApp
|
|
39
39
|
head :unauthorized
|
40
40
|
else
|
41
41
|
session[:return_to] = request.fullpath if request.get?
|
42
|
-
|
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
|
-
|
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
|
54
|
+
rescue NoMethodError
|
55
55
|
shopify_app.login_url(params)
|
56
56
|
end
|
57
57
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
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
|
-
|
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
|
-
|
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
|
-
)
|
94
|
+
)
|
76
95
|
end
|
77
96
|
|
78
|
-
def
|
79
|
-
|
80
|
-
|
97
|
+
def sanitized_shop_name
|
98
|
+
@sanitized_shop_name ||= sanitize_shop_param(params)
|
99
|
+
end
|
81
100
|
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
34
|
-
|
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
|
43
|
-
|
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.
|
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
|
-
|
25
|
+
redirect_to return_address
|
26
26
|
else
|
27
27
|
flash[:error] = I18n.t('could_not_log_in')
|
28
|
-
|
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
|
-
|
36
|
+
redirect_to login_url
|
37
37
|
end
|
38
38
|
|
39
39
|
protected
|
40
40
|
|
41
41
|
def authenticate
|
42
|
-
if
|
43
|
-
fullpage_redirect_to "#{main_app.root_path}auth/shopify?shop=#{
|
42
|
+
if sanitized_shop_name.present?
|
43
|
+
fullpage_redirect_to "#{main_app.root_path}auth/shopify?shop=#{sanitized_shop_name}"
|
44
44
|
else
|
45
|
-
|
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
|
data/lib/shopify_app/version.rb
CHANGED
@@ -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.
|
25
|
+
ActiveSupport::SecurityUtils.secure_compare(
|
26
26
|
shopify_hmac,
|
27
27
|
Base64.encode64(OpenSSL::HMAC.digest(digest, secret, data)).strip
|
28
28
|
)
|
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.
|
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:
|
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
|