shopify_app 13.2.0 → 20.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +1 -0
- data/.github/ISSUE_TEMPLATE/bug-report.md +63 -0
- data/.github/ISSUE_TEMPLATE/config.yml +1 -0
- data/.github/ISSUE_TEMPLATE/feature-request.md +33 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +22 -0
- data/.github/workflows/build.yml +40 -0
- data/.github/workflows/cla.yml +22 -0
- data/.github/workflows/close-waiting-for-response-issues.yml +20 -0
- data/.github/workflows/release.yml +24 -0
- data/.github/workflows/remove-labels-on-activity.yml +16 -0
- data/.github/workflows/rubocop.yml +22 -0
- data/.github/workflows/stale.yml +31 -0
- data/.gitignore +1 -2
- data/.nvmrc +1 -1
- data/.rubocop.yml +2 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +221 -0
- data/CONTRIBUTING.md +81 -0
- data/Gemfile +5 -2
- data/Gemfile.lock +248 -0
- data/README.md +74 -563
- data/Rakefile +4 -3
- data/SECURITY.md +59 -0
- data/app/assets/images/storage_access.svg +1 -2
- data/app/assets/javascripts/shopify_app/app_bridge_3.1.1.js +10 -0
- data/app/assets/javascripts/shopify_app/app_bridge_redirect.js +22 -0
- data/app/assets/javascripts/shopify_app/app_bridge_utils_3.1.1.js +1 -0
- data/app/assets/javascripts/shopify_app/post_redirect.js +9 -0
- data/app/assets/javascripts/shopify_app/redirect.js +10 -14
- data/app/assets/javascripts/shopify_app/storage_access.js +5 -10
- data/app/assets/javascripts/shopify_app/top_level_interaction.js +1 -1
- data/app/controllers/concerns/shopify_app/authenticated.rb +4 -0
- data/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb +39 -0
- data/app/controllers/concerns/shopify_app/require_known_shop.rb +48 -0
- data/app/controllers/concerns/shopify_app/shop_access_scopes_verification.rb +40 -0
- data/app/controllers/shopify_app/authenticated_controller.rb +1 -0
- data/app/controllers/shopify_app/callback_controller.rb +56 -77
- data/app/controllers/shopify_app/extension_verification_controller.rb +2 -7
- data/app/controllers/shopify_app/sessions_controller.rb +33 -117
- data/app/controllers/shopify_app/webhooks_controller.rb +5 -26
- data/app/views/shopify_app/partials/_button_styles.html.erb +41 -36
- data/app/views/shopify_app/partials/_card_styles.html.erb +3 -3
- data/app/views/shopify_app/partials/_empty_state_styles.html.erb +28 -59
- data/app/views/shopify_app/partials/_form_styles.html.erb +56 -0
- data/app/views/shopify_app/partials/_layout_styles.html.erb +16 -1
- data/app/views/shopify_app/partials/_typography_styles.html.erb +6 -6
- data/app/views/shopify_app/sessions/enable_cookies.html.erb +2 -7
- data/app/views/shopify_app/sessions/new.html.erb +38 -110
- data/app/views/shopify_app/sessions/request_storage_access.html.erb +12 -12
- data/app/views/shopify_app/sessions/top_level_interaction.html.erb +21 -22
- data/app/views/shopify_app/shared/post_redirect_to_auth_shopify.html.erb +13 -0
- data/app/views/shopify_app/shared/redirect.html.erb +2 -2
- data/config/locales/de.yml +11 -11
- data/config/locales/ja.yml +4 -4
- data/config/locales/nl.yml +2 -2
- data/config/locales/th.yml +4 -4
- data/config/locales/vi.yml +22 -0
- data/config/locales/zh-CN.yml +2 -2
- data/config/routes.rb +20 -12
- data/docs/Quickstart.md +19 -83
- data/docs/Releasing.md +18 -15
- data/docs/Troubleshooting.md +140 -5
- data/docs/Upgrading.md +247 -0
- data/docs/shopify_app/authentication.md +128 -0
- data/docs/shopify_app/content-security-policy.md +10 -0
- data/docs/shopify_app/engine.md +82 -0
- data/docs/shopify_app/generators.md +127 -0
- data/docs/shopify_app/handling-access-scopes-changes.md +24 -0
- data/docs/shopify_app/script-tags.md +28 -0
- data/docs/shopify_app/session-repository.md +88 -0
- data/docs/shopify_app/testing.md +38 -0
- data/docs/shopify_app/webhooks.md +72 -0
- data/karma.conf.js +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 +15 -14
- 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 +4 -4
- data/lib/generators/shopify_app/controllers/controllers_generator.rb +5 -4
- data/lib/generators/shopify_app/home_controller/home_controller_generator.rb +27 -4
- data/lib/generators/shopify_app/home_controller/templates/home_controller.rb +12 -2
- data/lib/generators/shopify_app/home_controller/templates/index.html.erb +74 -16
- data/lib/generators/shopify_app/home_controller/templates/unauthenticated_home_controller.rb +16 -0
- data/lib/generators/shopify_app/install/install_generator.rb +52 -40
- data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +5 -2
- data/lib/generators/shopify_app/install/templates/flash_messages.js +0 -2
- data/lib/generators/shopify_app/install/templates/session_store.rb +2 -1
- data/lib/generators/shopify_app/install/templates/shopify_app.js +1 -1
- data/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +43 -5
- data/lib/generators/shopify_app/install/templates/shopify_app_importmap.js +13 -0
- data/lib/generators/shopify_app/products_controller/products_controller_generator.rb +19 -0
- data/lib/generators/shopify_app/products_controller/templates/products_controller.rb +8 -0
- 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 +35 -7
- data/lib/generators/shopify_app/shop_model/templates/db/migrate/add_shop_access_scopes_column.erb +5 -0
- data/lib/generators/shopify_app/shop_model/templates/shop.rb +2 -1
- data/lib/generators/shopify_app/shopify_app_generator.rb +4 -3
- data/lib/generators/shopify_app/user_model/templates/db/migrate/add_user_access_scopes_column.erb +5 -0
- data/lib/generators/shopify_app/user_model/templates/user.rb +2 -1
- data/lib/generators/shopify_app/user_model/user_model_generator.rb +35 -7
- data/lib/generators/shopify_app/views/views_generator.rb +5 -4
- data/lib/shopify_app/access_scopes/noop_strategy.rb +13 -0
- data/lib/shopify_app/access_scopes/shop_strategy.rb +24 -0
- data/lib/shopify_app/access_scopes/user_strategy.rb +41 -0
- data/lib/shopify_app/configuration.rb +58 -11
- data/lib/shopify_app/controller_concerns/app_proxy_verification.rb +4 -4
- data/lib/shopify_app/controller_concerns/csrf_protection.rb +16 -0
- data/lib/shopify_app/controller_concerns/embedded_app.rb +6 -3
- data/lib/shopify_app/controller_concerns/ensure_billing.rb +243 -0
- data/lib/shopify_app/controller_concerns/frame_ancestors.rb +16 -0
- 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 +105 -90
- data/lib/shopify_app/controller_concerns/payload_verification.rb +25 -0
- data/lib/shopify_app/controller_concerns/redirect_for_embedded.rb +36 -0
- data/lib/shopify_app/controller_concerns/sanitized_params.rb +36 -0
- data/lib/shopify_app/controller_concerns/webhook_verification.rb +3 -18
- data/lib/shopify_app/engine.rb +26 -11
- data/lib/shopify_app/errors.rb +34 -0
- 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 +12 -6
- data/lib/shopify_app/managers/webhooks_manager.rb +62 -42
- data/lib/shopify_app/middleware/jwt_middleware.rb +6 -3
- data/lib/shopify_app/session/in_memory_session_store.rb +2 -3
- data/lib/shopify_app/session/in_memory_shop_session_store.rb +10 -7
- data/lib/shopify_app/session/in_memory_user_session_store.rb +10 -7
- data/lib/shopify_app/session/jwt.rb +19 -16
- data/lib/shopify_app/session/null_user_session_store.rb +2 -1
- data/lib/shopify_app/session/session_repository.rb +40 -2
- 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 +57 -0
- data/lib/shopify_app/session/user_session_storage.rb +20 -7
- data/lib/shopify_app/session/user_session_storage_with_scopes.rb +71 -0
- 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 +14 -7
- data/lib/shopify_app/version.rb +2 -1
- data/lib/shopify_app.rb +52 -29
- data/package.json +7 -8
- data/service.yml +1 -5
- data/shopify_app.gemspec +22 -20
- data/translation.yml +1 -1
- data/yarn.lock +2173 -2206
- metadata +110 -56
- data/.github/ISSUE_TEMPLATE.md +0 -14
- data/.github/probots.yml +0 -2
- data/.travis.yml +0 -28
- data/config/locales/hi.yml +0 -23
- data/config/locales/ms.yml +0 -22
- data/docs/install-on-dev-shop.png +0 -0
- data/docs/test-your-app.png +0 -0
- data/lib/generators/shopify_app/install/templates/omniauth.rb +0 -3
- data/lib/generators/shopify_app/install/templates/shopify_provider.rb +0 -20
- 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/package-lock.json +0 -7245
@@ -0,0 +1,88 @@
|
|
1
|
+
# Session repository
|
2
|
+
|
3
|
+
#### Table of contents
|
4
|
+
|
5
|
+
[`ShopifyApp::SessionRepository`](#shopifyappsessionrepository)
|
6
|
+
* [Shop-based token storage](#shop-based-token-storage)
|
7
|
+
* [User-based token storage](#user-based-token-storage)
|
8
|
+
|
9
|
+
[Access scopes](#access-scopes)
|
10
|
+
* [`ShopifyApp::ShopSessionStorageWithScopes`](#shopifyappshopsessionstoragewithscopes)
|
11
|
+
* [``ShopifyApp::UserSessionStorageWithScopes``](#shopifyappusersessionstoragewithscopes)
|
12
|
+
|
13
|
+
[Migrating from shop-based to user-based token strategy](#migrating-from-shop-based-to-user-based-token-strategy)
|
14
|
+
|
15
|
+
## ShopifyApp::SessionRepository
|
16
|
+
|
17
|
+
`ShopifyApp::SessionRepository` allows you as a developer to define how your sessions are stored and retrieved for shops. The `SessionRepository` is configured in the `config/initializers/shopify_app.rb` file and can be set to any object that implements `self.store(auth_session, *args)` which stores the session and returns a unique identifier and `self.retrieve(id)` which returns a `ShopifyAPI::Session` for the passed id. These methods are already implemented as part of the `ShopifyApp::SessionStorage` concern but can be overridden for custom implementation.
|
18
|
+
|
19
|
+
### Shop-based token storage
|
20
|
+
|
21
|
+
Storing tokens on the store model means that any user login associated with the store will have equal access levels to whatever the original user granted the app.
|
22
|
+
```sh
|
23
|
+
rails generate shopify_app:shop_model
|
24
|
+
```
|
25
|
+
This will generate a shop model which will be the storage for the tokens necessary for authentication.
|
26
|
+
|
27
|
+
### User-based token storage
|
28
|
+
|
29
|
+
A more granular control over the level of access per user on an app might be necessary, to which the shop-based token strategy is not sufficient. Shopify supports a user-based token storage strategy where a unique token to each user can be managed. Shop tokens must still be maintained if you are running background jobs so that you can make use of them when necessary.
|
30
|
+
```sh
|
31
|
+
rails generate shopify_app:shop_model
|
32
|
+
rails generate shopify_app:user_model
|
33
|
+
```
|
34
|
+
This will generate a shop model and user model, which will be the storage for the tokens necessary for authentication.
|
35
|
+
|
36
|
+
The current Shopify user will be stored in the rails session at `session[:shopify_user]`
|
37
|
+
|
38
|
+
Read more about Online vs. Offline access [here](https://help.shopify.com/api/getting-started/authentication/oauth).
|
39
|
+
|
40
|
+
## Access scopes
|
41
|
+
|
42
|
+
If you want to customize how access scopes are stored for shops and users, you can implement the `access_scopes` getters and setters in the models that include `ShopifyApp::ShopSessionStorageWithScopes` and `ShopifyApp::UserSessionStorageWithScopes` as shown:
|
43
|
+
|
44
|
+
### `ShopifyApp::ShopSessionStorageWithScopes`
|
45
|
+
```ruby
|
46
|
+
class Shop < ActiveRecord::Base
|
47
|
+
include ShopifyApp::ShopSessionStorageWithScopes
|
48
|
+
|
49
|
+
def access_scopes=(scopes)
|
50
|
+
# Store access scopes
|
51
|
+
end
|
52
|
+
def access_scopes
|
53
|
+
# Find access scopes
|
54
|
+
end
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
### `ShopifyApp::UserSessionStorageWithScopes`
|
59
|
+
```ruby
|
60
|
+
class User < ActiveRecord::Base
|
61
|
+
include ShopifyApp::UserSessionStorageWithScopes
|
62
|
+
|
63
|
+
def access_scopes=(scopes)
|
64
|
+
# Store access scopes
|
65
|
+
end
|
66
|
+
def access_scopes
|
67
|
+
# Find access scopes
|
68
|
+
end
|
69
|
+
end
|
70
|
+
```
|
71
|
+
## Migrating from shop-based to user-based token strategy
|
72
|
+
|
73
|
+
1. Run the `user_model` generator as mentioned above.
|
74
|
+
2. Ensure that both your `Shop` model and `User` model includes the necessary concerns `ShopifyApp::ShopSessionStorage` and `ShopifyApp::UserSessionStorage`.
|
75
|
+
3. Make changes to 2 initializer files as shown below:
|
76
|
+
```ruby
|
77
|
+
# In the `omniauth.rb` initializer:
|
78
|
+
provider :shopify,
|
79
|
+
...
|
80
|
+
setup: lambda { |env|
|
81
|
+
configuration = ShopifyApp::OmniAuthConfiguration.new(env['omniauth.strategy'], Rack::Request.new(env))
|
82
|
+
configuration.build_options
|
83
|
+
}
|
84
|
+
|
85
|
+
# In the `shopify_app.rb` initializer:
|
86
|
+
config.shop_session_repository = {YOUR_SHOP_MODEL_CLASS}
|
87
|
+
config.user_session_repository = {YOUR_USER_MODEL_CLASS}
|
88
|
+
```
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Testing
|
2
|
+
|
3
|
+
#### Table of contents
|
4
|
+
|
5
|
+
[Using test helpers inside your application](#using-test-helpers-inside-your-application)
|
6
|
+
|
7
|
+
[Testing an embedded app outside the Shopify admin](#testing-an-embedded-app-outside-the-shopify-admin)
|
8
|
+
|
9
|
+
## Using test helpers inside your application
|
10
|
+
|
11
|
+
A test helper that will allow you to test `ShopifyApp::WebhookVerification` in the controller from your app, to use this test, you need to `require` it directly inside your app `test/controllers/webhook_verification_test.rb`.
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
require 'test_helper'
|
15
|
+
require 'action_controller'
|
16
|
+
require 'action_controller/base'
|
17
|
+
require 'shopify_app/test_helpers/webhook_verification_helper'
|
18
|
+
```
|
19
|
+
|
20
|
+
Or you can require in your `test/test_helper.rb`.
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
ENV['RAILS_ENV'] ||= 'test'
|
24
|
+
require_relative '../config/environment'
|
25
|
+
require 'rails/test_help'
|
26
|
+
require 'byebug'
|
27
|
+
require 'shopify_app/test_helpers/all'
|
28
|
+
```
|
29
|
+
|
30
|
+
With `lib/shopify_app/test_helpers/all'` more tests can be added and will only need to be required in once in your library.
|
31
|
+
|
32
|
+
## Testing an embedded app outside the Shopify admin
|
33
|
+
|
34
|
+
By default, loading your embedded app will redirect to the Shopify admin, with the app view loaded in an `iframe`. If you need to load your app outside of the Shopify admin (e.g., for performance testing), you can change `forceRedirect: true` to `false` in `ShopifyApp.init` block in the `embedded_app` view. To keep the redirect on in production but off in your `development` and `test` environments, you can use:
|
35
|
+
|
36
|
+
```javascript
|
37
|
+
forceRedirect: <%= Rails.env.development? || Rails.env.test? ? 'false' : 'true' %>
|
38
|
+
```
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Webhooks
|
2
|
+
|
3
|
+
#### Table of contents
|
4
|
+
|
5
|
+
[Manage webhooks using `ShopifyApp::WebhooksManager`](#manage-webhooks-using-shopifyappwebhooksmanager)
|
6
|
+
|
7
|
+
## Manage webhooks using `ShopifyApp::WebhooksManager`
|
8
|
+
|
9
|
+
See [`ShopifyApp::WebhooksManager`](/lib/shopify_app/managers/webhooks_manager.rb)
|
10
|
+
ShopifyApp can manage your app's webhooks for you if you set which webhooks you require in the initializer:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
ShopifyApp.configure do |config|
|
14
|
+
config.webhooks = [
|
15
|
+
{topic: 'carts/update', path: 'webhooks/carts_update'}
|
16
|
+
]
|
17
|
+
end
|
18
|
+
```
|
19
|
+
|
20
|
+
When the [OAuth callback](/docs/shopify_app/authentication.md#oauth-callback) is completed successfully, ShopifyApp will queue a background job which will ensure all the specified webhooks exist for that shop. Because this runs on every OAuth callback, it means your app will always have the webhooks it needs even if the user uninstalls and re-installs the app.
|
21
|
+
|
22
|
+
ShopifyApp also provides a [WebhooksController](/app/controllers/shopify_app/webhooks_controller.rb) that receives webhooks and queues a job based on the received topic. 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).
|
23
|
+
|
24
|
+
If you would like to namespace your jobs, you may set `webhook_jobs_namespace` in the config. For example, if your app handles webhooks from other ecommerce applications as well, and you want Shopify cart update webhooks to be processed by a job living in `jobs/shopify/webhooks/carts_update_job.rb` rather than `jobs/carts_update_job.rb`):
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
ShopifyApp.configure do |config|
|
28
|
+
config.webhook_jobs_namespace = 'shopify/webhooks'
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
32
|
+
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.
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
ShopifyApp.configure do |config|
|
36
|
+
config.webhooks = [
|
37
|
+
{topic: 'products/update', path: 'webhooks/products_update', fields: ['title', 'vendor']}
|
38
|
+
]
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
If you'd rather implement your own controller then you'll want to use the [`ShopifyApp::WebhookVerification`](/lib/shopify_app/controller_concerns/webhook_verification.rb) module to verify your webhooks, example:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
class CustomWebhooksController < ApplicationController
|
46
|
+
include ShopifyApp::WebhookVerification
|
47
|
+
|
48
|
+
def carts_update
|
49
|
+
params.permit!
|
50
|
+
SomeJob.perform_later(shop_domain: shop_domain, webhook: webhook_params.to_h)
|
51
|
+
head :no_content
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def webhook_params
|
57
|
+
params.except(:controller, :action, :type)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
The module skips the `verify_authenticity_token` before_action and adds an action to verify that the webhook came from Shopify. You can now add a post route to your application, pointing to the controller and action to accept the webhook data from Shopify.
|
63
|
+
|
64
|
+
The WebhooksManager uses ActiveJob. If ActiveJob is not configured then by default Rails will run the jobs inline. However, it is highly recommended to configure a proper background processing queue like Sidekiq or Resque in production.
|
65
|
+
|
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
|
+
|
68
|
+
```
|
69
|
+
rails g shopify_app:add_webhook --topic carts/update --path webhooks/carts_update
|
70
|
+
```
|
71
|
+
|
72
|
+
Where `--topic` is the topic and `--path` is the path the webhook should be sent to.
|
data/karma.conf.js
CHANGED
@@ -8,7 +8,7 @@ module.exports = function(config) {
|
|
8
8
|
config.set({
|
9
9
|
mode: 'development',
|
10
10
|
basePath: '',
|
11
|
-
frameworks: ['mocha
|
11
|
+
frameworks: ['mocha', 'chai-sinon'],
|
12
12
|
files: [
|
13
13
|
'app/assets/javascripts/**/*.js',
|
14
14
|
'test/javascripts/**/*test.js',
|
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,12 +1,13 @@
|
|
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
|
-
class_option :
|
10
|
+
class_option :path, type: :string, aliases: "-p", required: true
|
10
11
|
|
11
12
|
hook_for :test_framework, as: :job, in: :rails do |instance, generator|
|
12
13
|
instance.invoke(generator, [instance.send(:job_file_name)])
|
@@ -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
|
-
|
50
|
+
path.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}\", path: \"#{path}\" },"
|
58
59
|
end
|
59
60
|
|
60
61
|
def topic
|
61
|
-
options[
|
62
|
+
options["topic"]
|
62
63
|
end
|
63
64
|
|
64
|
-
def
|
65
|
-
options[
|
65
|
+
def path
|
66
|
+
options["path"]
|
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
|
-
def
|
11
|
-
template(
|
10
|
+
def create_authenticated_controller
|
11
|
+
template("authenticated_controller.rb", "app/controllers/authenticated_controller.rb")
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require "rails/generators/base"
|
3
4
|
|
4
5
|
module ShopifyApp
|
5
6
|
module Generators
|
@@ -8,21 +9,21 @@ module ShopifyApp
|
|
8
9
|
|
9
10
|
def create_controllers
|
10
11
|
controllers.each do |controller|
|
11
|
-
copy_file
|
12
|
+
copy_file(controller)
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
15
16
|
private
|
16
17
|
|
17
18
|
def controllers
|
18
|
-
files_within_root(
|
19
|
+
files_within_root(".", "app/controllers/shopify_app/*.*")
|
19
20
|
end
|
20
21
|
|
21
22
|
def files_within_root(prefix, glob)
|
22
23
|
root = "#{self.class.source_root}/#{prefix}"
|
23
24
|
|
24
25
|
Dir["#{root}/#{glob}"].sort.map do |full_path|
|
25
|
-
full_path.sub(root,
|
26
|
+
full_path.sub(root, ".").gsub("/./", "/")
|
26
27
|
end
|
27
28
|
end
|
28
29
|
end
|
@@ -1,26 +1,49 @@
|
|
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 HomeControllerGenerator < Rails::Generators::Base
|
7
|
-
source_root File.expand_path(
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
9
|
+
|
10
|
+
class_option :embedded, type: :string, default: "true"
|
8
11
|
|
9
12
|
def create_home_controller
|
10
|
-
template(
|
13
|
+
template(home_controller_template, "app/controllers/home_controller.rb")
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_products_controller
|
17
|
+
generate("shopify_app:products_controller") if embedded? || embedded_app?
|
11
18
|
end
|
12
19
|
|
13
20
|
def create_home_index_view
|
14
|
-
|
21
|
+
template("index.html.erb", "app/views/home/index.html.erb")
|
15
22
|
end
|
16
23
|
|
17
24
|
def add_home_index_route
|
18
25
|
route("root :to => 'home#index'")
|
19
26
|
end
|
20
27
|
|
28
|
+
private
|
29
|
+
|
30
|
+
def embedded?
|
31
|
+
options["embedded"] == "true"
|
32
|
+
end
|
33
|
+
|
21
34
|
def embedded_app?
|
22
35
|
ShopifyApp.configuration.embedded_app?
|
23
36
|
end
|
37
|
+
|
38
|
+
def home_controller_template
|
39
|
+
return "unauthenticated_home_controller.rb" unless authenticated_home_controller_required?
|
40
|
+
|
41
|
+
"home_controller.rb"
|
42
|
+
end
|
43
|
+
|
44
|
+
def authenticated_home_controller_required?
|
45
|
+
!embedded? || !embedded_app?
|
46
|
+
end
|
24
47
|
end
|
25
48
|
end
|
26
49
|
end
|
@@ -1,8 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class HomeController < AuthenticatedController
|
4
|
+
include ShopifyApp::ShopAccessScopesVerification
|
5
|
+
|
6
|
+
before_action :set_host
|
7
|
+
|
4
8
|
def index
|
5
|
-
@products = ShopifyAPI::Product.
|
6
|
-
@webhooks = ShopifyAPI::Webhook.
|
9
|
+
@products = ShopifyAPI::Product.all(limit: 10)
|
10
|
+
@webhooks = ShopifyAPI::Webhook.all
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def set_host
|
16
|
+
@host = params[:host]
|
7
17
|
end
|
8
18
|
end
|