disco_app 0.12.7.pre.puma.pre.3 → 0.18.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (354) hide show
  1. checksums.yaml +5 -5
  2. data/Rakefile +1 -5
  3. data/app/assets/components/disco_app/forms/model-form.es6.jsx +2 -7
  4. data/app/assets/images/disco_app/.keep +0 -0
  5. data/app/assets/images/disco_app/logo.png +0 -0
  6. data/app/assets/javascripts/disco_app/components/custom/shop_row.js.jsx +2 -1
  7. data/app/assets/javascripts/disco_app/components/ui-kit/forms/base_form.es6.jsx +16 -44
  8. data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-checkbox.es6.jsx +10 -5
  9. data/app/clients/disco_app/api_client.rb +25 -19
  10. data/app/clients/disco_app/graphql_client.rb +85 -0
  11. data/app/controllers/disco_app/admin/app_settings_controller.rb +2 -0
  12. data/app/controllers/disco_app/admin/application_controller.rb +3 -1
  13. data/app/controllers/disco_app/admin/concerns/app_settings_controller.rb +2 -1
  14. data/app/controllers/disco_app/admin/concerns/authenticated_controller.rb +5 -3
  15. data/app/controllers/disco_app/admin/concerns/plans_controller.rb +3 -2
  16. data/app/controllers/disco_app/admin/concerns/shops_controller.rb +1 -0
  17. data/app/controllers/disco_app/admin/concerns/sources_controller.rb +52 -0
  18. data/app/controllers/disco_app/admin/concerns/subscriptions_controller.rb +3 -2
  19. data/app/controllers/disco_app/admin/plans_controller.rb +2 -0
  20. data/app/controllers/disco_app/admin/resources/shops_controller.rb +2 -0
  21. data/app/controllers/disco_app/admin/shops_controller.rb +2 -0
  22. data/app/controllers/disco_app/admin/sources_controller.rb +5 -0
  23. data/app/controllers/disco_app/admin/subscriptions_controller.rb +2 -0
  24. data/app/controllers/disco_app/charges_controller.rb +12 -8
  25. data/app/controllers/disco_app/concerns/app_proxy_controller.rb +6 -6
  26. data/app/controllers/disco_app/concerns/authenticated_controller.rb +31 -23
  27. data/app/controllers/disco_app/concerns/carrier_request_controller.rb +22 -11
  28. data/app/controllers/disco_app/concerns/user_authenticated_controller.rb +18 -0
  29. data/app/controllers/disco_app/concerns/webhooks_controller.rb +49 -0
  30. data/app/controllers/disco_app/flow/actions_controller.rb +9 -0
  31. data/app/controllers/disco_app/flow/concerns/actions_controller.rb +23 -0
  32. data/app/controllers/disco_app/flow/concerns/trigger_usage_controller.rb +29 -0
  33. data/app/controllers/disco_app/flow/concerns/verifies_flow_payload.rb +39 -0
  34. data/app/controllers/disco_app/flow/trigger_usage_controller.rb +9 -0
  35. data/app/controllers/disco_app/frame_controller.rb +0 -1
  36. data/app/controllers/disco_app/install_controller.rb +3 -6
  37. data/app/controllers/disco_app/subscriptions_controller.rb +12 -4
  38. data/app/controllers/disco_app/user_sessions_controller.rb +58 -0
  39. data/app/controllers/disco_app/webhooks_controller.rb +2 -43
  40. data/app/controllers/sessions_controller.rb +5 -6
  41. data/app/helpers/disco_app/application_helper.rb +7 -7
  42. data/app/jobs/application_job.rb +2 -0
  43. data/app/jobs/disco_app/app_installed_job.rb +2 -0
  44. data/app/jobs/disco_app/app_uninstalled_job.rb +2 -0
  45. data/app/jobs/disco_app/concerns/app_installed_job.rb +3 -4
  46. data/app/jobs/disco_app/concerns/app_uninstalled_job.rb +2 -1
  47. data/app/jobs/disco_app/concerns/customers_data_request_job.rb +13 -0
  48. data/app/jobs/disco_app/concerns/customers_redact_job.rb +13 -0
  49. data/app/jobs/disco_app/concerns/render_asset_group_job.rb +2 -1
  50. data/app/jobs/disco_app/concerns/shop_redact_job.rb +13 -0
  51. data/app/jobs/disco_app/concerns/shop_update_job.rb +10 -3
  52. data/app/jobs/disco_app/concerns/subscription_changed_job.rb +2 -1
  53. data/app/jobs/disco_app/concerns/synchronise_carrier_service_job.rb +9 -8
  54. data/app/jobs/disco_app/concerns/synchronise_resources_job.rb +3 -6
  55. data/app/jobs/disco_app/concerns/synchronise_users_job.rb +18 -0
  56. data/app/jobs/disco_app/concerns/synchronise_webhooks_job.rb +48 -18
  57. data/app/jobs/disco_app/customers_data_request_job.rb +5 -0
  58. data/app/jobs/disco_app/customers_redact_job.rb +5 -0
  59. data/app/jobs/disco_app/flow/process_action_job.rb +11 -0
  60. data/app/jobs/disco_app/flow/process_trigger_job.rb +11 -0
  61. data/app/jobs/disco_app/render_asset_group_job.rb +2 -0
  62. data/app/jobs/disco_app/send_subscription_job.rb +1 -1
  63. data/app/jobs/disco_app/shop_job.rb +12 -5
  64. data/app/jobs/disco_app/shop_redact_job.rb +5 -0
  65. data/app/jobs/disco_app/shop_update_job.rb +2 -0
  66. data/app/jobs/disco_app/subscription_changed_job.rb +2 -0
  67. data/app/jobs/disco_app/synchronise_carrier_service_job.rb +2 -0
  68. data/app/jobs/disco_app/synchronise_resources_job.rb +2 -0
  69. data/app/jobs/disco_app/synchronise_users_job.rb +5 -0
  70. data/app/jobs/disco_app/synchronise_webhooks_job.rb +2 -0
  71. data/app/models/application_record.rb +5 -0
  72. data/app/models/disco_app/app_settings.rb +3 -1
  73. data/app/models/disco_app/application_charge.rb +8 -2
  74. data/app/models/disco_app/concerns/app_settings.rb +2 -0
  75. data/app/models/disco_app/concerns/can_be_liquified.rb +34 -18
  76. data/app/models/disco_app/concerns/has_metafields.rb +100 -44
  77. data/app/models/disco_app/concerns/plan.rb +15 -7
  78. data/app/models/disco_app/concerns/plan_code.rb +5 -3
  79. data/app/models/disco_app/concerns/renders_assets.rb +15 -21
  80. data/app/models/disco_app/concerns/shop.rb +39 -16
  81. data/app/models/disco_app/concerns/source.rb +13 -0
  82. data/app/models/disco_app/concerns/subscription.rb +14 -7
  83. data/app/models/disco_app/concerns/synchronises.rb +16 -16
  84. data/app/models/disco_app/concerns/taggable.rb +8 -3
  85. data/app/models/disco_app/concerns/user.rb +21 -0
  86. data/app/models/disco_app/flow/action.rb +9 -0
  87. data/app/models/disco_app/flow/concerns/action.rb +27 -0
  88. data/app/models/disco_app/flow/concerns/trigger.rb +28 -0
  89. data/app/models/disco_app/flow/concerns/trigger_usage.rb +17 -0
  90. data/app/models/disco_app/flow/trigger.rb +9 -0
  91. data/app/models/disco_app/flow/trigger_usage.rb +9 -0
  92. data/app/models/disco_app/plan.rb +3 -1
  93. data/app/models/disco_app/plan_code.rb +3 -1
  94. data/app/models/disco_app/recurring_application_charge.rb +9 -2
  95. data/app/models/disco_app/session_storage.rb +5 -2
  96. data/app/models/disco_app/shop.rb +3 -1
  97. data/app/models/disco_app/source.rb +5 -0
  98. data/app/models/disco_app/subscription.rb +3 -1
  99. data/app/models/disco_app/user.rb +5 -0
  100. data/app/resources/disco_app/admin/resources/concerns/shop_resource.rb +16 -19
  101. data/app/resources/disco_app/admin/resources/shop_resource.rb +1 -0
  102. data/app/services/disco_app/carrier_request_service.rb +3 -3
  103. data/app/services/disco_app/charges_service.rb +28 -38
  104. data/app/services/disco_app/flow/create_action.rb +35 -0
  105. data/app/services/disco_app/flow/create_trigger.rb +34 -0
  106. data/app/services/disco_app/flow/process_action.rb +50 -0
  107. data/app/services/disco_app/flow/process_trigger.rb +72 -0
  108. data/app/services/disco_app/flow/update_trigger_usage.rb +42 -0
  109. data/app/services/disco_app/partner_app_service.rb +151 -0
  110. data/app/services/disco_app/proxy_service.rb +2 -2
  111. data/app/services/disco_app/request_validation_service.rb +2 -2
  112. data/app/services/disco_app/subscription_service.rb +62 -28
  113. data/app/services/disco_app/synchronise_resources_service.rb +54 -0
  114. data/app/services/disco_app/webhook_service.rb +9 -11
  115. data/app/views/disco_app/admin/sources/_form.html.erb +34 -0
  116. data/app/views/disco_app/admin/sources/edit.html.erb +7 -0
  117. data/app/views/disco_app/admin/sources/index.html.erb +32 -0
  118. data/app/views/disco_app/admin/sources/new.html.erb +7 -0
  119. data/app/views/disco_app/user_sessions/new.html.erb +12 -0
  120. data/app/views/layouts/admin/_nav_items.erb +7 -0
  121. data/app/views/layouts/admin.html.erb +1 -2
  122. data/app/views/layouts/application.html.erb +1 -2
  123. data/app/views/layouts/embedded_app.html.erb +2 -4
  124. data/app/views/layouts/embedded_app_modal.html.erb +2 -3
  125. data/app/views/shopify_app/sessions/new.html.erb +2 -4
  126. data/config/routes.rb +15 -3
  127. data/db/migrate/20150525000000_create_shops_if_not_existent.rb +81 -81
  128. data/db/migrate/20170315062548_create_disco_app_sources.rb +12 -0
  129. data/db/migrate/20170315062629_add_sources_to_shop_subscriptions.rb +16 -0
  130. data/db/migrate/20170327214540_create_disco_app_users.rb +14 -0
  131. data/db/migrate/20170606160751_fix_disco_app_users_index.rb +8 -0
  132. data/db/migrate/20181229100327_create_flow_actions_and_triggers.rb +32 -0
  133. data/db/migrate/20200405000000_create_flow_trigger_usages.rb +16 -0
  134. data/lib/disco_app/configuration.rb +12 -5
  135. data/lib/disco_app/constants.rb +4 -2
  136. data/lib/disco_app/engine.rb +1 -1
  137. data/lib/disco_app/session.rb +1 -0
  138. data/lib/disco_app/support/file_fixtures.rb +2 -1
  139. data/lib/disco_app/version.rb +3 -1
  140. data/lib/generators/disco_app/install/USAGE +5 -0
  141. data/lib/generators/disco_app/install/install_generator.rb +297 -0
  142. data/lib/generators/disco_app/{templates → install/templates}/assets/javascripts/application.js +0 -0
  143. data/lib/generators/disco_app/{templates → install/templates}/assets/javascripts/components.js +0 -0
  144. data/lib/generators/disco_app/{templates → install/templates}/assets/stylesheets/application.scss +0 -0
  145. data/lib/generators/disco_app/install/templates/config/appsignal.yml +12 -0
  146. data/lib/generators/disco_app/install/templates/config/cable.yml.tt +11 -0
  147. data/lib/generators/disco_app/{templates → install/templates}/config/database.yml.tt +7 -3
  148. data/lib/generators/disco_app/install/templates/config/environments/staging.rb +108 -0
  149. data/lib/generators/disco_app/{templates → install/templates}/config/puma.rb +0 -0
  150. data/lib/generators/disco_app/{templates → install/templates}/controllers/home_controller.rb +1 -0
  151. data/lib/generators/disco_app/{templates → install/templates}/initializers/disco_app.rb +5 -0
  152. data/lib/generators/disco_app/install/templates/initializers/session_store.rb +2 -0
  153. data/lib/generators/disco_app/install/templates/initializers/shopify_app.rb +11 -0
  154. data/lib/generators/disco_app/{templates → install/templates}/initializers/shopify_session_repository.rb +2 -1
  155. data/lib/generators/disco_app/install/templates/initializers/timber.rb +4 -0
  156. data/lib/generators/disco_app/install/templates/root/.editorconfig +9 -0
  157. data/lib/generators/disco_app/install/templates/root/.env +27 -0
  158. data/lib/generators/disco_app/install/templates/root/.env.local +29 -0
  159. data/lib/generators/disco_app/install/templates/root/.github/PULL_REQUEST_TEMPLATE.md +18 -0
  160. data/lib/generators/disco_app/install/templates/root/.gitignore +65 -0
  161. data/lib/generators/disco_app/install/templates/root/.rspec +1 -0
  162. data/lib/generators/disco_app/install/templates/root/.rubocop.yml +758 -0
  163. data/lib/generators/disco_app/install/templates/root/.tool-versions +2 -0
  164. data/lib/generators/disco_app/{templates → install/templates}/root/CHECKS +0 -0
  165. data/lib/generators/disco_app/{templates → install/templates}/root/Procfile +0 -0
  166. data/lib/generators/disco_app/install/templates/root/README.md +26 -0
  167. data/lib/generators/disco_app/install/templates/root/package.json.tt +17 -0
  168. data/lib/generators/disco_app/install/templates/spec/rails_helper.rb +40 -0
  169. data/lib/generators/disco_app/install/templates/spec/spec_helper.rb +24 -0
  170. data/lib/generators/disco_app/install/templates/spec/support/active_job.rb +13 -0
  171. data/lib/generators/disco_app/install/templates/spec/support/coveralls.rb +3 -0
  172. data/lib/generators/disco_app/install/templates/spec/support/database_cleaner.rb +17 -0
  173. data/lib/generators/disco_app/install/templates/spec/support/factory_bot.rb +3 -0
  174. data/lib/generators/disco_app/install/templates/spec/support/helpers/json_helper.rb +13 -0
  175. data/lib/generators/disco_app/install/templates/spec/support/shared_examples/a_synchronise_job.rb +12 -0
  176. data/lib/generators/disco_app/install/templates/spec/support/shoulda.rb +6 -0
  177. data/lib/generators/disco_app/install/templates/spec/support/vcr.rb +14 -0
  178. data/lib/generators/disco_app/install/templates/spec/support/webmock.rb +8 -0
  179. data/lib/generators/disco_app/{templates → install/templates}/views/home/index.html.erb +0 -0
  180. data/lib/generators/disco_app/react/USAGE +5 -0
  181. data/lib/generators/disco_app/react/react_generator.rb +108 -0
  182. data/lib/generators/disco_app/react/templates/app/controllers/embedded/api/base_controller.rb +18 -0
  183. data/lib/generators/disco_app/react/templates/app/controllers/embedded/api/home_controller.rb +10 -0
  184. data/lib/generators/disco_app/react/templates/app/controllers/embedded/api/shops_controller.rb +11 -0
  185. data/lib/generators/disco_app/react/templates/app/controllers/embedded/api/users_controller.rb +11 -0
  186. data/lib/generators/disco_app/react/templates/app/controllers/embedded/home_controller.rb +13 -0
  187. data/lib/generators/disco_app/react/templates/app/models/api_response.rb +107 -0
  188. data/lib/generators/disco_app/react/templates/app/serializers/disco_app/shop_serializer.rb +13 -0
  189. data/lib/generators/disco_app/react/templates/app/serializers/disco_app/user_serializer.rb +13 -0
  190. data/lib/generators/disco_app/react/templates/app/serializers/empty_serializer.rb +5 -0
  191. data/lib/generators/disco_app/react/templates/app/serializers/error_serializer.rb +76 -0
  192. data/lib/generators/disco_app/react/templates/app/views/embedded/home/index.html.erb +12 -0
  193. data/lib/generators/disco_app/react/templates/app/views/layouts/embedded.html.erb +10 -0
  194. data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/App.jsx +77 -0
  195. data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/HomePage.jsx +34 -0
  196. data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/Shared/EmbeddedPage.jsx +70 -0
  197. data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/Shared/ErrorBanner.jsx +58 -0
  198. data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/Shared/PaginationWrapper.jsx +10 -0
  199. data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/Shared/ScrollToTop.jsx +23 -0
  200. data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/withApi.jsx +125 -0
  201. data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/index.jsx +39 -0
  202. data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/utils.js +19 -0
  203. data/lib/generators/disco_app/react/templates/app/webpack/packs/embedded.js +2 -0
  204. data/lib/generators/disco_app/react/templates/app/webpack/stylesheets/embedded/shared/banners.scss +7 -0
  205. data/lib/generators/disco_app/react/templates/app/webpack/stylesheets/embedded/shared/busy.scss +3 -0
  206. data/lib/generators/disco_app/react/templates/app/webpack/stylesheets/embedded/shared/index.scss +3 -0
  207. data/lib/generators/disco_app/react/templates/app/webpack/stylesheets/embedded/shared/pagination.scss +5 -0
  208. data/lib/generators/disco_app/react/templates/app/webpack/stylesheets/embedded.scss +2 -0
  209. data/lib/generators/disco_app/react/templates/config/initializers/mime_types.rb +13 -0
  210. data/lib/generators/disco_app/react/templates/config/initializers/omniauth.rb +27 -0
  211. data/lib/generators/disco_app/react/templates/config/initializers/version.rb.tt +7 -0
  212. data/lib/generators/disco_app/react/templates/config/webpack/staging.js +5 -0
  213. data/lib/generators/disco_app/react/templates/config/webpack/test.js +5 -0
  214. data/lib/generators/disco_app/react/templates/config/webpacker.yml +96 -0
  215. data/lib/generators/disco_app/react/templates/root/.eslintignore +5 -0
  216. data/lib/generators/disco_app/react/templates/root/.eslintrc +69 -0
  217. data/lib/generators/disco_app/react/templates/root/.prettierrc +3 -0
  218. data/lib/generators/disco_app/react/templates/root/VERSION +1 -0
  219. data/lib/generators/disco_app/react/templates/root/babel.config.js +72 -0
  220. data/lib/generators/disco_app/react/templates/root/package.json.tt +84 -0
  221. data/lib/generators/disco_app/react/templates/root/postcss.config.js +14 -0
  222. data/lib/tasks/api.rake +0 -2
  223. data/lib/tasks/carrier_service.rake +0 -2
  224. data/lib/tasks/database.rake +1 -1
  225. data/lib/tasks/partner_app.rake +26 -0
  226. data/lib/tasks/sessions.rake +0 -2
  227. data/lib/tasks/shops.rake +0 -2
  228. data/lib/tasks/users.rake +8 -0
  229. data/lib/tasks/webhooks.rake +0 -2
  230. data/test/clients/disco_app/api_client_test.rb +3 -3
  231. data/test/controllers/disco_app/admin/shops_controller_test.rb +1 -0
  232. data/test/controllers/disco_app/charges_controller_test.rb +19 -21
  233. data/test/controllers/disco_app/flow/trigger_usage_controller_test.rb +41 -0
  234. data/test/controllers/disco_app/install_controller_test.rb +2 -1
  235. data/test/controllers/disco_app/subscriptions_controller_test.rb +6 -5
  236. data/test/controllers/disco_app/webhooks_controller_test.rb +6 -5
  237. data/test/controllers/home_controller_test.rb +2 -2
  238. data/test/controllers/proxy_controller_test.rb +3 -3
  239. data/test/disco_app_test.rb +3 -1
  240. data/test/dummy/Rakefile +1 -1
  241. data/test/dummy/app/assets/config/manifest.js +2 -0
  242. data/test/dummy/app/controllers/application_controller.rb +2 -0
  243. data/test/dummy/app/controllers/carrier_request_controller.rb +1 -0
  244. data/test/dummy/app/controllers/disco_app/admin/shops_controller.rb +2 -1
  245. data/test/dummy/app/controllers/home_controller.rb +1 -0
  246. data/test/dummy/app/controllers/proxy_controller.rb +2 -1
  247. data/test/dummy/app/javascript/packs/application.js +18 -0
  248. data/test/dummy/app/jobs/application_job.rb +2 -0
  249. data/test/dummy/app/jobs/carts_update_job.rb +1 -1
  250. data/test/dummy/app/jobs/disco_app/app_installed_job.rb +2 -3
  251. data/test/dummy/app/jobs/disco_app/app_uninstalled_job.rb +3 -2
  252. data/test/dummy/app/jobs/products_create_job.rb +1 -1
  253. data/test/dummy/app/jobs/products_delete_job.rb +1 -1
  254. data/test/dummy/app/jobs/products_update_job.rb +1 -1
  255. data/test/dummy/app/models/application_record.rb +5 -0
  256. data/test/dummy/app/models/cart.rb +8 -7
  257. data/test/dummy/app/models/disco_app/shop.rb +8 -6
  258. data/test/dummy/app/models/js_configuration.rb +2 -1
  259. data/test/dummy/app/models/product.rb +4 -3
  260. data/test/dummy/app/models/widget_configuration.rb +2 -1
  261. data/test/dummy/babel.config.js +72 -0
  262. data/test/dummy/bin/bundle +1 -1
  263. data/test/dummy/bin/rails +1 -1
  264. data/test/dummy/bin/setup +8 -8
  265. data/test/dummy/bin/webpack +19 -0
  266. data/test/dummy/bin/webpack-dev-server +19 -0
  267. data/test/dummy/config/application.rb +3 -5
  268. data/test/dummy/config/boot.rb +2 -2
  269. data/test/dummy/config/database.yml +4 -0
  270. data/test/dummy/config/environment.rb +1 -1
  271. data/test/dummy/config/environments/staging.rb +85 -0
  272. data/test/dummy/config/environments/test.rb +2 -2
  273. data/test/dummy/config/initializers/disco_app.rb +8 -1
  274. data/test/dummy/config/initializers/omniauth.rb +3 -4
  275. data/test/dummy/config/initializers/session_store.rb +1 -1
  276. data/test/dummy/config/initializers/shopify_app.rb +1 -0
  277. data/test/dummy/config/initializers/shopify_session_repository.rb +1 -1
  278. data/test/dummy/config/routes.rb +0 -2
  279. data/test/dummy/config/secrets.yml +3 -0
  280. data/test/dummy/config/webpack/development.js +5 -0
  281. data/test/dummy/config/webpack/environment.js +3 -0
  282. data/test/dummy/config/webpack/production.js +5 -0
  283. data/test/dummy/config/webpack/test.js +5 -0
  284. data/test/dummy/config/webpacker.yml +95 -0
  285. data/test/dummy/db/migrate/20160307182229_create_products.rb +3 -1
  286. data/test/dummy/db/migrate/20160530160739_create_asset_models.rb +3 -1
  287. data/test/dummy/db/migrate/20161105054746_create_carts.rb +3 -1
  288. data/test/dummy/db/schema.rb +141 -88
  289. data/test/dummy/package.json +10 -0
  290. data/test/dummy/postcss.config.js +12 -0
  291. data/test/dummy/yarn.lock +7278 -0
  292. data/test/fixtures/api/subscriptions/valid_request.json +2 -2
  293. data/test/fixtures/api/widget_store/charges/{activate_application_charge_request.json → get_active_application_charge_response.json} +1 -1
  294. data/test/fixtures/api/widget_store/charges/{activate_recurring_application_charge_request.json → get_active_recurring_application_charge_response.json} +2 -2
  295. data/test/fixtures/api/widget_store/empty_webhooks.json +3 -0
  296. data/test/fixtures/api/widget_store/existing_webhooks.json +43 -0
  297. data/test/fixtures/api/widget_store/products/get_metafields_empty_response.json +3 -0
  298. data/test/fixtures/api/widget_store/products/get_metafields_with_existing_response.json +11 -0
  299. data/test/fixtures/api/widget_store/products/write_metafields_multiple_namespaces_request.json +4 -0
  300. data/test/fixtures/api/widget_store/products/write_metafields_single_namespace_request.json +2 -0
  301. data/test/fixtures/api/widget_store/products/write_metafields_with_existing_single_namespace_request.json +21 -0
  302. data/test/fixtures/api/widget_store/{charges/activate_application_charge_response.json → products/write_metafields_with_existing_single_namespace_response.json} +0 -0
  303. data/test/fixtures/api/widget_store/shops/get_metafields_with_existing_response.json +11 -0
  304. data/test/fixtures/api/widget_store/shops/write_metafields_with_existing_first_request.json +9 -0
  305. data/test/fixtures/api/widget_store/{charges/activate_recurring_application_charge_response.json → shops/write_metafields_with_existing_first_response.json} +0 -0
  306. data/test/fixtures/api/widget_store/shops/write_metafields_with_existing_second_request.json +9 -0
  307. data/test/fixtures/api/widget_store/shops/write_metafields_with_existing_second_response.json +1 -0
  308. data/test/fixtures/api/widget_store/users.json +42 -0
  309. data/test/fixtures/disco_app/shops.yml +9 -2
  310. data/test/fixtures/disco_app/sources.yml +3 -0
  311. data/test/fixtures/liquid/model.liquid +8 -8
  312. data/test/fixtures/webhooks/flow/trigger_usage.json +7 -0
  313. data/test/integration/synchronises_test.rb +20 -13
  314. data/test/jobs/disco_app/app_installed_job_test.rb +30 -11
  315. data/test/jobs/disco_app/app_uninstalled_job_test.rb +5 -2
  316. data/test/jobs/disco_app/send_subscription_job_test.rb +3 -2
  317. data/test/jobs/disco_app/synchronise_carrier_service_job_test.rb +1 -0
  318. data/test/jobs/disco_app/synchronise_users_job_test.rb +27 -0
  319. data/test/jobs/disco_app/synchronise_webhooks_job_test.rb +78 -9
  320. data/test/models/disco_app/can_be_liquified_test.rb +3 -1
  321. data/test/models/disco_app/has_metafields_test.rb +109 -20
  322. data/test/models/disco_app/renders_assets_test.rb +2 -1
  323. data/test/models/disco_app/session_test.rb +2 -2
  324. data/test/models/disco_app/shop_test.rb +1 -1
  325. data/test/services/disco_app/charges_service_test.rb +10 -14
  326. data/test/services/disco_app/flow/create_action_test.rb +51 -0
  327. data/test/services/disco_app/flow/create_trigger_test.rb +56 -0
  328. data/test/services/disco_app/flow/process_action_test.rb +68 -0
  329. data/test/services/disco_app/flow/process_trigger_test.rb +94 -0
  330. data/test/services/disco_app/flow/update_trigger_usage_test.rb +87 -0
  331. data/test/services/disco_app/subscription_service_test.rb +3 -2
  332. data/test/services/disco_app/synchronise_resources_service_test.rb +57 -0
  333. data/test/support/test_file_fixtures.rb +2 -2
  334. data/test/support/test_shopify_api.rb +1 -1
  335. data/test/test_helper.rb +24 -6
  336. data/test/vcr/flow_trigger_invalid_title.yml +35 -0
  337. data/test/vcr/flow_trigger_valid.yml +38 -0
  338. data/test/vcr/synchronise_products.yml +130 -0
  339. data/test/vcr/synchronise_products_paginated.yml +119 -0
  340. data/test/vcr/synchronise_products_since_id.yml +125 -0
  341. data/test/vcr/synchronise_products_with_params.yml +130 -0
  342. data/test/vcr/webhook_failure.yml +640 -0
  343. metadata +388 -142
  344. data/app/clients/disco_app/disco_api_error.rb +0 -2
  345. data/lib/generators/disco_app/USAGE +0 -5
  346. data/lib/generators/disco_app/disco_app_generator.rb +0 -236
  347. data/lib/generators/disco_app/templates/config/newrelic.yml +0 -26
  348. data/lib/generators/disco_app/templates/initializers/rollbar.rb +0 -23
  349. data/lib/generators/disco_app/templates/initializers/session_store.rb +0 -2
  350. data/lib/generators/disco_app/templates/initializers/shopify_app.rb +0 -6
  351. data/test/dummy/config/database.gitlab-ci.yml +0 -24
  352. data/test/fixtures/api/widget_store/charges/get_accepted_application_charge_response.json +0 -16
  353. data/test/fixtures/api/widget_store/charges/get_accepted_recurring_application_charge_response.json +0 -20
  354. data/test/fixtures/api/widget_store/webhooks.json +0 -1
