duodealer_app 1.0.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 (212) hide show
  1. checksums.yaml +7 -0
  2. data/.!66854!duodealer_app.gemspec +0 -0
  3. data/.babelrc +5 -0
  4. data/.gitignore +16 -0
  5. data/.nvmrc +1 -0
  6. data/.rubocop.yml +263 -0
  7. data/.ruby-version +1 -0
  8. data/.travis.yml +27 -0
  9. data/Gemfile +8 -0
  10. data/LICENSE +19 -0
  11. data/README.md +553 -0
  12. data/Rakefile +6 -0
  13. data/app/assets/images/storage_access.svg +2 -0
  14. data/app/assets/javascripts/duodealer_app/enable_cookies.js +3 -0
  15. data/app/assets/javascripts/duodealer_app/itp_helper.js +40 -0
  16. data/app/assets/javascripts/duodealer_app/partition_cookies.js +8 -0
  17. data/app/assets/javascripts/duodealer_app/redirect.js +33 -0
  18. data/app/assets/javascripts/duodealer_app/request_storage_access.js +3 -0
  19. data/app/assets/javascripts/duodealer_app/storage_access.js +153 -0
  20. data/app/assets/javascripts/duodealer_app/storage_access_redirect.js +17 -0
  21. data/app/assets/javascripts/duodealer_app/top_level.js +2 -0
  22. data/app/assets/javascripts/duodealer_app/top_level_interaction.js +11 -0
  23. data/app/controllers/concerns/duodealer_app/authenticated.rb +15 -0
  24. data/app/controllers/concerns/duodealer_app/authenticated.rb-e +15 -0
  25. data/app/controllers/duodealer_app/authenticated_controller.rb +9 -0
  26. data/app/controllers/duodealer_app/authenticated_controller.rb-e +9 -0
  27. data/app/controllers/duodealer_app/callback_controller.rb +104 -0
  28. data/app/controllers/duodealer_app/callback_controller.rb-e +104 -0
  29. data/app/controllers/duodealer_app/extension_verification_controller.rb +19 -0
  30. data/app/controllers/duodealer_app/extension_verification_controller.rb-e +19 -0
  31. data/app/controllers/duodealer_app/sessions_controller.rb +159 -0
  32. data/app/controllers/duodealer_app/sessions_controller.rb-e +159 -0
  33. data/app/controllers/duodealer_app/webhooks_controller.rb +37 -0
  34. data/app/controllers/duodealer_app/webhooks_controller.rb-e +37 -0
  35. data/app/views/duodealer_app/partials/_button_styles.html.erb +104 -0
  36. data/app/views/duodealer_app/partials/_button_styles.html.erb-e +104 -0
  37. data/app/views/duodealer_app/partials/_card_styles.html.erb +33 -0
  38. data/app/views/duodealer_app/partials/_card_styles.html.erb-e +33 -0
  39. data/app/views/duodealer_app/partials/_empty_state_styles.html.erb +129 -0
  40. data/app/views/duodealer_app/partials/_empty_state_styles.html.erb-e +129 -0
  41. data/app/views/duodealer_app/partials/_layout_styles.html.erb +167 -0
  42. data/app/views/duodealer_app/partials/_layout_styles.html.erb-e +167 -0
  43. data/app/views/duodealer_app/partials/_typography_styles.html.erb +35 -0
  44. data/app/views/duodealer_app/partials/_typography_styles.html.erb-e +35 -0
  45. data/app/views/duodealer_app/sessions/enable_cookies.html.erb +75 -0
  46. data/app/views/duodealer_app/sessions/enable_cookies.html.erb-e +75 -0
  47. data/app/views/duodealer_app/sessions/new.html.erb +123 -0
  48. data/app/views/duodealer_app/sessions/new.html.erb-e +123 -0
  49. data/app/views/duodealer_app/sessions/request_storage_access.html.erb +68 -0
  50. data/app/views/duodealer_app/sessions/request_storage_access.html.erb-e +68 -0
  51. data/app/views/duodealer_app/sessions/top_level_interaction.html.erb +64 -0
  52. data/app/views/duodealer_app/sessions/top_level_interaction.html.erb-e +64 -0
  53. data/app/views/duodealer_app/shared/redirect.html.erb +23 -0
  54. data/app/views/duodealer_app/shared/redirect.html.erb-e +23 -0
  55. data/config/locales/cs.yml +23 -0
  56. data/config/locales/da.yml +20 -0
  57. data/config/locales/de.yml +22 -0
  58. data/config/locales/en.yml +15 -0
  59. data/config/locales/es.yml +22 -0
  60. data/config/locales/fi.yml +20 -0
  61. data/config/locales/fr.yml +23 -0
  62. data/config/locales/hi.yml +23 -0
  63. data/config/locales/it.yml +21 -0
  64. data/config/locales/ja.yml +17 -0
  65. data/config/locales/ko.yml +19 -0
  66. data/config/locales/ms.yml +22 -0
  67. data/config/locales/nb.yml +21 -0
  68. data/config/locales/nl.yml +21 -0
  69. data/config/locales/pl.yml +21 -0
  70. data/config/locales/pt-BR.yml +21 -0
  71. data/config/locales/pt-PT.yml +22 -0
  72. data/config/locales/sv.yml +21 -0
  73. data/config/locales/th.yml +20 -0
  74. data/config/locales/tr.yml +22 -0
  75. data/config/locales/zh-CN.yml +16 -0
  76. data/config/locales/zh-TW.yml +16 -0
  77. data/config/routes.rb +22 -0
  78. data/docs/.!20385!test-your-app.png +0 -0
  79. data/docs/.!20388!install-on-dev-shop.png +0 -0
  80. data/docs/.!62511!test-your-app.png +0 -0
  81. data/docs/.!62512!install-on-dev-shop.png +0 -0
  82. data/docs/.!62763!test-your-app.png +0 -0
  83. data/docs/.!62765!install-on-dev-shop.png +0 -0
  84. data/docs/.!63018!test-your-app.png +0 -0
  85. data/docs/.!63020!install-on-dev-shop.png +0 -0
  86. data/docs/.!63289!test-your-app.png +0 -0
  87. data/docs/.!63291!install-on-dev-shop.png +0 -0
  88. data/docs/.!63562!test-your-app.png +0 -0
  89. data/docs/.!63564!install-on-dev-shop.png +0 -0
  90. data/docs/.!63872!test-your-app.png +0 -0
  91. data/docs/.!63874!install-on-dev-shop.png +0 -0
  92. data/docs/.!64151!test-your-app.png +0 -0
  93. data/docs/.!64153!install-on-dev-shop.png +0 -0
  94. data/docs/.!64428!test-your-app.png +0 -0
  95. data/docs/.!64431!install-on-dev-shop.png +0 -0
  96. data/docs/.!64737!test-your-app.png +0 -0
  97. data/docs/.!64740!install-on-dev-shop.png +0 -0
  98. data/docs/.!65025!test-your-app.png +0 -0
  99. data/docs/.!65028!install-on-dev-shop.png +0 -0
  100. data/docs/.!65324!test-your-app.png +0 -0
  101. data/docs/.!65327!install-on-dev-shop.png +0 -0
  102. data/docs/.!65626!test-your-app.png +0 -0
  103. data/docs/.!65629!install-on-dev-shop.png +0 -0
  104. data/docs/.!65942!test-your-app.png +0 -0
  105. data/docs/.!65945!install-on-dev-shop.png +0 -0
  106. data/docs/.!66760!test-your-app.png +0 -0
  107. data/docs/.!66763!install-on-dev-shop.png +0 -0
  108. data/docs/.!67028!test-your-app.png +0 -0
  109. data/docs/.!67031!install-on-dev-shop.png +0 -0
  110. data/docs/.!67657!test-your-app.png +0 -0
  111. data/docs/.!67660!install-on-dev-shop.png +0 -0
  112. data/docs/.!68031!test-your-app.png +0 -0
  113. data/docs/.!68034!install-on-dev-shop.png +0 -0
  114. data/docs/.!68363!test-your-app.png +0 -0
  115. data/docs/.!68366!install-on-dev-shop.png +0 -0
  116. data/docs/Quickstart.md +103 -0
  117. data/docs/Releasing.md +17 -0
  118. data/docs/Troubleshooting.md +16 -0
  119. data/docs/install-on-dev-shop.png +0 -0
  120. data/docs/test-your-app.png +0 -0
  121. data/duodealer_app.gemspec +34 -0
  122. data/images/.!20334!app-proxy-screenshot.png +0 -0
  123. data/images/.!62504!app-proxy-screenshot.png +0 -0
  124. data/images/.!62754!app-proxy-screenshot.png +0 -0
  125. data/images/.!63008!app-proxy-screenshot.png +0 -0
  126. data/images/.!63277!app-proxy-screenshot.png +0 -0
  127. data/images/.!63548!app-proxy-screenshot.png +0 -0
  128. data/images/.!63855!app-proxy-screenshot.png +0 -0
  129. data/images/.!64132!app-proxy-screenshot.png +0 -0
  130. data/images/.!64407!app-proxy-screenshot.png +0 -0
  131. data/images/.!64714!app-proxy-screenshot.png +0 -0
  132. data/images/.!65000!app-proxy-screenshot.png +0 -0
  133. data/images/.!65296!app-proxy-screenshot.png +0 -0
  134. data/images/.!65594!app-proxy-screenshot.png +0 -0
  135. data/images/.!65908!app-proxy-screenshot.png +0 -0
  136. data/images/.!66724!app-proxy-screenshot.png +0 -0
  137. data/images/.!66989!app-proxy-screenshot.png +0 -0
  138. data/images/.!67614!app-proxy-screenshot.png +0 -0
  139. data/images/.!67986!app-proxy-screenshot.png +0 -0
  140. data/images/.!68314!app-proxy-screenshot.png +0 -0
  141. data/images/app-proxy-screenshot.png +0 -0
  142. data/karma.conf.js +44 -0
  143. data/lib/duodealer_app.rb +54 -0
  144. data/lib/duodealer_app/configuration.rb +85 -0
  145. data/lib/duodealer_app/controller_concerns/app_proxy_verification.rb +39 -0
  146. data/lib/duodealer_app/controller_concerns/embedded_app.rb +20 -0
  147. data/lib/duodealer_app/controller_concerns/itp.rb +44 -0
  148. data/lib/duodealer_app/controller_concerns/localization.rb +23 -0
  149. data/lib/duodealer_app/controller_concerns/login_protection.rb +180 -0
  150. data/lib/duodealer_app/controller_concerns/webhook_verification.rb +39 -0
  151. data/lib/duodealer_app/engine.rb +22 -0
  152. data/lib/duodealer_app/jobs/scripttags_manager_job.rb +17 -0
  153. data/lib/duodealer_app/jobs/webhooks_manager_job.rb +17 -0
  154. data/lib/duodealer_app/managers/scripttags_manager.rb +78 -0
  155. data/lib/duodealer_app/managers/webhooks_manager.rb +62 -0
  156. data/lib/duodealer_app/middleware/same_site_cookie_middleware.rb +69 -0
  157. data/lib/duodealer_app/session/in_memory_session_store.rb +29 -0
  158. data/lib/duodealer_app/session/session_repository.rb +33 -0
  159. data/lib/duodealer_app/session/session_storage.rb +31 -0
  160. data/lib/duodealer_app/session/storage_strategies/shop_storage_strategy.rb +25 -0
  161. data/lib/duodealer_app/session/storage_strategies/user_storage_strategy.rb +26 -0
  162. data/lib/duodealer_app/utils.rb +24 -0
  163. data/lib/duodealer_app/version.rb +5 -0
  164. data/lib/generators/duodealer_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb +46 -0
  165. data/lib/generators/duodealer_app/add_after_authenticate_job/templates/after_authenticate_job.rb +10 -0
  166. data/lib/generators/duodealer_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +40 -0
  167. data/lib/generators/duodealer_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +62 -0
  168. data/lib/generators/duodealer_app/add_webhook/add_webhook_generator.rb +69 -0
  169. data/lib/generators/duodealer_app/add_webhook/templates/webhook_job.rb +8 -0
  170. data/lib/generators/duodealer_app/app_proxy_controller/app_proxy_controller_generator.rb +27 -0
  171. data/lib/generators/duodealer_app/app_proxy_controller/templates/app_proxy_controller.rb +9 -0
  172. data/lib/generators/duodealer_app/app_proxy_controller/templates/app_proxy_route.rb +10 -0
  173. data/lib/generators/duodealer_app/app_proxy_controller/templates/index.html.erb +19 -0
  174. data/lib/generators/duodealer_app/authenticated_controller/authenticated_controller_generator.rb +15 -0
  175. data/lib/generators/duodealer_app/authenticated_controller/templates/authenticated_controller.rb +5 -0
  176. data/lib/generators/duodealer_app/controllers/controllers_generator.rb +30 -0
  177. data/lib/generators/duodealer_app/duodealer_app_generator.rb +19 -0
  178. data/lib/generators/duodealer_app/home_controller/home_controller_generator.rb +27 -0
  179. data/lib/generators/duodealer_app/home_controller/templates/home_controller.rb +8 -0
  180. data/lib/generators/duodealer_app/home_controller/templates/index.html.erb +21 -0
  181. data/lib/generators/duodealer_app/install/install_generator.rb +83 -0
  182. data/lib/generators/duodealer_app/install/templates/_flash_messages.html.erb +3 -0
  183. data/lib/generators/duodealer_app/install/templates/duodealer_app.js +15 -0
  184. data/lib/generators/duodealer_app/install/templates/duodealer_app.rb +15 -0
  185. data/lib/generators/duodealer_app/install/templates/duodealer_app_index.js +2 -0
  186. data/lib/generators/duodealer_app/install/templates/duodealer_provider.rb +20 -0
  187. data/lib/generators/duodealer_app/install/templates/embedded_app.html.erb +41 -0
  188. data/lib/generators/duodealer_app/install/templates/flash_messages.js +26 -0
  189. data/lib/generators/duodealer_app/install/templates/omniauth.rb +2 -0
  190. data/lib/generators/duodealer_app/install/templates/session_store.rb +4 -0
  191. data/lib/generators/duodealer_app/install/templates/user_agent.rb +5 -0
  192. data/lib/generators/duodealer_app/rotate_duodealer_token_job/rotate_duodealer_token_job_generator.rb +16 -0
  193. data/lib/generators/duodealer_app/rotate_duodealer_token_job/templates/rotate_duodealer_token.rake +17 -0
  194. data/lib/generators/duodealer_app/rotate_duodealer_token_job/templates/rotate_duodealer_token_job.rb +42 -0
  195. data/lib/generators/duodealer_app/routes/routes_generator.rb +32 -0
  196. data/lib/generators/duodealer_app/routes/templates/routes.rb +11 -0
  197. data/lib/generators/duodealer_app/shop_model/shop_model_generator.rb +39 -0
  198. data/lib/generators/duodealer_app/shop_model/templates/db/migrate/create_shops.erb +15 -0
  199. data/lib/generators/duodealer_app/shop_model/templates/shop.rb +7 -0
  200. data/lib/generators/duodealer_app/shop_model/templates/shops.yml +3 -0
  201. data/lib/generators/duodealer_app/user_model/templates/db/migrate/create_users.erb +16 -0
  202. data/lib/generators/duodealer_app/user_model/templates/user.rb +7 -0
  203. data/lib/generators/duodealer_app/user_model/templates/users.yml +4 -0
  204. data/lib/generators/duodealer_app/user_model/user_model_generator.rb +39 -0
  205. data/lib/generators/duodealer_app/views/views_generator.rb +30 -0
  206. data/package-lock.json +7224 -0
  207. data/package.json +28 -0
  208. data/shipit.rubygems.yml +4 -0
  209. data/translation.yml +7 -0
  210. data/webpack.config.js +24 -0
  211. data/yarn.lock +5263 -0
  212. metadata +447 -0
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ class WebhooksManager
5
+ class CreationFailed < StandardError; end
6
+
7
+ def self.queue(shop_domain, shop_token, webhooks)
8
+ DuodealerApp::WebhooksManagerJob.perform_later(
9
+ shop_domain: shop_domain,
10
+ shop_token: shop_token,
11
+ webhooks: webhooks
12
+ )
13
+ end
14
+
15
+ attr_reader :required_webhooks
16
+
17
+ def initialize(webhooks)
18
+ @required_webhooks = webhooks
19
+ end
20
+
21
+ def recreate_webhooks!
22
+ destroy_webhooks
23
+ create_webhooks
24
+ end
25
+
26
+ def create_webhooks
27
+ return if required_webhooks.blank?
28
+
29
+ required_webhooks.each do |webhook|
30
+ create_webhook(webhook) unless webhook_exists?(webhook[:topic])
31
+ end
32
+ end
33
+
34
+ def destroy_webhooks
35
+ DuodealerAPI::Webhook.all.to_a.each do |webhook|
36
+ DuodealerAPI::Webhook.delete(webhook.id) if is_required_webhook?(webhook)
37
+ end
38
+
39
+ @current_webhooks = nil
40
+ end
41
+
42
+ private
43
+ def is_required_webhook?(webhook)
44
+ required_webhooks.map { |w| w[:address] }.include? webhook.address
45
+ end
46
+
47
+ def create_webhook(attributes)
48
+ attributes.reverse_merge!(format: "json")
49
+ webhook = DuodealerAPI::Webhook.create(attributes)
50
+ raise CreationFailed, webhook.errors.full_messages.to_sentence unless webhook.persisted?
51
+ webhook
52
+ end
53
+
54
+ def webhook_exists?(topic)
55
+ current_webhooks[topic]
56
+ end
57
+
58
+ def current_webhooks
59
+ @current_webhooks ||= DuodealerAPI::Webhook.all.to_a.index_by(&:topic)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ class SameSiteCookieMiddleware
5
+ COOKIE_SEPARATOR = "\n"
6
+
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ status, headers, body = @app.call(env)
13
+ user_agent = env["HTTP_USER_AGENT"]
14
+
15
+ if headers && headers["Set-Cookie"] &&
16
+ !SameSiteCookieMiddleware.same_site_none_incompatible?(user_agent) &&
17
+ DuodealerApp.configuration.enable_same_site_none
18
+
19
+ set_cookies = headers["Set-Cookie"]
20
+ .split(COOKIE_SEPARATOR)
21
+ .compact
22
+ .map do |cookie|
23
+ cookie << "; Secure" if not cookie =~ /;\s*secure/i
24
+ cookie << "; SameSite=None" unless /;\s*samesite=/i.match?(cookie)
25
+ cookie
26
+ end
27
+
28
+ headers["Set-Cookie"] = set_cookies.join(COOKIE_SEPARATOR)
29
+ end
30
+
31
+ [status, headers, body]
32
+ end
33
+
34
+ def self.same_site_none_incompatible?(user_agent)
35
+ sniffer = BrowserSniffer.new(user_agent)
36
+
37
+ webkit_same_site_bug?(sniffer) || drops_unrecognized_same_site_cookies?(sniffer)
38
+ rescue
39
+ true
40
+ end
41
+
42
+ def self.webkit_same_site_bug?(sniffer)
43
+ (sniffer.os == :ios && sniffer.os_version.match(/^([0-9]|1[12])[\.\_]/)) ||
44
+ (sniffer.os == :mac && sniffer.browser == :safari && sniffer.os_version.match(/^10[\.\_]14/))
45
+ end
46
+
47
+ def self.drops_unrecognized_same_site_cookies?(sniffer)
48
+ (chromium_based?(sniffer) && sniffer.major_browser_version >= 51 && sniffer.major_browser_version <= 66) ||
49
+ (uc_browser?(sniffer) && !uc_browser_version_at_least?(sniffer: sniffer, major: 12, minor: 13, build: 2))
50
+ end
51
+
52
+ def self.chromium_based?(sniffer)
53
+ sniffer.browser_name.downcase.match(/chrom(e|ium)/)
54
+ end
55
+
56
+ def self.uc_browser?(sniffer)
57
+ sniffer.user_agent.downcase.match(/uc\s?browser/)
58
+ end
59
+
60
+ def self.uc_browser_version_at_least?(sniffer:, major:, minor:, build:)
61
+ digits = sniffer.browser_version.split(".").map(&:to_i)
62
+ return false unless digits.count >= 3
63
+
64
+ return digits[0] > major if digits[0] != major
65
+ return digits[1] > minor if digits[1] != minor
66
+ digits[2] >= build
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ class InMemorySessionStore
5
+ class EnvironmentError < StandardError; end
6
+
7
+ def self.retrieve(id)
8
+ repo[id]
9
+ end
10
+
11
+ def self.store(session, *args)
12
+ id = SecureRandom.uuid
13
+ repo[id] = session
14
+ id
15
+ end
16
+
17
+ def self.clear
18
+ @@repo = nil
19
+ end
20
+
21
+ def self.repo
22
+ if Rails.env.production?
23
+ raise EnvironmentError.new("Cannot use InMemorySessionStore in a Production environment. \
24
+ Please initialize DuodealerApp with a model that can store and retrieve sessions")
25
+ end
26
+ @@repo ||= {}
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ class SessionRepository
5
+ class ConfigurationError < StandardError; end
6
+
7
+ class << self
8
+ def storage=(storage)
9
+ @storage = storage
10
+
11
+ unless storage.nil? || self.storage.respond_to?(:store) && self.storage.respond_to?(:retrieve)
12
+ raise ArgumentError, "storage must respond to :store and :retrieve"
13
+ end
14
+ end
15
+
16
+ delegate :retrieve, to: :storage
17
+
18
+ def store(session, *args)
19
+ storage.store(session, *args)
20
+ end
21
+
22
+ def storage
23
+ load_storage || raise(ConfigurationError.new("DuodealerSessionRepository.storage is not configured!"))
24
+ end
25
+
26
+ private
27
+ def load_storage
28
+ return unless @storage
29
+ @storage.respond_to?(:safe_constantize) ? @storage.safe_constantize : @storage
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ module SessionStorage
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ if DuodealerApp.configuration.per_user_tokens?
9
+ extend DuodealerApp::SessionStorage::UserStorageStrategy
10
+ else
11
+ extend DuodealerApp::SessionStorage::ShopStorageStrategy
12
+ end
13
+
14
+ validates :duodealer_token, presence: true
15
+ validates :api_version, presence: true
16
+ validates :duodealer_domain, presence: true,
17
+ if: Proc.new { |_| DuodealerApp.configuration.per_user_tokens? }
18
+ validates :duodealer_domain, presence: true, uniqueness: { case_sensitive: false },
19
+ if: Proc.new { |_| !DuodealerApp.configuration.per_user_tokens? }
20
+ end
21
+
22
+ def with_duodealer_session(&block)
23
+ DuodealerAPI::Session.temp(
24
+ domain: duodealer_domain,
25
+ token: duodealer_token,
26
+ api_version: api_version,
27
+ &block
28
+ )
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ module SessionStorage
5
+ module ShopStorageStrategy
6
+ def store(auth_session, *args)
7
+ shop = find_or_initialize_by(duodealer_domain: auth_session.domain)
8
+ shop.duodealer_token = auth_session.token
9
+ shop.save!
10
+ shop.id
11
+ end
12
+
13
+ def retrieve(id)
14
+ return unless id
15
+ if shop = self.find_by(id: id)
16
+ DuodealerAPI::Session.new(
17
+ domain: shop.duodealer_domain,
18
+ token: shop.duodealer_token,
19
+ api_version: shop.api_version
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ module SessionStorage
5
+ module UserStorageStrategy
6
+ def store(auth_session, user)
7
+ user = find_or_initialize_by(duodealer_user_id: user[:id])
8
+ user.duodealer_token = auth_session.token
9
+ user.duodealer_domain = auth_session.domain
10
+ user.save!
11
+ user.id
12
+ end
13
+
14
+ def retrieve(id)
15
+ return unless id
16
+ if user = self.find_by(duodealer_user_id: id)
17
+ DuodealerAPI::Session.new(
18
+ domain: user.duodealer_domain,
19
+ token: user.duodealer_token,
20
+ api_version: user.api_version
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ module Utils
5
+ def self.sanitize_shop_domain(shop_domain)
6
+ name = shop_domain.to_s.downcase.strip
7
+ name += ".#{DuodealerApp.configuration.duodealer_domain}" if !name.include?("#{DuodealerApp.configuration.duodealer_domain}") && !name.include?(".")
8
+ name.sub!(%r|https?://|, "")
9
+
10
+ u = URI("http://#{name}")
11
+ u.host if u.host&.match(/^[a-z0-9][a-z0-9\-]*[a-z0-9]\.#{Regexp.escape(DuodealerApp.configuration.duodealer_domain)}$/)
12
+ rescue URI::InvalidURIError
13
+ nil
14
+ end
15
+
16
+ def self.fetch_known_api_versions
17
+ Rails.logger.info("[DuodealerAPI::ApiVersion] Fetching known Admin API Versions from Duo Dealer...")
18
+ DuodealerAPI::ApiVersion.fetch_known_versions
19
+ Rails.logger.info("[DuodealerAPI::ApiVersion] Known API Versions: #{DuodealerAPI::ApiVersion.versions.keys}")
20
+ rescue ActiveResource::ConnectionError
21
+ logger.error("[DuodealerAPI::ApiVersion] Unable to fetch api_versions from Duo Dealer")
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DuodealerApp
4
+ VERSION = "1.0.0"
5
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module DuodealerApp
6
+ module Generators
7
+ class AddAfterAuthenticateJobGenerator < Rails::Generators::Base
8
+ source_root File.expand_path("../templates", __FILE__)
9
+
10
+ hook_for :test_framework, as: :job, in: :rails do |instance, generator|
11
+ instance.invoke generator, [ instance.send(:job_file_name) ]
12
+ end
13
+
14
+ def init_after_authenticate_config
15
+ initializer = load_initializer
16
+
17
+ after_authenticate_job_config =
18
+ " config.after_authenticate_job = "\
19
+ "{ job: \"Duodealer::AfterAuthenticateJob\", inline: false }\n"
20
+
21
+ inject_into_file(
22
+ "config/initializers/duodealer_app.rb",
23
+ after_authenticate_job_config,
24
+ before: "end"
25
+ )
26
+
27
+ unless initializer.include?(after_authenticate_job_config)
28
+ shell.say("Error adding after_authenticate_job to config. Add this line manually: #{after_authenticate_job_config}", :red)
29
+ end
30
+ end
31
+
32
+ def add_after_authenticate_job
33
+ template "after_authenticate_job.rb", "app/jobs/#{job_file_name}_job.rb"
34
+ end
35
+
36
+ private
37
+ def load_initializer
38
+ File.read(File.join(destination_root, "config/initializers/duodealer_app.rb"))
39
+ end
40
+
41
+ def job_file_name
42
+ "duodealer/after_authenticate"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,10 @@
1
+ module Duodealer
2
+ class AfterAuthenticateJob < ActiveJob::Base
3
+ def perform(shop_domain:)
4
+ shop = Shop.find_by(duodealer_domain: shop_domain)
5
+
6
+ shop.with_duodealer_session do
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module DuodealerApp
6
+ module Generators
7
+ class AddMarketingActivityExtensionGenerator < Rails::Generators::Base
8
+ source_root File.expand_path("../templates", __FILE__)
9
+
10
+ def generate_app_extension
11
+ template "marketing_activities_controller.rb", "app/controllers/marketing_activities_controller.rb"
12
+ generate_routes
13
+ end
14
+
15
+ private
16
+ def generate_routes
17
+ inject_into_file(
18
+ "config/routes.rb",
19
+ optimize_indentation(routes, 2),
20
+ after: "root :to => 'home#index'\n"
21
+ )
22
+ end
23
+
24
+ def routes
25
+ <<~EOS
26
+
27
+ resource :marketing_activities, only: [:create, :update] do
28
+ patch :resume
29
+ patch :pause
30
+ patch :delete
31
+ post :republish
32
+ post :preload_form_data
33
+ post :preview
34
+ post :errors
35
+ end
36
+ EOS
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MarketingActivitiesController < DuodealerApp::ExtensionVerificationController
4
+ def preload_form_data
5
+ preload_data = {
6
+ "form_data": {}
7
+ }
8
+ render(json: preload_data, status: :ok)
9
+ end
10
+
11
+ def update
12
+ render(json: {}, status: :accepted)
13
+ end
14
+
15
+ def pause
16
+ render(json: {}, status: :accepted)
17
+ end
18
+
19
+ def resume
20
+ render(json: {}, status: :accepted)
21
+ end
22
+
23
+ def delete
24
+ render(json: {}, status: :accepted)
25
+ end
26
+
27
+ def preview
28
+ placeholder_img = "https://cdn.duodealer.com/s/files/1/0533/2089/files/placeholder-images-image_small.png"
29
+ preview_response = {
30
+ "desktop": {
31
+ "preview_url": placeholder_img,
32
+ "content_type": "text/html",
33
+ "width": 360,
34
+ "height": 200
35
+ },
36
+ "mobile": {
37
+ "preview_url": placeholder_img,
38
+ "content_type": "text/html",
39
+ "width": 360,
40
+ "height": 200
41
+ }
42
+ }
43
+ render(json: preview_response, status: :ok)
44
+ end
45
+
46
+ def create
47
+ render(json: {}, status: :ok)
48
+ end
49
+
50
+ def republish
51
+ render(json: {}, status: :accepted)
52
+ end
53
+
54
+ def errors
55
+ request_id = params[:request_id]
56
+ message = params[:message]
57
+
58
+ Rails.logger.info("[Marketing Activity App Error Feedback] Request id: #{request_id}, message: #{message}")
59
+
60
+ render(json: {}, status: :ok)
61
+ end
62
+ end