shopify_app 13.0.0 → 13.3.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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rubocop.yml +28 -0
  3. data/.rubocop.yml +13 -6
  4. data/.travis.yml +4 -3
  5. data/CHANGELOG.md +33 -0
  6. data/Gemfile +5 -0
  7. data/README.md +67 -38
  8. data/Rakefile +1 -0
  9. data/SECURITY.md +59 -0
  10. data/app/controllers/concerns/shopify_app/require_known_shop.rb +39 -0
  11. data/app/controllers/shopify_app/authenticated_controller.rb +1 -0
  12. data/app/controllers/shopify_app/callback_controller.rb +39 -8
  13. data/app/controllers/shopify_app/extension_verification_controller.rb +2 -7
  14. data/app/controllers/shopify_app/sessions_controller.rb +9 -6
  15. data/app/controllers/shopify_app/webhooks_controller.rb +6 -5
  16. data/config/locales/fi.yml +1 -1
  17. data/config/locales/nl.yml +7 -7
  18. data/config/routes.rb +1 -0
  19. data/docs/Quickstart.md +5 -14
  20. data/docs/Releasing.md +1 -0
  21. data/lib/generators/shopify_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb +5 -3
  22. data/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb +1 -0
  23. data/lib/generators/shopify_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +2 -1
  24. data/lib/generators/shopify_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +4 -4
  25. data/lib/generators/shopify_app/add_webhook/add_webhook_generator.rb +5 -4
  26. data/lib/generators/shopify_app/add_webhook/templates/{webhook_job.rb → webhook_job.rb.tt} +5 -0
  27. data/lib/generators/shopify_app/app_proxy_controller/app_proxy_controller_generator.rb +4 -3
  28. data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_controller.rb +3 -3
  29. data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_route.rb +10 -9
  30. data/lib/generators/shopify_app/controllers/controllers_generator.rb +1 -0
  31. data/lib/generators/shopify_app/home_controller/home_controller_generator.rb +4 -3
  32. data/lib/generators/shopify_app/install/install_generator.rb +10 -9
  33. data/lib/generators/shopify_app/install/templates/omniauth.rb +2 -1
  34. data/lib/generators/shopify_app/install/templates/{shopify_app.rb → shopify_app.rb.tt} +1 -1
  35. data/lib/generators/shopify_app/install/templates/user_agent.rb +2 -1
  36. data/lib/generators/shopify_app/routes/routes_generator.rb +1 -0
  37. data/lib/generators/shopify_app/routes/templates/routes.rb +10 -9
  38. data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +12 -7
  39. data/lib/generators/shopify_app/shop_model/templates/shop.rb +1 -0
  40. data/lib/generators/shopify_app/shopify_app_generator.rb +4 -3
  41. data/lib/generators/shopify_app/user_model/templates/user.rb +1 -0
  42. data/lib/generators/shopify_app/user_model/user_model_generator.rb +12 -7
  43. data/lib/generators/shopify_app/views/views_generator.rb +1 -0
  44. data/lib/shopify_app.rb +11 -5
  45. data/lib/shopify_app/configuration.rb +15 -8
  46. data/lib/shopify_app/controller_concerns/app_proxy_verification.rb +3 -3
  47. data/lib/shopify_app/controller_concerns/embedded_app.rb +3 -2
  48. data/lib/shopify_app/controller_concerns/localization.rb +1 -0
  49. data/lib/shopify_app/controller_concerns/login_protection.rb +52 -15
  50. data/lib/shopify_app/controller_concerns/payload_verification.rb +24 -0
  51. data/lib/shopify_app/controller_concerns/webhook_verification.rb +3 -18
  52. data/lib/shopify_app/engine.rb +5 -0
  53. data/lib/shopify_app/jobs/scripttags_manager_job.rb +1 -1
  54. data/lib/shopify_app/jobs/webhooks_manager_job.rb +1 -1
  55. data/lib/shopify_app/managers/scripttags_manager.rb +4 -3
  56. data/lib/shopify_app/managers/webhooks_manager.rb +4 -3
  57. data/lib/shopify_app/middleware/jwt_middleware.rb +42 -0
  58. data/lib/shopify_app/middleware/same_site_cookie_middleware.rb +2 -1
  59. data/lib/shopify_app/session/in_memory_session_store.rb +7 -3
  60. data/lib/shopify_app/session/in_memory_shop_session_store.rb +10 -0
  61. data/lib/shopify_app/session/in_memory_user_session_store.rb +10 -0
  62. data/lib/shopify_app/session/jwt.rb +61 -0
  63. data/lib/shopify_app/session/null_user_session_store.rb +22 -0
  64. data/lib/shopify_app/session/session_repository.rb +13 -16
  65. data/lib/shopify_app/session/session_storage.rb +1 -0
  66. data/lib/shopify_app/session/shop_session_storage.rb +21 -9
  67. data/lib/shopify_app/session/user_session_storage.rb +19 -8
  68. data/lib/shopify_app/test_helpers/all.rb +2 -0
  69. data/lib/shopify_app/test_helpers/webhook_verification_helper.rb +17 -0
  70. data/lib/shopify_app/utils.rb +6 -5
  71. data/lib/shopify_app/version.rb +2 -1
  72. data/package-lock.json +4 -4
  73. data/package.json +1 -1
  74. data/shopify_app.gemspec +12 -7
  75. data/yarn.lock +3 -3
  76. metadata +48 -10