@@ -1,10 +1,10 @@
1
1
  require 'jsonapi/resource'
2
2
 
3
3
  module DiscoApp::Admin::Resources::Concerns::ShopResource
4
+
4
5
  extend ActiveSupport::Concern
5
6
 
6
7
  included do
7
-
8
8
  attributes :domain, :status, :created_at
9
9
  attributes :email, :country_name, :currency, :plan_display_name
10
10
  attributes :current_subscription_id, :current_subscription_display_amount, :current_subscription_display_plan, :current_subscription_display_plan_code, :current_subscription_source
@@ -15,56 +15,54 @@ module DiscoApp::Admin::Resources::Concerns::ShopResource
15
15
  filters :query, :status
16
16
 
17
17
  # Adjust the base records method to ensure only models for the authenticated domain are retrieved.
18
- def self.records(options = {})
18
+ def self.records(_options = {})
19
19
  records = DiscoApp::Shop.order(created_at: :desc)
20
20
  records
21
21
  end
22
22
 
23
23
  # Apply filters.
24
- def self.apply_filter(records, filter, value, options)
24
+ def self.apply_filter(records, filter, value, _options)
25
25
  return records if value.blank?
26
26
 
27
27
  # Perform appropriate filtering.
28
28
  case filter
29
- when :query
30
- return records.where('name LIKE ? OR shopify_domain LIKE ? OR domain LIKE ?', "%#{value.first}%", "%#{value.first}%", "%#{value.first}%")
31
- when :status
32
- return records.where(status: value.map { |v| DiscoApp::Shop.statuses[v.to_sym] } )
33
- else
34
- return super(records, filter, value)
29
+ when :query
30
+ return records.where('name LIKE ? OR shopify_domain LIKE ? OR domain LIKE ?', "%#{value.first}%", "%#{value.first}%", "%#{value.first}%")
31
+ when :status
32
+ return records.where(status: value.map { |v| DiscoApp::Shop.statuses[v.to_sym] })
33
+ else
34
+ return super(records, filter, value)
35
35
  end
