shopify_app 11.2.0 → 11.5.0
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/.travis.yml +0 -2
- data/CHANGELOG.md +30 -0
- data/README.md +116 -101
- data/app/controllers/concerns/shopify_app/authenticated.rb +1 -1
- data/app/controllers/shopify_app/callback_controller.rb +8 -2
- data/config/locales/de.yml +3 -3
- data/config/locales/nl.yml +7 -7
- data/config/locales/pt-BR.yml +5 -5
- data/lib/generators/shopify_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +39 -0
- data/lib/generators/shopify_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +66 -0
- data/lib/generators/shopify_app/install/install_generator.rb +20 -9
- data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +5 -1
- data/lib/generators/shopify_app/install/templates/shopify_app_index.js +2 -0
- data/lib/generators/shopify_app/install/templates/shopify_provider.rb +1 -0
- data/lib/generators/shopify_app/user_model/templates/db/migrate/create_users.erb +16 -0
- data/lib/generators/shopify_app/user_model/templates/user.rb +7 -0
- data/lib/generators/shopify_app/user_model/templates/users.yml +4 -0
- data/lib/generators/shopify_app/user_model/user_model_generator.rb +38 -0
- data/lib/shopify_app.rb +46 -29
- data/lib/shopify_app/configuration.rb +8 -0
- data/lib/shopify_app/controller_concerns/login_protection.rb +22 -3
- data/lib/shopify_app/controllers/extension_verification_controller.rb +18 -0
- data/lib/shopify_app/session/in_memory_session_store.rb +1 -1
- data/lib/shopify_app/session/session_repository.rb +2 -2
- data/lib/shopify_app/session/session_storage.rb +14 -15
- data/lib/shopify_app/session/storage_strategies/shop_storage_strategy.rb +24 -0
- data/lib/shopify_app/session/storage_strategies/user_storage_strategy.rb +26 -0
- data/lib/shopify_app/version.rb +1 -1
- data/package-lock.json +1218 -1221
- data/package.json +1 -2
- data/service.yml +1 -1
- data/shopify_app.gemspec +5 -2
- data/yarn.lock +14 -14
- metadata +56 -4
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MarketingActivitiesController < ExtensionVerificationController
|
4
|
+
def preload_form_data
|
5
|
+
preload_data = {
|
6
|
+
"form_data": {
|
7
|
+
"budget": {
|
8
|
+
"currency": "USD",
|
9
|
+
}
|
10
|
+
}
|
11
|
+
}
|
12
|
+
render(json: preload_data, status: :ok)
|
13
|
+
end
|
14
|
+
|
15
|
+
def update
|
16
|
+
render(json: {}, status: :accepted)
|
17
|
+
end
|
18
|
+
|
19
|
+
def pause
|
20
|
+
render(json: {}, status: :accepted)
|
21
|
+
end
|
22
|
+
|
23
|
+
def resume
|
24
|
+
render(json: {}, status: :accepted)
|
25
|
+
end
|
26
|
+
|
27
|
+
def delete
|
28
|
+
render(json: {}, status: :accepted)
|
29
|
+
end
|
30
|
+
|
31
|
+
def preview
|
32
|
+
placeholder_img = "https://cdn.shopify.com/s/files/1/0533/2089/files/placeholder-images-image_small.png"
|
33
|
+
preview_response = {
|
34
|
+
"desktop": {
|
35
|
+
"preview_url": placeholder_img,
|
36
|
+
"content_type": "text/html",
|
37
|
+
"width": 360,
|
38
|
+
"height": 200
|
39
|
+
},
|
40
|
+
"mobile": {
|
41
|
+
"preview_url": placeholder_img,
|
42
|
+
"content_type": "text/html",
|
43
|
+
"width": 360,
|
44
|
+
"height": 200
|
45
|
+
}
|
46
|
+
}
|
47
|
+
render(json: preview_response, status: :ok)
|
48
|
+
end
|
49
|
+
|
50
|
+
def create
|
51
|
+
render(json: {}, status: :ok)
|
52
|
+
end
|
53
|
+
|
54
|
+
def republish
|
55
|
+
render(json: {}, status: :accepted)
|
56
|
+
end
|
57
|
+
|
58
|
+
def errors
|
59
|
+
request_id = params[:request_id]
|
60
|
+
message = params[:message]
|
61
|
+
|
62
|
+
Rails.logger.info("[Marketing Activity App Error Feedback] Request id: #{request_id}, message: #{message}")
|
63
|
+
|
64
|
+
render(json: {}, status: :ok)
|
65
|
+
end
|
66
|
+
end
|
@@ -11,10 +11,6 @@ module ShopifyApp
|
|
11
11
|
class_option :embedded, type: :string, default: 'true'
|
12
12
|
class_option :api_version, type: :string, default: nil
|
13
13
|
|
14
|
-
def add_dotenv_gem
|
15
|
-
gem('dotenv-rails', group: [:test, :development])
|
16
|
-
end
|
17
|
-
|
18
14
|
def create_shopify_app_initializer
|
19
15
|
@application_name = format_array_argument(options['application_name'])
|
20
16
|
@scope = format_array_argument(options['scope'])
|
@@ -40,12 +36,19 @@ module ShopifyApp
|
|
40
36
|
end
|
41
37
|
|
42
38
|
def create_embedded_app_layout
|
43
|
-
|
44
|
-
|
39
|
+
return unless embedded_app?
|
40
|
+
|
41
|
+
copy_file 'embedded_app.html.erb', 'app/views/layouts/embedded_app.html.erb'
|
42
|
+
copy_file '_flash_messages.html.erb', 'app/views/layouts/_flash_messages.html.erb'
|
43
|
+
|
44
|
+
if ShopifyApp.use_webpacker?
|
45
|
+
copy_file('shopify_app.js', 'app/javascript/shopify_app/shopify_app.js')
|
46
|
+
copy_file('flash_messages.js', 'app/javascript/shopify_app/flash_messages.js')
|
47
|
+
copy_file('shopify_app_index.js', 'app/javascript/shopify_app/index.js')
|
48
|
+
append_to_file('app/javascript/packs/application.js', 'require("shopify_app")')
|
49
|
+
else
|
45
50
|
copy_file('shopify_app.js', 'app/assets/javascripts/shopify_app.js')
|
46
|
-
copy_file
|
47
|
-
copy_file('flash_messages.js',
|
48
|
-
'app/assets/javascripts/flash_messages.js')
|
51
|
+
copy_file('flash_messages.js', 'app/assets/javascripts/flash_messages.js')
|
49
52
|
end
|
50
53
|
end
|
51
54
|
|
@@ -57,6 +60,14 @@ module ShopifyApp
|
|
57
60
|
route "mount ShopifyApp::Engine, at: '/'"
|
58
61
|
end
|
59
62
|
|
63
|
+
def insert_hosts_into_development_config
|
64
|
+
inject_into_file(
|
65
|
+
'config/environments/development.rb',
|
66
|
+
" config.hosts = (config.hosts rescue []) << /\\h+.ngrok.io/\n",
|
67
|
+
after: "Rails.application.configure do\n"
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
60
71
|
private
|
61
72
|
|
62
73
|
def embedded_app?
|
@@ -5,7 +5,11 @@
|
|
5
5
|
<% application_name = ShopifyApp.configuration.application_name %>
|
6
6
|
<title><%= application_name %></title>
|
7
7
|
<%= stylesheet_link_tag 'application' %>
|
8
|
-
|
8
|
+
<% if ShopifyApp.use_webpacker? %>
|
9
|
+
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
|
10
|
+
<% else %>
|
11
|
+
<%= javascript_include_tag 'application', "data-turbolinks-track" => true %>
|
12
|
+
<% end %>
|
9
13
|
<%= csrf_meta_tags %>
|
10
14
|
</head>
|
11
15
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateUsers < ActiveRecord::Migration[<%= rails_migration_version %>]
|
2
|
+
def self.up
|
3
|
+
create_table :users do |t|
|
4
|
+
t.bigint :shopify_user_id, null: false
|
5
|
+
t.string :shopify_domain, null: false
|
6
|
+
t.string :shopify_token, null: false
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
|
10
|
+
add_index :users, :shopify_user_id, unique: true
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.down
|
14
|
+
drop_table :users
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
require 'rails/generators/active_record'
|
3
|
+
|
4
|
+
module ShopifyApp
|
5
|
+
module Generators
|
6
|
+
class UserModelGenerator < Rails::Generators::Base
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
source_root File.expand_path('../templates', __FILE__)
|
9
|
+
|
10
|
+
def create_user_model
|
11
|
+
copy_file 'user.rb', 'app/models/user.rb'
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_user_migration
|
15
|
+
migration_template 'db/migrate/create_users.erb', 'db/migrate/create_users.rb'
|
16
|
+
end
|
17
|
+
|
18
|
+
def update_shopify_app_initializer
|
19
|
+
gsub_file 'config/initializers/shopify_app.rb', 'ShopifyApp::InMemorySessionStore', 'User'
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_user_fixtures
|
23
|
+
copy_file 'users.yml', 'test/fixtures/users.yml'
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def rails_migration_version
|
29
|
+
Rails.version.match(/\d\.\d/)[0]
|
30
|
+
end
|
31
|
+
|
32
|
+
# for generating a timestamp when using `create_migration`
|
33
|
+
def self.next_migration_number(dir)
|
34
|
+
ActiveRecord::Generators::Base.next_migration_number(dir)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/shopify_app.rb
CHANGED
@@ -4,32 +4,49 @@ require 'shopify_app/version'
|
|
4
4
|
require 'shopify_api'
|
5
5
|
require 'omniauth-shopify-oauth2'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
require 'shopify_app/
|
20
|
-
|
21
|
-
|
22
|
-
require 'shopify_app/
|
23
|
-
|
24
|
-
#
|
25
|
-
require 'shopify_app/
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
require 'shopify_app/
|
34
|
-
require 'shopify_app/
|
35
|
-
require 'shopify_app/
|
7
|
+
module ShopifyApp
|
8
|
+
def self.rails6?
|
9
|
+
Rails::VERSION::MAJOR >= 6
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.use_webpacker?
|
13
|
+
rails6? &&
|
14
|
+
defined?(Webpacker) == 'constant' &&
|
15
|
+
!configuration.disable_webpacker
|
16
|
+
end
|
17
|
+
|
18
|
+
# config
|
19
|
+
require 'shopify_app/configuration'
|
20
|
+
|
21
|
+
# engine
|
22
|
+
require 'shopify_app/engine'
|
23
|
+
|
24
|
+
# utils
|
25
|
+
require 'shopify_app/utils'
|
26
|
+
|
27
|
+
# controllers
|
28
|
+
require 'shopify_app/controllers/extension_verification_controller'
|
29
|
+
|
30
|
+
# controller concerns
|
31
|
+
require 'shopify_app/controller_concerns/localization'
|
32
|
+
require 'shopify_app/controller_concerns/itp'
|
33
|
+
require 'shopify_app/controller_concerns/login_protection'
|
34
|
+
require 'shopify_app/controller_concerns/embedded_app'
|
35
|
+
require 'shopify_app/controller_concerns/webhook_verification'
|
36
|
+
require 'shopify_app/controller_concerns/app_proxy_verification'
|
37
|
+
|
38
|
+
# jobs
|
39
|
+
require 'shopify_app/jobs/webhooks_manager_job'
|
40
|
+
require 'shopify_app/jobs/scripttags_manager_job'
|
41
|
+
|
42
|
+
# managers
|
43
|
+
require 'shopify_app/managers/webhooks_manager'
|
44
|
+
require 'shopify_app/managers/scripttags_manager'
|
45
|
+
|
46
|
+
# session
|
47
|
+
require 'shopify_app/session/storage_strategies/shop_storage_strategy'
|
48
|
+
require 'shopify_app/session/storage_strategies/user_storage_strategy'
|
49
|
+
require 'shopify_app/session/session_storage'
|
50
|
+
require 'shopify_app/session/session_repository'
|
51
|
+
require 'shopify_app/session/in_memory_session_store'
|
52
|
+
end
|
@@ -15,6 +15,8 @@ module ShopifyApp
|
|
15
15
|
attr_accessor :scripttags
|
16
16
|
attr_accessor :after_authenticate_job
|
17
17
|
attr_accessor :session_repository
|
18
|
+
attr_accessor :per_user_tokens
|
19
|
+
alias_method :per_user_tokens?, :per_user_tokens
|
18
20
|
attr_accessor :api_version
|
19
21
|
|
20
22
|
# customise urls
|
@@ -28,6 +30,9 @@ module ShopifyApp
|
|
28
30
|
# configure myshopify domain for local shopify development
|
29
31
|
attr_accessor :myshopify_domain
|
30
32
|
|
33
|
+
# ability to have webpacker installed but not used in this gem and the generators
|
34
|
+
attr_accessor :disable_webpacker
|
35
|
+
|
31
36
|
# allow namespacing webhook jobs
|
32
37
|
attr_accessor :webhook_jobs_namespace
|
33
38
|
|
@@ -36,6 +41,8 @@ module ShopifyApp
|
|
36
41
|
@myshopify_domain = 'myshopify.com'
|
37
42
|
@scripttags_manager_queue_name = Rails.application.config.active_job.queue_name
|
38
43
|
@webhooks_manager_queue_name = Rails.application.config.active_job.queue_name
|
44
|
+
@per_user_tokens = false
|
45
|
+
@disable_webpacker = ENV['SHOPIFY_APP_DISABLE_WEBPACKER'].present?
|
39
46
|
end
|
40
47
|
|
41
48
|
def login_url
|
@@ -59,6 +66,7 @@ module ShopifyApp
|
|
59
66
|
def has_scripttags?
|
60
67
|
scripttags.present?
|
61
68
|
end
|
69
|
+
|
62
70
|
end
|
63
71
|
|
64
72
|
def self.configuration
|
@@ -27,12 +27,30 @@ module ShopifyApp
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def shop_session
|
30
|
-
|
31
|
-
|
30
|
+
if ShopifyApp.configuration.per_user_tokens?
|
31
|
+
return unless session[:shopify_user]
|
32
|
+
@shop_session ||= ShopifyApp::SessionRepository.retrieve(session[:shopify_user]['id'])
|
33
|
+
else
|
34
|
+
return unless session[:shopify]
|
35
|
+
@shop_session ||= ShopifyApp::SessionRepository.retrieve(session[:shopify])
|
36
|
+
end
|
32
37
|
end
|
33
38
|
|
34
|
-
def
|
39
|
+
def login_again_if_different_user_or_shop
|
40
|
+
if ShopifyApp.configuration.per_user_tokens?
|
41
|
+
valid_session_data = session[:user_session].present? && params[:session].present? # session data was sent/stored correctly
|
42
|
+
sessions_do_not_match = session[:user_session] != params[:session] # current user is different from stored user
|
43
|
+
|
44
|
+
if valid_session_data && sessions_do_not_match
|
45
|
+
clear_session = true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
35
49
|
if shop_session && params[:shop] && params[:shop].is_a?(String) && (shop_session.domain != params[:shop])
|
50
|
+
clear_session = true
|
51
|
+
end
|
52
|
+
|
53
|
+
if clear_session
|
36
54
|
clear_shop_session
|
37
55
|
redirect_to_login
|
38
56
|
end
|
@@ -60,6 +78,7 @@ module ShopifyApp
|
|
60
78
|
session[:shopify] = nil
|
61
79
|
session[:shopify_domain] = nil
|
62
80
|
session[:shopify_user] = nil
|
81
|
+
session[:user_session] = nil
|
63
82
|
end
|
64
83
|
|
65
84
|
def login_url_with_optional_shop(top_level: false)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ExtensionVerificationController < ActionController::Base
|
4
|
+
protect_from_forgery with: :null_session
|
5
|
+
before_action :verify_request
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def verify_request
|
10
|
+
hmac_header = request.headers['HTTP_X_SHOPIFY_HMAC_SHA256']
|
11
|
+
request_body = request.body.read
|
12
|
+
secret = ShopifyApp.configuration.secret
|
13
|
+
digest = OpenSSL::Digest.new('sha256')
|
14
|
+
|
15
|
+
expected_hmac = Base64.strict_encode64(OpenSSL::HMAC.digest(digest, secret, request_body))
|
16
|
+
head(:unauthorized) unless ActiveSupport::SecurityUtils.secure_compare(expected_hmac, hmac_header)
|
17
|
+
end
|
18
|
+
end
|
@@ -3,9 +3,12 @@ module ShopifyApp
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
-
validates :shopify_domain, presence: true, uniqueness: { case_sensitive: false }
|
7
6
|
validates :shopify_token, presence: true
|
8
7
|
validates :api_version, presence: true
|
8
|
+
validates :shopify_domain, presence: true,
|
9
|
+
if: Proc.new {|_| ShopifyApp.configuration.per_user_tokens? }
|
10
|
+
validates :shopify_domain, presence: true, uniqueness: { case_sensitive: false },
|
11
|
+
if: Proc.new {|_| !ShopifyApp.configuration.per_user_tokens? }
|
9
12
|
end
|
10
13
|
|
11
14
|
def with_shopify_session(&block)
|
@@ -18,23 +21,19 @@ module ShopifyApp
|
|
18
21
|
end
|
19
22
|
|
20
23
|
class_methods do
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
|
25
|
+
def strategy_klass
|
26
|
+
ShopifyApp.configuration.per_user_tokens? ?
|
27
|
+
ShopifyApp::SessionStorage::UserStorageStrategy :
|
28
|
+
ShopifyApp::SessionStorage::ShopStorageStrategy
|
26
29
|
end
|
27
30
|
|
28
|
-
def
|
29
|
-
|
31
|
+
def store(auth_session, user: nil)
|
32
|
+
strategy_klass.store(auth_session, user)
|
33
|
+
end
|
30
34
|
|
31
|
-
|
32
|
-
|
33
|
-
domain: shop.shopify_domain,
|
34
|
-
token: shop.shopify_token,
|
35
|
-
api_version: shop.api_version
|
36
|
-
)
|
37
|
-
end
|
35
|
+
def retrieve(id)
|
36
|
+
strategy_klass.retrieve(id)
|
38
37
|
end
|
39
38
|
end
|
40
39
|
end
|