@@ -2,6 +2,11 @@ class <%= @job_class_name %> < ActiveJob::Base
2
2
  def perform(shop_domain:, webhook:)
3
3
  shop = Shop.find_by(shopify_domain: shop_domain)
4
4
 
5
+ if shop.nil?
6
+ logger.error("#{self.class} failed: cannot find shop with domain '#{shop_domain}'")
7
+ return
8
+ end
9
+
5
10
  shop.with_shopify_session do
6
11
  end
7
12
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
 
3
4
  module ShopifyApp
@@ -6,11 +7,11 @@ module ShopifyApp
6
7
  source_root File.expand_path('../templates', __FILE__)
7
8
 
8
9
  def create_app_proxy_controller
9
- template 'app_proxy_controller.rb', 'app/controllers/app_proxy_controller.rb'
10
+ template('app_proxy_controller.rb', 'app/controllers/app_proxy_controller.rb')
10
11
  end
11
12
 
12
13
  def create_app_proxy_index_view
13
- copy_file 'index.html.erb', 'app/views/app_proxy/index.html.erb'
14
+ copy_file('index.html.erb', 'app/views/app_proxy/index.html.erb')
14
15
  end
15
16
 
16
17
  def add_app_proxy_route
@@ -18,7 +19,7 @@ module ShopifyApp
18
19
  'config/routes.rb',
19
20
  File.read(File.expand_path(find_in_source_paths('app_proxy_route.rb'))),
20
21
  after: "mount ShopifyApp::Engine, at: '/'\n"
21
- )
22
+ )
22
23
  end
23
24
  end
24
25
  end
@@ -1,8 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  class AppProxyController < ApplicationController
2
- include ShopifyApp::AppProxyVerification
3
+ include ShopifyApp::AppProxyVerification
3
4
 
4
5
  def index
5
- render layout: false, content_type: 'application/liquid'
6
+ render(layout: false, content_type: 'application/liquid')
6
7
  end
7
-
8
8
  end
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
1
2
 
2
- namespace :app_proxy do
3
- root action: 'index'
4
- # simple routes without a specified controller will go to AppProxyController
5
-
6
- # more complex routes will go to controllers in the AppProxy namespace
7
- # resources :reviews
8
- # GET /app_proxy/reviews will now be routed to
9
- # AppProxy::ReviewsController#index, for example
10
- end
3
+ namespace :app_proxy do
4
+ root action: 'index'
5
+ # simple routes without a specified controller will go to AppProxyController
6
+
7
+ # more complex routes will go to controllers in the AppProxy namespace
8
+ # resources :reviews
9
+ # GET /app_proxy/reviews will now be routed to
10
+ # AppProxy::ReviewsController#index, for example
11
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
 
3
4
  module ShopifyApp
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
 
3
4
  module ShopifyApp
@@ -6,15 +7,15 @@ module ShopifyApp
6
7
  source_root File.expand_path('../templates', __FILE__)
7
8
 
8
9
  def create_home_controller
9
- template 'home_controller.rb', 'app/controllers/home_controller.rb'
10
+ template('home_controller.rb', 'app/controllers/home_controller.rb')
10
11
  end
11
12
 
12
13
  def create_home_index_view
13
- copy_file 'index.html.erb', 'app/views/home/index.html.erb'
14
+ copy_file('index.html.erb', 'app/views/home/index.html.erb')
14
15
  end
15
16
 
16
17
  def add_home_index_route
17
- route "root :to => 'home#index'"
18
+ route("root :to => 'home#index'")
18
19
  end
19
20
 
20
21
  def embedded_app?
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
 
3
4
  module ShopifyApp
@@ -16,7 +17,7 @@ module ShopifyApp
16
17
  @scope = format_array_argument(options['scope'])