36
36
  end
37
37
 
38
38
  # Don't allow the update of any fields via the API.
39
- def self.updatable_fields(context)
39
+ def self.updatable_fields(_context)
40
40
  []
41
41
  end
42
42
 
43
43
  # Don't allow the creation of any fields via the API.
44
- def self.creatable_fields(context)
44
+ def self.creatable_fields(_context)
45
45
  []
46
46
  end
47
47
 
48
48
  def email
49
- @model.data['email']
49
+ @model.data[:email]
50
50
  end
51
51
 
52
52
  def country_name
53
- @model.data['country_name']
53
+ @model.data[:country_name]
54
54
  end
55
55
 
56
56
  def currency
57
- @model.data['currency']
57
+ @model.data[:currency]
58
58
  end
59
59
 
60
60
  def plan_display_name
61
- @model.data['plan_display_name']
61
+ @model.data[:plan_display_name]
62
62
  end
63
63
 
64
64
  def current_subscription_id
65
- if @model.current_subscription?
66
- @model.current_subscription.id
67
- end
65
+ @model.current_subscription.id if @model.current_subscription?
68
66
  end
69
67
 
70
68
  def current_subscription_display_amount
@@ -94,7 +92,6 @@ module DiscoApp::Admin::Resources::Concerns::ShopResource
94
92
  '-'
95
93
  end
96
94
  end
97
-
98
95
  end
99
96
 
100
97
  end
@@ -1,4 +1,5 @@
1
1
  class DiscoApp::Admin::Resources::ShopResource < JSONAPI::Resource
2
+
2
3
  include DiscoApp::Admin::Resources::Concerns::ShopResource
3
4
 
4
5
  end
@@ -2,13 +2,13 @@ class DiscoApp::CarrierRequestService
2
2
 
3
3
  # Return true iff the provided hmac_to_verify matches that calculated from the
4
4
  # given data and secret.
5
- def self.is_valid_hmac?(body, secret, hmac_to_verify)
6
- ActiveSupport::SecurityUtils.secure_compare(self.calculated_hmac(body, secret), hmac_to_verify.to_s)
5
+ def self.valid_hmac?(body, secret, hmac_to_verify)
6
+ ActiveSupport::SecurityUtils.secure_compare(calculated_hmac(body, secret), hmac_to_verify.to_s)
7
7
  end
8
8
 
9
9
  # Calculate the HMAC for the given data and secret.
10
10
  def self.calculated_hmac(body, secret)
11
- digest = OpenSSL::Digest.new('sha256')
11
+ digest = OpenSSL::Digest.new('sha256')
12
12
  Base64.encode64(OpenSSL::HMAC.digest(digest, secret, body)).strip