17
18
  @api_version = options['api_version'] || ShopifyAPI::Meta.admin_versions.find(&:latest_supported).handle
18
19
 
19
- template 'shopify_app.rb', 'config/initializers/shopify_app.rb'
20
+ template('shopify_app.rb', 'config/initializers/shopify_app.rb')
20
21
  end
21
22
 
22
23
  def create_session_store_initializer
@@ -24,28 +25,28 @@ module ShopifyApp
24
25
  end
25
26
 
26
27
  def create_and_inject_into_omniauth_initializer
27
- unless File.exist? "config/initializers/omniauth.rb"
28
- copy_file 'omniauth.rb', 'config/initializers/omniauth.rb'
28
+ unless File.exist?("config/initializers/omniauth.rb")
29
+ copy_file('omniauth.rb', 'config/initializers/omniauth.rb')
29
30
  end
30
31
 
31
32
  inject_into_file(
32
33
  'config/initializers/omniauth.rb',
33
34
  File.read(File.expand_path(find_in_source_paths('shopify_provider.rb'))),
34
- after: "Rails.application.config.middleware.use OmniAuth::Builder do\n"
35
+ after: "Rails.application.config.middleware.use(OmniAuth::Builder) do\n"
35
36
  )
36
37
  end
37
38
 
38
39
  def create_embedded_app_layout
39
40
  return unless embedded_app?
40
41
 
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'
42
+ copy_file('embedded_app.html.erb', 'app/views/layouts/embedded_app.html.erb')
43
+ copy_file('_flash_messages.html.erb', 'app/views/layouts/_flash_messages.html.erb')
43
44
 
44
45
  if ShopifyApp.use_webpacker?
45
46
  copy_file('shopify_app.js', 'app/javascript/shopify_app/shopify_app.js')
46
47
  copy_file('flash_messages.js', 'app/javascript/shopify_app/flash_messages.js')
47
48
  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
+ append_to_file('app/javascript/packs/application.js', "require(\"shopify_app\")\n")
49
50
  else
50
51
  copy_file('shopify_app.js', 'app/assets/javascripts/shopify_app.js')
51
52
  copy_file('flash_messages.js', 'app/assets/javascripts/flash_messages.js')
@@ -53,11 +54,11 @@ module ShopifyApp
53
54
  end
54
55
 
55
56
  def create_user_agent_initializer
56
- template 'user_agent.rb', 'config/initializers/user_agent.rb'
57
+ template('user_agent.rb', 'config/initializers/user_agent.rb')
57
58
  end
58
59
 
59
60
  def mount_engine
60
- route "mount ShopifyApp::Engine, at: '/'"
61
+ route("mount ShopifyApp::Engine, at: '/'")
61
62
  end
62
63
 
63
64
  def insert_hosts_into_development_config
@@ -1,2 +1,3 @@
1
- Rails.application.config.middleware.use OmniAuth::Builder do
1
+ # frozen_string_literal: true
2
+ Rails.application.config.middleware.use(OmniAuth::Builder) do
2
3
  end
@@ -8,7 +8,7 @@ ShopifyApp.configure do |config|
8
8
  config.embedded_app = <%= embedded_app? %>
9
9
  config.after_authenticate_job = false
10
10
  config.api_version = "<%= @api_version %>"
11
- config.shop_session_repository = 'ShopifyApp::InMemoryShopSessionStore'
11
+ config.shop_session_repository = 'Shop'
12
12
  end
13
13
 
14
14
  # ShopifyApp::Utils.fetch_known_api_versions # Uncomment to fetch known api versions from shopify servers on boot
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  module ShopifyAPI
2
3
  class Base < ActiveResource::Base
3
- self.headers['User-Agent'] << " | ShopifyApp/#{ShopifyApp::VERSION}"
4
+ headers['User-Agent'] << " | ShopifyApp/#{ShopifyApp::VERSION}"
4
5
  end
5
6
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
 
3
4
  module ShopifyApp
@@ -1,11 +1,12 @@
1
+ # frozen_string_literal: true
1
2
 
2
- controller :sessions do
3
- get 'login' => :new, :as => :login
4
- post 'login' => :create, :as => :authenticate
5
- get 'auth/shopify/callback' => :callback
6
- get 'logout' => :destroy, :as => :logout
7
- end
3
+ controller :sessions do
4
+ get 'login' => :new, :as => :login
5
+ post 'login' => :create, :as => :authenticate
6
+ get 'auth/shopify/callback' => :callback
7
+ get 'logout' => :destroy, :as => :logout
8
+ end
8
9
 
9
- namespace :webhooks do
10
- post ':type' => :receive
11
- end
10
+ namespace :webhooks do
11
+ post ':type' => :receive
12
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
  require 'rails/generators/active_record'
3
4
 
@@ -8,19 +9,19 @@ module ShopifyApp
8
9
  source_root File.expand_path('../templates', __FILE__)
9
10
 
10
11
  def create_shop_model
11
- copy_file 'shop.rb', 'app/models/shop.rb'
12
+ copy_file('shop.rb', 'app/models/shop.rb')
12
13
  end
13
14
 
14
15
  def create_shop_migration
15
- migration_template 'db/migrate/create_shops.erb', 'db/migrate/create_shops.rb'
16
+ migration_template('db/migrate/create_shops.erb', 'db/migrate/create_shops.rb')
16
17
  end
17
18
 
18
19
  def update_shopify_app_initializer
19
- gsub_file 'config/initializers/shopify_app.rb', 'ShopifyApp::InMemoryShopSessionStore', 'Shop'
20
+ gsub_file('config/initializers/shopify_app.rb', 'ShopifyApp::InMemoryShopSessionStore', 'Shop')
20
21
  end
21
22
 
22
23
  def create_shop_fixtures
23
- copy_file 'shops.yml', 'test/fixtures/shops.yml'
24
+ copy_file('shops.yml', 'test/fixtures/shops.yml')
24
25
  end
25
26
 
26
27
  private
@@ -29,9 +30,13 @@ module ShopifyApp
29
30
  Rails.version.match(/\d\.\d/)[0]
30
31
  end
31
32
 
32
- # for generating a timestamp when using `create_migration`
33
- def self.next_migration_number(dir)
34
- ActiveRecord::Generators::Base.next_migration_number(dir)
33
+ class << self
34
+ private :next_migration_number
35
+
36
+ # for generating a timestamp when using `create_migration`
37
+ def next_migration_number(dir)
38
+ ActiveRecord::Generators::Base.next_migration_number(dir)
39
+ end
35
40
  end
36
41
  end
37
42
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class Shop < ActiveRecord::Base
2
3
  include ShopifyApp::ShopSessionStorage
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module ShopifyApp
2
3
  module Generators
3
4
  class ShopifyAppGenerator < Rails::Generators::Base
@@ -7,10 +8,10 @@ module ShopifyApp
7
8
  end
8
9
 
9
10
  def run_all_generators
10
- generate "shopify_app:install #{@opts.join(' ')}"
11
- generate "shopify_app:shop_model"
11
+ generate("shopify_app:install #{@opts.join(' ')}")
12
+ generate("shopify_app:shop_model")
12
13
  generate("shopify_app:authenticated_controller")
13
- generate "shopify_app:home_controller"
14
+ generate("shopify_app:home_controller")
14
15
  end
15
16
  end
16
17
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class User < ActiveRecord::Base
2
3
  include ShopifyApp::UserSessionStorage
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
  require 'rails/generators/active_record'
3
4
 
@@ -8,19 +9,19 @@ module ShopifyApp
8
9
  source_root File.expand_path('../templates', __FILE__)
9
10
 
10
11
  def create_user_model
11
- copy_file 'user.rb', 'app/models/user.rb'
12
+ copy_file('user.rb', 'app/models/user.rb')
12
13
  end
13
14
 
14
15
  def create_user_migration
15
- migration_template 'db/migrate/create_users.erb', 'db/migrate/create_users.rb'
16
+ migration_template('db/migrate/create_users.erb', 'db/migrate/create_users.rb')
16
17
  end
17
18
 
18
19
  def update_shopify_app_initializer
19
- gsub_file 'config/initializers/shopify_app.rb', 'ShopifyApp::InMemoryUserSessionStore', 'User'
20
+ gsub_file('config/initializers/shopify_app.rb', 'ShopifyApp::InMemoryUserSessionStore', 'User')
20
21
  end
21
22
 
22
23
  def create_user_fixtures
23
- copy_file 'users.yml', 'test/fixtures/users.yml'
24
+ copy_file('users.yml', 'test/fixtures/users.yml')
24
25
  end
25
26
 
26
27
  private
@@ -29,9 +30,13 @@ module ShopifyApp
29
30
  Rails.version.match(/\d\.\d/)[0]
30
31
  end
31
32
 
32
- # for generating a timestamp when using `create_migration`
33
- def self.next_migration_number(dir)
34
- ActiveRecord::Generators::Base.next_migration_number(dir)
33
+ class << self
34
+ private :next_migration_number
35
+
36
+ # for generating a timestamp when using `create_migration`
37
+ def next_migration_number(dir)
38
+ ActiveRecord::Generators::Base.next_migration_number(dir)
39
+ end
35
40
  end