13
13
  end
14
14
 
@@ -6,24 +6,22 @@ class DiscoApp::ChargesService
6
6
  # Create the charge object locally first.
7
7
  charge = subscription.charge_class.create!(
8
8
  shop: shop,
9
- subscription: subscription,
9
+ subscription: subscription
10
10
  )
11
11
 
12
12
  # Create the charge object on Shopify.
13
- shopify_charge = shop.with_api_context {
13
+ shopify_charge = shop.with_api_context do
14
14
  subscription.shopify_charge_class.create(
15
15
  name: subscription.plan.name,
16
- price: '%.2f' % (subscription.amount.to_f / 100.0),
16
+ price: format('%.2f', (subscription.amount.to_f / 100.0)),
17
17
  trial_days: subscription.plan.has_trial? ? subscription.trial_period_days : nil,
18
18
  return_url: charge.activate_url,
19
19
  test: !DiscoApp.configuration.real_charges?
20
20
  )
21
- }
21
+ end
22
22
 
23
23
  # If we couldn't create the charge on Shopify, return nil.
24
- if shopify_charge.nil?
25
- return nil
26
- end
24
+ return nil if shopify_charge.nil?
27
25
 
28
26
  # Update the local record of the charge from Shopify's created charge, then
29
27
  # return it.