36
41
  end
37
42
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
 
3
4
  module ShopifyApp
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  require 'shopify_app/version'
2
3
 
3
4
  # deps
4
5
  require 'shopify_api'
5
6
  require 'omniauth-shopify-oauth2'
7
+ require 'redirect_safely'
6
8
 
7
9
  module ShopifyApp
8
10
  def self.rails6?
@@ -29,8 +31,9 @@ module ShopifyApp
29
31
  require 'shopify_app/controller_concerns/itp'
30
32
  require 'shopify_app/controller_concerns/login_protection'
31
33
  require 'shopify_app/controller_concerns/embedded_app'
32
- require 'shopify_app/controller_concerns/webhook_verification'
34
+ require 'shopify_app/controller_concerns/payload_verification'
33
35
  require 'shopify_app/controller_concerns/app_proxy_verification'
36
+ require 'shopify_app/controller_concerns/webhook_verification'
34
37
 
35
38
  # jobs
36
39
  require 'shopify_app/jobs/webhooks_manager_job'
@@ -41,14 +44,17 @@ module ShopifyApp
41
44
  require 'shopify_app/managers/scripttags_manager'
42
45
 
43
46
  # middleware
47
+ require 'shopify_app/middleware/jwt_middleware'
44
48
  require 'shopify_app/middleware/same_site_cookie_middleware'
45
49
 
46
50
  # session
47
- require 'shopify_app/session/session_storage'
48
- require 'shopify_app/session/shop_session_storage'
49
- require 'shopify_app/session/user_session_storage'
50
- require 'shopify_app/session/session_repository'
51
51
  require 'shopify_app/session/in_memory_session_store'
52
52
  require 'shopify_app/session/in_memory_shop_session_store'
53
53
  require 'shopify_app/session/in_memory_user_session_store'
54
+ require 'shopify_app/session/jwt'
55
+ require 'shopify_app/session/null_user_session_store'
56
+ require 'shopify_app/session/session_repository'
57
+ require 'shopify_app/session/session_storage'
58
+ require 'shopify_app/session/shop_session_storage'
59
+ require 'shopify_app/session/user_session_storage'
54
60
  end
@@ -1,11 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  module ShopifyApp
2
3
  class Configuration
3
-
4
4
  # Shopify App settings. These values should match the configuration
5
5
  # for the app in your Shopify Partners page. Change your settings in
6
6
  # `config/initializers/shopify_app.rb`
7
7
  attr_accessor :application_name
8
- attr_accessor :api_key
8
+ attr_accessor :api_key
9
9
  attr_accessor :secret
10
10
  attr_accessor :old_secret
11
11
  attr_accessor :scope
@@ -14,13 +14,11 @@ module ShopifyApp
14
14
  attr_accessor :webhooks
15
15
  attr_accessor :scripttags
16
16
  attr_accessor :after_authenticate_job
17
- attr_reader :shop_session_repository
18
- attr_reader :user_session_repository
19
17
  attr_accessor :api_version
20
18
 
21
19
  # customise urls
22
20
  attr_accessor :root_url
23
- attr_accessor :login_url
21
+ attr_writer :login_url
24
22
 
25
23
  # customise ActiveJob queue names
26
24
  attr_accessor :scripttags_manager_queue_name
@@ -36,7 +34,10 @@ module ShopifyApp
36
34
  attr_accessor :webhook_jobs_namespace
37
35
 
38
36
  # allow enabling of same site none on cookies
39
- attr_accessor :enable_same_site_none
37
+ attr_writer :enable_same_site_none
38
+
39
+ # allow enabling jwt headers for authentication
40
+ attr_accessor :allow_jwt_authentication
40
41
 
41
42
  def initialize
42
43
  @root_url = '/'
@@ -51,15 +52,21 @@ module ShopifyApp
51
52
  end
52
53
 
53
54
  def user_session_repository=(klass)
54
- @user_session_repository = klass
55
55
  ShopifyApp::SessionRepository.user_storage = klass
56
56
  end
57
57
 
58
+ def user_session_repository
59
+ ShopifyApp::SessionRepository.user_storage
60
+ end
61
+
58
62
  def shop_session_repository=(klass)
59
- @shop_session_repository = klass
60
63
  ShopifyApp::SessionRepository.shop_storage = klass
61
64
  end
62
65
 
66
+ def shop_session_repository
67
+ ShopifyApp::SessionRepository.shop_storage
68
+ end
69
+
63
70
  def has_webhooks?
64
71
  webhooks.present?
65
72
  end