@@ -34,48 +32,40 @@ class DiscoApp::ChargesService
34
32
  charge
35
33
  end
36
34
 
37
- # Attempt to activate the given Shopify charge for the given Shop using the
38
- # Shopify API. Returns true on successful activation, false otherwise.
35
+ # Synchronises the status of a given charge from the Shopify API and returns
36
+ # true if it's active (and false otherwise).
37
+ #
38
+ # Previously, the activation of a charge also required updating Shopify via the
39
+ # API, but that requirement has been removed.
40
+ #
41
+ # See https://shopify.dev/changelog/auto-activation-of-charges-and-subscriptions
39
42
  def self.activate(shop, charge)
40
- begin
41
- # Start by fetching the Shopify charge to check that it was accepted.
42
- shopify_charge = shop.with_api_context {
43
- charge.subscription.shopify_charge_class.find(charge.shopify_id)
44
- }
45
-
46
- # Update the status of the local charge based on the Shopify charge.
47
- charge.send("#{shopify_charge.status}!") if charge.respond_to? "#{shopify_charge.status}!"
48
-
49
- # If the charge wasn't accepted, fail and return.
50
- return false unless charge.accepted?
43
+ # Start by fetching the Shopify charge to check that it was accepted.
44
+ shopify_charge = shop.with_api_context do
45
+ charge.subscription.shopify_charge_class.find(charge.shopify_id)
46
+ end
51
47
 
52
- # If the charge was indeed accepted, activate it via Shopify.
53
- charge.shop.with_api_context {
54
- shopify_charge.activate
55
- }
48
+ # Update the status of the local charge based on the Shopify charge.
49
+ charge.send("#{shopify_charge.status}!") if charge.respond_to? "#{shopify_charge.status}!"
56
50
 
57
- # If the charge was recurring, make sure that all other local recurring
58
- # charges are marked inactive.
59
- if charge.recurring?
60
- self.cancel_recurring_charges(shop, charge)
61
- end
51
+ # If the charge isn't active, fail and return.
52
+ return false unless charge.active?
62
53
 
63
- charge.active!
54
+ # If the charge was recurring, make sure that all other local recurring
55
+ # charges are marked inactive.
56
+ cancel_recurring_charges(shop, charge) if charge.recurring?
64
57
 
65
- true
66
- rescue
67
- false
68
- end
58
+ true
59
+ rescue StandardError
60
+ false
69
61
  end
70
62
 
71
63
  # Cancel all recurring charges for the given shop. If the optional charge
72
64
  # parameter is given, it will be excluded from the cancellation.
73
65
  def self.cancel_recurring_charges(shop, charge = nil)
74
66
  charges = DiscoApp::RecurringApplicationCharge.where(shop: shop)
75
- if charge.present?
76
- charges = charges.where.not(id: charge)
77
- end
78
- charges.update_all(status: DiscoApp::RecurringApplicationCharge.statuses[:cancelled])
67
+ charges = charges.where.not(id: charge) if charge.present?
68
+ charges.update_all(status: DiscoApp::RecurringApplicationCharge.statuses[:cancelled]) # rubocop:disable Rails/SkipsModelValidations
79
69
  end
80
70
 
81
71
  end
@@ -0,0 +1,35 @@
1
+ require 'interactor'
2
+
3
+ module DiscoApp
4
+ module Flow
5
+ class CreateAction
6
+
7
+ include Interactor
8
+
9
+ delegate :shop, :action_id, :action_run_id, :properties, to: :context
10
+ delegate :action, to: :context
11
+
12
+ def call
13
+ create_action
14
+ enqueue_process_action_job
15
+ end
16
+
17
+ private
18
+
19
+ def create_action
20
+ context.action = shop.flow_actions.create!(
21
+ action_id: action_id,
22
+ action_run_id: action_run_id,
23
+ properties: properties
24
+ )
25
+ rescue ActiveRecord::RecordNotUnique, PG::UniqueViolation
26
+ context.fail!
27
+ end
28
+
29
+ def enqueue_process_action_job
30
+ ProcessActionJob.perform_later(shop, action)
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ require 'interactor'
2
+
3
+ module DiscoApp
4
+ module Flow
5
+ class CreateTrigger
6
+
7
+ include Interactor
8
+
9
+ delegate :shop, :title, :resource_name, :resource_url, :properties, to: :context
10
+ delegate :trigger, to: :context
11
+
12
+ def call
13
+ create_trigger
14
+ enqueue_process_trigger_job
15
+ end
16
+
17
+ private
18
+
19
+ def create_trigger
20
+ context.trigger = shop.flow_triggers.create!(
21
+ title: title,
22
+ resource_name: resource_name,
23
+ resource_url: resource_url,
24
+ properties: properties
25
+ )
26
+ end
27
+
28
+ def enqueue_process_trigger_job
29
+ ProcessTriggerJob.perform_later(shop, trigger)
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,50 @@
1
+ require 'interactor'
2
+
3
+ module DiscoApp
4
+ module Flow
5
+ class ProcessAction
6
+
7
+ include Interactor
8
+
9
+ delegate :action, to: :context
10
+ delegate :action_service_class, to: :context
11
+
12
+ def call
13
+ validate_action
14
+ find_action_service_class
15
+ execute_action_service_class
16
+ end
17
+
18
+ private
19
+
20
+ def validate_action
21
+ context.fail! unless action.pending?
22
+ end
23
+
24
+ def find_action_service_class
25
+ context.action_service_class =
26
+ action.action_id.classify.safe_constantize ||
27
+ %(Flow::Actions::#{action.action_id.to_s.classify}).safe_constantize
28
+
29
+ return unless action_service_class.nil?
30
+
31
+ update_action(false, ["Could not find service class for #{action.action_id}"])
32
+ context.fail!
33
+ end
34
+
35
+ def execute_action_service_class
36
+ result = action_service_class.call(shop: action.shop, properties: action.properties)
37
+ update_action(result.success?, result.errors)
38
+ end
39
+
40
+ def update_action(success, errors)
41
+ action.update!(
42
+ status: success ? Action.statuses[:succeeded] : Action.statuses[:failed],
43
+ processing_errors: success ? [] : errors,
44
+ processed_at: Time.current
45
+ )
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,72 @@
1
+ require 'interactor'
2
+
3
+ module DiscoApp
4
+ module Flow
5
+ class ProcessTrigger
6
+
7
+ include Interactor
8
+
9
+ delegate :trigger, to: :context
10
+ delegate :api_success, :api_errors, to: :context
11
+
12
+ def call
13
+ validate_trigger
14
+ make_api_request unless trigger_not_in_use?
15
+ update_trigger
16
+ fail_if_errors_present
17
+ end
18
+
19
+ private
20
+
21
+ def validate_trigger
22
+ context.fail! unless trigger.pending?
23
+ end
24
+
25
+ def make_api_request
26
+ context.api_success, context.api_errors = api_client.create_flow_trigger(
27
+ trigger.title,
28
+ trigger.resource_name,
29
+ trigger.resource_url,
30
+ trigger.properties
31
+ )
32
+ end
33
+
34
+ def update_trigger
35
+ trigger.update!(
36
+ status: trigger_status,
37
+ processing_errors: processing_errors,
38
+ processed_at: Time.current
39
+ )
40
+ end
41
+
42
+ def trigger_status
43
+ return Trigger.statuses[:skipped] if trigger_not_in_use?
44
+
45
+ api_success ? Trigger.statuses[:succeeded] : Trigger.statuses[:failed]
46
+ end
47
+
48
+ def processing_errors
49
+ return [] if trigger_not_in_use?
50
+ return [] if api_success
51
+ api_errors
52
+ end
53
+
54
+ def fail_if_errors_present
55
+ context.fail! unless trigger_not_in_use? || api_success
56
+ end
57
+
58
+ def api_client
59
+ @api_client ||= DiscoApp::GraphqlClient.new(trigger.shop)
60
+ end
61
+
62
+ def trigger_not_in_use?
63
+ trigger_usage.present? && !trigger_usage.has_enabled_flow?
64
+ end
65
+
66
+ def trigger_usage
67
+ @trigger_usage ||= TriggerUsage.find_by(shop: trigger.shop, flow_trigger_definition_id: trigger.title)
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,42 @@
1
+ require 'interactor'
2
+
3
+ module DiscoApp
4
+ module Flow
5
+ class UpdateTriggerUsage
6
+
7
+ include Interactor
8
+
9
+ delegate :shop, :flow_trigger_definition_id, :has_enabled_flow, :timestamp, to: :context
10
+ delegate :trigger_usage, to: :context
11
+
12
+ def call
13
+ find_or_create_trigger_usage
14
+ update_trigger_usage
15
+ end
16
+
17
+ private
18
+
19
+ def find_or_create_trigger_usage
20
+ context.trigger_usage = shop.flow_trigger_usages.create_or_find_by!(
21
+ flow_trigger_definition_id: flow_trigger_definition_id
22
+ )
23
+ rescue ActiveRecord::RecordNotUnique, PG::UniqueViolation
24
+ context.fail!
25
+ end
26
+
27
+ def update_trigger_usage
28
+ return if existing_timestamp_is_newer?
29
+
30
+ trigger_usage.update(
31
+ has_enabled_flow: has_enabled_flow,
32
+ timestamp: timestamp
33
+ )
34
+ end
35
+
36
+ def existing_timestamp_is_newer?
37
+ trigger_usage.timestamp.present? && timestamp < trigger_usage.timestamp
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,151 @@
1
+ module DiscoApp
2
+ class PartnerAppService
3
+
4
+ def initialize(params)
5
+ @email = params[:email]
6
+ @password = params[:password]
7
+ @app_name = params[:app_name]
8
+ @app_url = params[:app_url]
9
+ @organization = params[:organization]
10
+
11
+ @agent = Mechanize.new do |a|
12
+ a.user_agent_alias = 'Mac Safari'
13
+ a.follow_meta_refresh = true
14
+ a.keep_alive = false
15
+ a.pre_connect_hooks << lambda do |_agent, request|
16
+ request['Accept'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
17
+ end
18
+ end
19
+ end
20
+
21
+ def generate_partner_app
22
+ begin
23
+ # Login to Shopify Partners
24
+ login
25
+ # Access Partner dashboard
26
+ org_link = organizations
27
+ dashboard = dashboard_page(org_link)
28
+ # Create App
29
+ apps_page = refresh_page(dashboard)
30
+ create_partner_app(apps_page)
31
+ # Configure newly created app with embedded app use
32
+ apps_page = refresh_page(dashboard)
33
+ embedded_admin_app(apps_page)
34
+ # Add Disco App icon
35
+ apps_page = refresh_page(dashboard)
36
+ add_disco_icon(apps_page)
37
+ # Fetch API credentials
38
+ apps_page = refresh_page(dashboard)
39
+ api_key, secret = api_credentials(apps_page)
40
+ rescue Mechanize::Error => e
41
+ puts 'Error while trying to create partner app'
42
+ puts "Error #{e}, message : #{e.message}"
43
+ return
44
+ end
45
+ # Add them to .env.local file
46
+ append_credentials(api_key, secret)
47
+ puts '#' * 80
48
+ puts 'New Partner App successfully created!'
49
+ puts 'API Credentials have been pasted to your .env.local file'
50
+ puts '#' * 80
51
+ end
52
+
53
+ private
54
+
55
+ def login
56
+ @agent.get('https://accounts.shopify.com/login') do |page|
57
+ page.form do |form|
58
+ form['account[email]'] = @email
59
+ form['account[password]'] = @password
60
+ end.submit
61
+ end
62
+ end
63
+
64
+ def organizations
65
+ organizations = @agent.get('https://partners.shopify.com/organizations/')
66
+ organizations.links.select { |link| link.text[/#{@organization}/] }.first.href
67
+ end
68
+
69
+ def dashboard_page(org_link)
70
+ @agent.get('https://partners.shopify.com' + org_link)
71
+ end
72
+
73
+ def create_partner_app(apps_page)
74
+ apps_page.form do |form|
75
+ # App name
76
+ form['create_form[title]'] = @app_name
77
+
78
+ # App URL
79
+ form['create_form[application_url]'] = @app_url
80
+
81
+ # Accept TOS
82
+ if form['create_form[accepted]'].present?
83
+ form['create_form[accepted]'] = '1'
84
+ form.hiddens.last.value = 1
85
+ end
86
+ end.submit
87
+ end
88
+
89
+ def api_credentials(apps_page)
90
+ app = apps_page.link_with(text: @app_name).click
91
+ app_info = app.link_with(text: 'App info').click
92
+ add_whitelist_url(app_info)
93
+ api_key = app_info.search('#api_key').first.values[3]
94
+ secret = app_info.search('#settings_form_secrets').first.values[3]
95
+ [api_key, secret]
96
+ end
97
+
98
+ def embedded_admin_app(apps_page)
99
+ app = apps_page.link_with(text: @app_name).click
100
+ extensions = app.link_with(text: 'Extensions').click
101
+ extensions.form do |form|
102
+ form['extensions_form[embedded]'] = '1'
103
+ end.submit
104
+ end
105
+
106
+ def add_disco_icon(apps_page)
107
+ app = apps_page.link_with(text: @app_name).click
108
+ app_info = app.link_with(text: 'App info').click
109
+ logo = DiscoApp::Engine.root.join('app', 'assets', 'images', 'disco_app', 'logo.png').to_s
110
+ app_info.form do |form|
111
+ form.file_uploads.first.file_name = logo
112
+ end.submit
113
+ end
114
+
115
+ # Write credentials of newly created app to .env.local file
116
+ def append_credentials(api_key, secret)
117
+ original_file = '.env.local'
118
+ new_file = original_file + '.new'
119
+ File.open(new_file, 'w') do |file|
120
+ File.foreach(original_file) do |line|
121
+ if line.include?('SHOPIFY_APP_API_KEY')
122
+ file.puts "SHOPIFY_APP_API_KEY=#{api_key}"
123
+ elsif line.include?('SHOPIFY_APP_SECRET')
124
+ file.puts "SHOPIFY_APP_SECRET=#{secret}"
125
+ else
126
+ file.puts line
127
+ end
128
+ end
129
+ end
130
+ File.delete(original_file)
131
+ File.rename(new_file, original_file)
132
+ end
133
+
134
+ def add_whitelist_url(app_info)
135
+ app_info.form do |form|
136
+ form['info_form[redirect_url_whitelist]'] = callback_url
137
+ end.submit
138
+ end
139
+
140
+ # Access the "Apps" section of the dashboard, also used to reload the dashboard
141
+ # When an action has been taken
142
+ def refresh_page(dashboard)
143
+ dashboard.link_with(text: ' Apps').click
144
+ end
145
+
146
+ def callback_url
147
+ [@app_url + '/auth/shopify/callback', @app_url + '/auth/shopify_user/callback'].join("\n")
148
+ end
149
+
150
+ end
151
+ end
@@ -5,12 +5,12 @@ class DiscoApp::ProxyService
5
5
  def self.proxy_signature_is_valid?(query_string, secret)
6
6
  query_hash = Rack::Utils.parse_query(query_string)
7
7
  signature = query_hash.delete('signature').to_s
8
- ActiveSupport::SecurityUtils.variable_size_secure_compare(self.calculated_signature(query_hash, secret), signature)
8
+ ActiveSupport::SecurityUtils.secure_compare(calculated_signature(query_hash, secret), signature)
9
9
  end
10
10
 
11
11
  # Return the calculated signature for the given query hash and secret.
12
12
  def self.calculated_signature(query_hash, secret)
13
- sorted_params = query_hash.collect{ |k, v| "#{k}=#{Array(v).join(',')}" }.sort.join
13
+ sorted_params = query_hash.map{ |k, v| "#{k}=#{Array(v).join(',')}" }.sort.join
14
14
  OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, sorted_params)
15
15
  end
16
16
 
@@ -3,12 +3,12 @@ class DiscoApp::RequestValidationService
3
3
  def self.hmac_valid?(query_string, secret)
4
4
  query_hash = Rack::Utils.parse_query(query_string)
5
5
  hmac = query_hash.delete('hmac').to_s
6
- ActiveSupport::SecurityUtils.variable_size_secure_compare(self.calculated_hmac(query_hash, secret), hmac)
6
+ ActiveSupport::SecurityUtils.secure_compare(calculated_hmac(query_hash, secret), hmac)
7
7
  end
8
8
 
9
9
  # Return the calculated hmac for the given query hash and secret.
10
10
  def self.calculated_hmac(query_hash, secret)
11
- sorted_params = query_hash.collect{ |k, v| "#{k}=#{Array(v).join(',')}" }.sort.join('&')
11
+ sorted_params = query_hash.map{ |k, v| "#{k}=#{Array(v).join(',')}" }.sort.join('&')
12
12
  OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, sorted_params)
13
13
  end
14
14