shopify-cli 0.9.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 (273) hide show
  1. checksums.yaml +7 -0
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/CODE_OF_CONDUCT.md +73 -0
  4. data/.github/CONTRIBUTING.md +51 -0
  5. data/.github/DESIGN.md +153 -0
  6. data/.github/ISSUE_TEMPLATE.md +38 -0
  7. data/.github/PULL_REQUEST_TEMPLATE.md +22 -0
  8. data/.github/probots.yml +3 -0
  9. data/.gitignore +19 -0
  10. data/.rubocop.yml +47 -0
  11. data/.ruby-version +1 -0
  12. data/.travis.yml +12 -0
  13. data/Gemfile +22 -0
  14. data/Gemfile.lock +77 -0
  15. data/LICENSE.md +7 -0
  16. data/README.md +13 -0
  17. data/Rakefile +101 -0
  18. data/SECURITY.md +59 -0
  19. data/Vagrantfile +17 -0
  20. data/bin/load_shopify.rb +20 -0
  21. data/bin/shopify +32 -0
  22. data/dev.yml +17 -0
  23. data/docs/Gemfile +5 -0
  24. data/docs/Gemfile.lock +248 -0
  25. data/docs/_config.yml +16 -0
  26. data/docs/_data/nav.yml +26 -0
  27. data/docs/_includes/footer.html +15 -0
  28. data/docs/_includes/head.html +19 -0
  29. data/docs/_includes/sidebar_nav.html +22 -0
  30. data/docs/_includes/toc.html +112 -0
  31. data/docs/_layouts/default.html +79 -0
  32. data/docs/app/node/commands/index.md +82 -0
  33. data/docs/app/node/index.md +35 -0
  34. data/docs/app/rails/commands/index.md +80 -0
  35. data/docs/app/rails/index.md +36 -0
  36. data/docs/core/index.md +70 -0
  37. data/docs/css/docs.css +157 -0
  38. data/docs/getting-started/index.md +61 -0
  39. data/docs/help/start-app/index.md +6 -0
  40. data/docs/images/header.png +0 -0
  41. data/docs/index.md +27 -0
  42. data/docs/installing-ruby.md +28 -0
  43. data/ext/shopify-cli/extconf.rb +27 -0
  44. data/install.sh +7 -0
  45. data/lib/docgen/class_template.md.erb +81 -0
  46. data/lib/docgen/index_template.md.erb +5 -0
  47. data/lib/docgen/markdown.rb +101 -0
  48. data/lib/graphql/admin_introspection.graphql +87 -0
  49. data/lib/graphql/all_organizations.graphql +19 -0
  50. data/lib/graphql/all_orgs_with_apps.graphql +30 -0
  51. data/lib/graphql/api_versions.graphql +6 -0
  52. data/lib/graphql/convert_dev_to_test_store.graphql +10 -0
  53. data/lib/graphql/create_app.graphql +20 -0
  54. data/lib/graphql/create_customer.graphql +9 -0
  55. data/lib/graphql/create_draft_order.graphql +8 -0
  56. data/lib/graphql/create_product.graphql +9 -0
  57. data/lib/graphql/extension_create.graphql +21 -0
  58. data/lib/graphql/extension_update_draft.graphql +18 -0
  59. data/lib/graphql/find_organization.graphql +17 -0
  60. data/lib/graphql/get_app_urls.graphql +6 -0
  61. data/lib/graphql/update_dashboard_urls.graphql +8 -0
  62. data/lib/project_types/extension/cli.rb +71 -0
  63. data/lib/project_types/extension/commands/build.rb +29 -0
  64. data/lib/project_types/extension/commands/create.rb +49 -0
  65. data/lib/project_types/extension/commands/extension_command.rb +22 -0
  66. data/lib/project_types/extension/commands/push.rb +69 -0
  67. data/lib/project_types/extension/commands/register.rb +78 -0
  68. data/lib/project_types/extension/commands/serve.rb +24 -0
  69. data/lib/project_types/extension/commands/tunnel.rb +69 -0
  70. data/lib/project_types/extension/extension_project.rb +85 -0
  71. data/lib/project_types/extension/extension_project_keys.rb +10 -0
  72. data/lib/project_types/extension/features/argo.rb +48 -0
  73. data/lib/project_types/extension/features/argo_dependencies.rb +28 -0
  74. data/lib/project_types/extension/features/argo_setup.rb +54 -0
  75. data/lib/project_types/extension/features/argo_setup_step.rb +31 -0
  76. data/lib/project_types/extension/features/argo_setup_steps.rb +53 -0
  77. data/lib/project_types/extension/features/tunnel_url.rb +20 -0
  78. data/lib/project_types/extension/forms/create.rb +52 -0
  79. data/lib/project_types/extension/forms/register.rb +48 -0
  80. data/lib/project_types/extension/messages/message_loading.rb +37 -0
  81. data/lib/project_types/extension/messages/messages.rb +126 -0
  82. data/lib/project_types/extension/models/app.rb +14 -0
  83. data/lib/project_types/extension/models/registration.rb +19 -0
  84. data/lib/project_types/extension/models/type.rb +76 -0
  85. data/lib/project_types/extension/models/types/checkout_post_purchase.rb +20 -0
  86. data/lib/project_types/extension/models/types/subscription_management.rb +20 -0
  87. data/lib/project_types/extension/models/validation_error.rb +17 -0
  88. data/lib/project_types/extension/models/version.rb +15 -0
  89. data/lib/project_types/extension/tasks/converters/registration_converter.rb +26 -0
  90. data/lib/project_types/extension/tasks/converters/validation_error_converter.rb +25 -0
  91. data/lib/project_types/extension/tasks/converters/version_converter.rb +28 -0
  92. data/lib/project_types/extension/tasks/create_extension.rb +31 -0
  93. data/lib/project_types/extension/tasks/get_apps.rb +34 -0
  94. data/lib/project_types/extension/tasks/update_draft.rb +29 -0
  95. data/lib/project_types/extension/tasks/user_errors.rb +45 -0
  96. data/lib/project_types/node/cli.rb +37 -0
  97. data/lib/project_types/node/commands/create.rb +117 -0
  98. data/lib/project_types/node/commands/deploy.rb +22 -0
  99. data/lib/project_types/node/commands/deploy/heroku.rb +91 -0
  100. data/lib/project_types/node/commands/generate.rb +51 -0
  101. data/lib/project_types/node/commands/generate/billing.rb +37 -0
  102. data/lib/project_types/node/commands/generate/page.rb +55 -0
  103. data/lib/project_types/node/commands/generate/webhook.rb +33 -0
  104. data/lib/project_types/node/commands/open.rb +16 -0
  105. data/lib/project_types/node/commands/populate.rb +23 -0
  106. data/lib/project_types/node/commands/populate/customer.rb +31 -0
  107. data/lib/project_types/node/commands/populate/draft_order.rb +28 -0
  108. data/lib/project_types/node/commands/populate/product.rb +30 -0
  109. data/lib/project_types/node/commands/serve.rb +45 -0
  110. data/lib/project_types/node/commands/tunnel.rb +39 -0
  111. data/lib/project_types/node/forms/create.rb +87 -0
  112. data/lib/project_types/node/messages/messages.rb +260 -0
  113. data/lib/project_types/rails/cli.rb +41 -0
  114. data/lib/project_types/rails/commands/create.rb +126 -0
  115. data/lib/project_types/rails/commands/deploy.rb +22 -0
  116. data/lib/project_types/rails/commands/deploy/heroku.rb +113 -0
  117. data/lib/project_types/rails/commands/generate.rb +49 -0
  118. data/lib/project_types/rails/commands/generate/webhook.rb +39 -0
  119. data/lib/project_types/rails/commands/open.rb +16 -0
  120. data/lib/project_types/rails/commands/populate.rb +23 -0
  121. data/lib/project_types/rails/commands/populate/customer.rb +31 -0
  122. data/lib/project_types/rails/commands/populate/draft_order.rb +28 -0
  123. data/lib/project_types/rails/commands/populate/product.rb +30 -0
  124. data/lib/project_types/rails/commands/serve.rb +47 -0
  125. data/lib/project_types/rails/commands/tunnel.rb +39 -0
  126. data/lib/project_types/rails/forms/create.rb +116 -0
  127. data/lib/project_types/rails/gem.rb +56 -0
  128. data/lib/project_types/rails/messages/messages.rb +283 -0
  129. data/lib/project_types/rails/ruby.rb +17 -0
  130. data/lib/project_types/script/cli.rb +76 -0
  131. data/lib/project_types/script/commands/create.rb +45 -0
  132. data/lib/project_types/script/commands/disable.rb +36 -0
  133. data/lib/project_types/script/commands/enable.rb +46 -0
  134. data/lib/project_types/script/commands/push.rb +39 -0
  135. data/lib/project_types/script/config/extension_points.yml +18 -0
  136. data/lib/project_types/script/errors.rb +16 -0
  137. data/lib/project_types/script/forms/create.rb +29 -0
  138. data/lib/project_types/script/forms/enable.rb +24 -0
  139. data/lib/project_types/script/forms/push.rb +19 -0
  140. data/lib/project_types/script/forms/script_form.rb +66 -0
  141. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +27 -0
  142. data/lib/project_types/script/graphql/script_service_proxy.graphql +8 -0
  143. data/lib/project_types/script/graphql/shop_script_delete.graphql +14 -0
  144. data/lib/project_types/script/graphql/shop_script_update_or_create.graphql +28 -0
  145. data/lib/project_types/script/layers/application/build_script.rb +43 -0
  146. data/lib/project_types/script/layers/application/create_script.rb +47 -0
  147. data/lib/project_types/script/layers/application/disable_script.rb +19 -0
  148. data/lib/project_types/script/layers/application/enable_script.rb +21 -0
  149. data/lib/project_types/script/layers/application/extension_points.rb +17 -0
  150. data/lib/project_types/script/layers/application/project_dependencies.rb +34 -0
  151. data/lib/project_types/script/layers/application/push_script.rb +30 -0
  152. data/lib/project_types/script/layers/domain/errors.rb +25 -0
  153. data/lib/project_types/script/layers/domain/extension_point.rb +29 -0
  154. data/lib/project_types/script/layers/domain/push_package.rb +29 -0
  155. data/lib/project_types/script/layers/domain/script.rb +18 -0
  156. data/lib/project_types/script/layers/infrastructure/assemblyscript_dependency_manager.rb +73 -0
  157. data/lib/project_types/script/layers/infrastructure/assemblyscript_tsconfig.rb +38 -0
  158. data/lib/project_types/script/layers/infrastructure/assemblyscript_wasm_builder.rb +39 -0
  159. data/lib/project_types/script/layers/infrastructure/dependency_manager.rb +36 -0
  160. data/lib/project_types/script/layers/infrastructure/errors.rb +38 -0
  161. data/lib/project_types/script/layers/infrastructure/extension_point_repository.rb +31 -0
  162. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +47 -0
  163. data/lib/project_types/script/layers/infrastructure/script_builder.rb +34 -0
  164. data/lib/project_types/script/layers/infrastructure/script_repository.rb +89 -0
  165. data/lib/project_types/script/layers/infrastructure/script_service.rb +165 -0
  166. data/lib/project_types/script/layers/infrastructure/test_suite_repository.rb +59 -0
  167. data/lib/project_types/script/messages/messages.rb +204 -0
  168. data/lib/project_types/script/script_project.rb +37 -0
  169. data/lib/project_types/script/templates/ts/as-pect.config.js +21 -0
  170. data/lib/project_types/script/ui/error_handler.rb +136 -0
  171. data/lib/project_types/script/ui/strict_spinner.rb +22 -0
  172. data/lib/rubygems_plugin.rb +18 -0
  173. data/lib/shopify-cli/admin_api.rb +99 -0
  174. data/lib/shopify-cli/admin_api/populate_resource_command.rb +165 -0
  175. data/lib/shopify-cli/admin_api/schema.rb +32 -0
  176. data/lib/shopify-cli/api.rb +104 -0
  177. data/lib/shopify-cli/command.rb +67 -0
  178. data/lib/shopify-cli/commands.rb +28 -0
  179. data/lib/shopify-cli/commands/connect.rb +108 -0
  180. data/lib/shopify-cli/commands/create.rb +50 -0
  181. data/lib/shopify-cli/commands/help.rb +79 -0
  182. data/lib/shopify-cli/commands/logout.rb +23 -0
  183. data/lib/shopify-cli/commands/system.rb +135 -0
  184. data/lib/shopify-cli/commands/version.rb +15 -0
  185. data/lib/shopify-cli/context.rb +372 -0
  186. data/lib/shopify-cli/core.rb +9 -0
  187. data/lib/shopify-cli/core/entry_point.rb +40 -0
  188. data/lib/shopify-cli/core/executor.rb +21 -0
  189. data/lib/shopify-cli/core/help_resolver.rb +20 -0
  190. data/lib/shopify-cli/core/monorail.rb +118 -0
  191. data/lib/shopify-cli/db.rb +114 -0
  192. data/lib/shopify-cli/form.rb +40 -0
  193. data/lib/shopify-cli/git.rb +141 -0
  194. data/lib/shopify-cli/helpers.rb +5 -0
  195. data/lib/shopify-cli/helpers/haikunator.rb +92 -0
  196. data/lib/shopify-cli/heroku.rb +97 -0
  197. data/lib/shopify-cli/js_deps.rb +110 -0
  198. data/lib/shopify-cli/js_system.rb +98 -0
  199. data/lib/shopify-cli/messages/messages.rb +287 -0
  200. data/lib/shopify-cli/oauth.rb +192 -0
  201. data/lib/shopify-cli/oauth/servlet.rb +61 -0
  202. data/lib/shopify-cli/options.rb +40 -0
  203. data/lib/shopify-cli/packager.rb +116 -0
  204. data/lib/shopify-cli/partners_api.rb +114 -0
  205. data/lib/shopify-cli/partners_api/organizations.rb +32 -0
  206. data/lib/shopify-cli/process_supervision.rb +187 -0
  207. data/lib/shopify-cli/project.rb +191 -0
  208. data/lib/shopify-cli/project_type.rb +83 -0
  209. data/lib/shopify-cli/resources.rb +5 -0
  210. data/lib/shopify-cli/resources/env_file.rb +96 -0
  211. data/lib/shopify-cli/sub_command.rb +15 -0
  212. data/lib/shopify-cli/task.rb +10 -0
  213. data/lib/shopify-cli/tasks.rb +32 -0
  214. data/lib/shopify-cli/tasks/create_api_client.rb +29 -0
  215. data/lib/shopify-cli/tasks/ensure_dev_store.rb +41 -0
  216. data/lib/shopify-cli/tasks/ensure_env.rb +31 -0
  217. data/lib/shopify-cli/tasks/ensure_loopback_url.rb +20 -0
  218. data/lib/shopify-cli/tasks/update_dashboard_urls.rb +44 -0
  219. data/lib/shopify-cli/tunnel.rb +154 -0
  220. data/lib/shopify-cli/version.rb +3 -0
  221. data/lib/shopify_cli.rb +132 -0
  222. data/shopify-cli.gemspec +40 -0
  223. data/shopify.fish +12 -0
  224. data/shopify.sh +11 -0
  225. data/vendor/deps/cli-kit/REVISION +1 -0
  226. data/vendor/deps/cli-kit/lib/cli/kit.rb +60 -0
  227. data/vendor/deps/cli-kit/lib/cli/kit/autocall.rb +21 -0
  228. data/vendor/deps/cli-kit/lib/cli/kit/base_command.rb +49 -0
  229. data/vendor/deps/cli-kit/lib/cli/kit/command_registry.rb +94 -0
  230. data/vendor/deps/cli-kit/lib/cli/kit/config.rb +133 -0
  231. data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +115 -0
  232. data/vendor/deps/cli-kit/lib/cli/kit/executor.rb +81 -0
  233. data/vendor/deps/cli-kit/lib/cli/kit/ini.rb +102 -0
  234. data/vendor/deps/cli-kit/lib/cli/kit/levenshtein.rb +82 -0
  235. data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +76 -0
  236. data/vendor/deps/cli-kit/lib/cli/kit/resolver.rb +60 -0
  237. data/vendor/deps/cli-kit/lib/cli/kit/ruby_backports/enumerable.rb +6 -0
  238. data/vendor/deps/cli-kit/lib/cli/kit/support.rb +9 -0
  239. data/vendor/deps/cli-kit/lib/cli/kit/support/test_helper.rb +244 -0
  240. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +207 -0
  241. data/vendor/deps/cli-kit/lib/cli/kit/util.rb +189 -0
  242. data/vendor/deps/cli-kit/lib/cli/kit/version.rb +5 -0
  243. data/vendor/deps/cli-ui/REVISION +1 -0
  244. data/vendor/deps/cli-ui/lib/cli/ui.rb +187 -0
  245. data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +153 -0
  246. data/vendor/deps/cli-ui/lib/cli/ui/box.rb +15 -0
  247. data/vendor/deps/cli-ui/lib/cli/ui/color.rb +79 -0
  248. data/vendor/deps/cli-ui/lib/cli/ui/formatter.rb +179 -0
  249. data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +310 -0
  250. data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +78 -0
  251. data/vendor/deps/cli-ui/lib/cli/ui/progress.rb +88 -0
  252. data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +248 -0
  253. data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +472 -0
  254. data/vendor/deps/cli-ui/lib/cli/ui/prompt/options_handler.rb +24 -0
  255. data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +48 -0
  256. data/vendor/deps/cli-ui/lib/cli/ui/spinner/async.rb +40 -0
  257. data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +241 -0
  258. data/vendor/deps/cli-ui/lib/cli/ui/stdout_router.rb +227 -0
  259. data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +36 -0
  260. data/vendor/deps/cli-ui/lib/cli/ui/truncater.rb +102 -0
  261. data/vendor/deps/cli-ui/lib/cli/ui/version.rb +5 -0
  262. data/vendor/deps/smart_properties/REVISION +1 -0
  263. data/vendor/deps/smart_properties/lib/smart_properties.rb +174 -0
  264. data/vendor/deps/smart_properties/lib/smart_properties/errors.rb +114 -0
  265. data/vendor/deps/smart_properties/lib/smart_properties/property.rb +162 -0
  266. data/vendor/deps/smart_properties/lib/smart_properties/property_collection.rb +83 -0
  267. data/vendor/deps/smart_properties/lib/smart_properties/validations.rb +8 -0
  268. data/vendor/deps/smart_properties/lib/smart_properties/validations/ancestor.rb +27 -0
  269. data/vendor/deps/smart_properties/lib/smart_properties/version.rb +3 -0
  270. data/vendor/lib/semantic/LICENSE +20 -0
  271. data/vendor/lib/semantic/semantic.rb +4 -0
  272. data/vendor/lib/semantic/version.rb +180 -0
  273. metadata +374 -0
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shopify_cli'
4
+
5
+ module Script
6
+ module UI
7
+ module StrictSpinner
8
+ def self.spin(title, auto_debrief: false)
9
+ exception = nil
10
+ CLI::UI::Spinner.spin(title, auto_debrief: auto_debrief) do |*args|
11
+ begin
12
+ yield(*args)
13
+ rescue StandardError => e
14
+ exception = e
15
+ CLI::UI::Spinner::TASK_FAILED
16
+ end
17
+ end
18
+ raise exception if exception
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ Gem.post_uninstall do |uninstaller|
2
+ if uninstaller.spec.name == 'shopify-cli'
3
+ require 'fileutils'
4
+
5
+ symlink = '/usr/local/bin/shopify'
6
+
7
+ # delete the symbolic link IFF it exists AND it does not point to a file
8
+ # (i.e., it's been left hanging as a result of the uninstall, as expected)
9
+ #
10
+ # if the file still exists, either the uninstall failed (possible but
11
+ # unlikely) OR
12
+ # there's another installation of the gem in another ruby folder that has
13
+ # overwritten it, so leave the symbolic link alone
14
+ system("sudo rm -f #{symlink}") if File.symlink?(symlink) && !File.exist?(symlink)
15
+ end
16
+
17
+ true
18
+ end
@@ -0,0 +1,99 @@
1
+ require 'shopify_cli'
2
+
3
+ module ShopifyCli
4
+ ##
5
+ # ShopifyCli::AdminAPI wraps our graphql functionality with authentication so that
6
+ # these concerns are taken care of.
7
+ #
8
+ class AdminAPI < API
9
+ autoload :PopulateResourceCommand, 'shopify-cli/admin_api/populate_resource_command'
10
+ autoload :Schema, 'shopify-cli/admin_api/schema'
11
+
12
+ class << self
13
+ ##
14
+ # issues a graphql query or mutation to the Shopify Admin API. It loads a graphql
15
+ # query from a file so that you do not need to use large unwieldy query strings.
16
+ #
17
+ # #### Parameters
18
+ # - `ctx`: running context from your command
19
+ # - `query_name`: name of the query you want to use, loaded from the `lib/graphql` directory.
20
+ # - `api_version`: an api version string to specify version. If no version is supplied then unstable will be used
21
+ # - `shop`: shop domain string for which shop that you are calling the admin
22
+ # API on. If not supplied, then it will be fetched from the `.env` file
23
+ # - `**variable`: a hash of variables to be supplied to the query ro mutation
24
+ #
25
+ # #### Raises
26
+ #
27
+ # * http 404 will raise a ShopifyCli::API::APIRequestNotFoundError
28
+ # * http 400..499 will raise a ShopifyCli::API::APIRequestClientError
29
+ # * http 500..599 will raise a ShopifyCli::API::APIRequestServerError
30
+ # * All other codes will raise ShopifyCli::API::APIRequestUnexpectedError
31
+ #
32
+ # #### Returns
33
+ #
34
+ # * `resp` - graphql response data hash. This can be a different shape for every query.
35
+ #
36
+ # #### Example
37
+ #
38
+ # ShopifyCli::AdminAPI.query(@ctx, 'all_organizations')
39
+ #
40
+ def query(ctx, query_name, api_version: nil, shop: nil, **variables)
41
+ shop ||= Project.current.env.shop
42
+ authenticated_req(ctx, shop) do
43
+ api_client(ctx, api_version, shop).query(query_name, variables: variables)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def authenticated_req(ctx, shop)
50
+ yield
51
+ rescue API::APIRequestUnauthorizedError
52
+ authenticate(ctx, shop)
53
+ retry
54
+ end
55
+
56
+ def authenticate(ctx, shop)
57
+ env = Project.current.env
58
+ ShopifyCli::OAuth.new(
59
+ ctx: ctx,
60
+ service: 'admin',
61
+ client_id: env.api_key,
62
+ secret: env.secret,
63
+ scopes: env.scopes,
64
+ token_path: "/access_token",
65
+ options: { 'grant_options[]' => 'per user' },
66
+ ).authenticate("https://#{shop}/admin/oauth")
67
+ end
68
+
69
+ def api_client(ctx, api_version, shop)
70
+ new(
71
+ ctx: ctx,
72
+ auth_header: 'X-Shopify-Access-Token',
73
+ token: admin_access_token(ctx, shop),
74
+ url: "https://#{shop}/admin/api/#{fetch_api_version(ctx, api_version, shop)}/graphql.json",
75
+ )
76
+ end
77
+
78
+ def admin_access_token(ctx, shop)
79
+ ShopifyCli::DB.get(:admin_access_token) do
80
+ authenticate(ctx, shop)
81
+ ShopifyCli::DB.get(:admin_access_token)
82
+ end
83
+ end
84
+
85
+ def fetch_api_version(ctx, api_version, shop)
86
+ return api_version unless api_version.nil?
87
+ client = new(
88
+ ctx: ctx,
89
+ auth_header: 'X-Shopify-Access-Token',
90
+ token: admin_access_token(ctx, shop),
91
+ url: "https://#{shop}/admin/api/unstable/graphql.json",
92
+ )
93
+ versions = client.query('api_versions')['data']['publicApiVersions']
94
+ latest = versions.find { |version| version['displayName'].include?('Latest') }
95
+ latest['handle']
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,165 @@
1
+ require 'shopify_cli'
2
+ require 'optparse'
3
+
4
+ module ShopifyCli
5
+ class AdminAPI
6
+ class PopulateResourceCommand < ShopifyCli::SubCommand
7
+ DEFAULT_COUNT = 5
8
+
9
+ attr_reader :input
10
+
11
+ class << self
12
+ attr_accessor :input_type
13
+
14
+ # we override the call classmethod here because we parse options at runtime
15
+ def call(args, command_name, _parent_command)
16
+ cmd = new(@ctx)
17
+ cmd.call(args, command_name)
18
+ end
19
+
20
+ def help
21
+ cmd = new(@ctx)
22
+ output = cmd.display_parent_help + "\n"
23
+ output + cmd.display_parent_extended_help
24
+ end
25
+ end
26
+
27
+ def call(args, _)
28
+ return unless Project.current
29
+ Tasks::EnsureEnv.call(@ctx)
30
+ @args = args
31
+ @input = Hash.new
32
+ @count = DEFAULT_COUNT
33
+ @help = false
34
+ input_options
35
+ resource_options.parse(@args)
36
+
37
+ if @help
38
+ output = display_parent_extended_help
39
+ output += "\n#{@ctx.message('core.populate.options.header', camel_case_resource_type)}\n"
40
+ output += resource_options.help
41
+ return @ctx.puts(output)
42
+ end
43
+
44
+ if @silent
45
+ spin_group = CLI::UI::SpinGroup.new
46
+ spin_group.add(@ctx.message('core.populate.populating', @count, camel_case_resource_type)) do |spinner|
47
+ populate
48
+ spinner.update_title(completion_message)
49
+ end
50
+ spin_group.wait
51
+ else
52
+ populate
53
+ @ctx.puts(completion_message)
54
+ end
55
+ end
56
+
57
+ def message
58
+ raise NotImplementedError
59
+ end
60
+
61
+ def defaults
62
+ raise NotImplementedError
63
+ end
64
+
65
+ def display_parent_help
66
+ parent_command_klass.respond_to?(:help) ? parent_command_klass.help : ""
67
+ end
68
+
69
+ def display_parent_extended_help
70
+ parent_command_klass.respond_to?(:extended_help) ? parent_command_klass.extended_help : ""
71
+ end
72
+
73
+ def resource_options
74
+ @resource_options ||= OptionParser.new do |opts|
75
+ opts.banner = ""
76
+ opts.on(
77
+ "-c #{DEFAULT_COUNT}",
78
+ "--count=#{DEFAULT_COUNT}",
79
+ @ctx.message('core.populate.options.count_help')
80
+ ) do |value|
81
+ @count = value.to_i
82
+ end
83
+
84
+ opts.on('-h', '--help', 'print help') do |value|
85
+ @help = value
86
+ end
87
+
88
+ opts.on("--silent") { |v| @silent = v }
89
+
90
+ opts.on('--shop=', '-s') { |value| @shop = value }
91
+ end
92
+ end
93
+
94
+ def populate
95
+ @count.times do
96
+ run_mutation(defaults.merge(@input))
97
+ end
98
+ end
99
+
100
+ def input_options
101
+ schema.type(self.class.input_type)['inputFields'].each do |field|
102
+ resource_options.on(
103
+ "--#{field['name']}=#{field['defaultValue']}",
104
+ field['description']
105
+ ) do |value|
106
+ @input[field['name']] = value
107
+ end
108
+ end
109
+ end
110
+
111
+ def schema
112
+ @schema ||= AdminAPI::Schema.get(@ctx)
113
+ end
114
+
115
+ def run_mutation(data)
116
+ kwargs = { input: data }
117
+ kwargs[:shop] = @shop if @shop
118
+ resp = AdminAPI.query(
119
+ @ctx, "create_#{snake_case_resource_type}", kwargs
120
+ )
121
+ @ctx.abort(resp['errors']) if resp['errors']
122
+ @ctx.done(message(resp['data'])) unless @silent
123
+ end
124
+
125
+ def completion_message
126
+ plural = @count > 1 ? "s" : ""
127
+ @ctx.message(
128
+ 'core.populate.completion_message',
129
+ @count,
130
+ "#{camel_case_resource_type}#{plural}",
131
+ Project.current.env.shop,
132
+ camel_case_resource_type,
133
+ admin_url,
134
+ snake_case_resource_type
135
+ )
136
+ end
137
+
138
+ def admin_url
139
+ "https://#{Project.current.env.shop}/admin/"
140
+ end
141
+
142
+ def price
143
+ format('%.2f', rand(1..10))
144
+ end
145
+
146
+ private
147
+
148
+ def camel_case_resource_type
149
+ @camel_case_resource_type ||= self.class.to_s.split('::').last
150
+ end
151
+
152
+ def snake_case_resource_type
153
+ @snake_case_resource_type ||= camel_case_resource_type.to_s
154
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
155
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
156
+ .tr("-", "_")
157
+ .downcase
158
+ end
159
+
160
+ def parent_command_klass
161
+ @parent_command_klass ||= Module.const_get(self.class.to_s.split('::')[0..-2].join('::'))
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,32 @@
1
+ require 'shopify_cli'
2
+
3
+ module ShopifyCli
4
+ class AdminAPI
5
+ class Schema < Hash
6
+ def self.get(ctx)
7
+ unless ShopifyCli::DB.exists?(:shopify_admin_schema)
8
+ schema = AdminAPI.query(ctx, 'admin_introspection')
9
+ ShopifyCli::DB.set(shopify_admin_schema: JSON.dump(schema))
10
+ end
11
+ # This is ruby magic for making a new hash with another hash.
12
+ # It wraps the JSON in our Schema Class to have the helper methods
13
+ # available
14
+ self[JSON.parse(ShopifyCli::DB.get(:shopify_admin_schema))]
15
+ end
16
+
17
+ def type(name)
18
+ data = self["data"]
19
+ schema = data["__schema"]
20
+ schema["types"].find do |object|
21
+ object['name'] == name.to_s
22
+ end
23
+ end
24
+
25
+ def get_names_from_type(name)
26
+ type(name)["enumValues"].map do |object|
27
+ object["name"]
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,104 @@
1
+ require 'shopify_cli'
2
+ require 'net/http'
3
+
4
+ module ShopifyCli
5
+ class API
6
+ include SmartProperties
7
+
8
+ property! :ctx, accepts: ShopifyCli::Context
9
+ property! :token, accepts: String
10
+ property :auth_header, accepts: String
11
+ property! :url, accepts: String
12
+
13
+ class APIRequestError < StandardError; end
14
+ class APIRequestNotFoundError < APIRequestError; end
15
+ class APIRequestClientError < APIRequestError; end
16
+ class APIRequestUnauthorizedError < APIRequestClientError; end
17
+ class APIRequestUnexpectedError < APIRequestError; end
18
+ class APIRequestRetriableError < APIRequestError; end
19
+ class APIRequestServerError < APIRequestRetriableError; end
20
+ class APIRequestThrottledError < APIRequestRetriableError; end
21
+
22
+ def self.gid_to_id(gid)
23
+ gid.split('/').last
24
+ end
25
+
26
+ def query(query_name, variables: {})
27
+ _, resp = request(
28
+ load_query(query_name),
29
+ variables: variables,
30
+ headers: default_headers,
31
+ graphql_url: url,
32
+ )
33
+ ctx.debug(resp)
34
+ resp
35
+ end
36
+
37
+ protected
38
+
39
+ def load_query(name)
40
+ project_type = ShopifyCli::Project.current_project_type
41
+ project_file_path = File.join(
42
+ ShopifyCli::ROOT, 'lib', 'project_types', project_type.to_s, 'graphql', "#{name}.graphql"
43
+ )
44
+ if !project_type.nil? && File.exist?(project_file_path)
45
+ File.read(project_file_path)
46
+ else
47
+ File.read(File.join(ShopifyCli::ROOT, 'lib', 'graphql', "#{name}.graphql"))
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def request(body, graphql_url:, variables: {}, headers: {})
54
+ CLI::Kit::Util.begin do
55
+ uri = URI.parse(graphql_url)
56
+ unless uri.is_a?(URI::HTTP)
57
+ ctx.abort("Invalid URL: #{graphql_url}")
58
+ end
59
+ http = ::Net::HTTP.new(uri.host, uri.port)
60
+ http.use_ssl = true
61
+
62
+ req = ::Net::HTTP::Post.new(uri.request_uri)
63
+ req.body = JSON.dump(query: body.tr("\n", ""), variables: variables)
64
+ req['Content-Type'] = 'application/json'
65
+ headers.each { |header, value| req[header] = value }
66
+ response = http.request(req)
67
+
68
+ case response.code.to_i
69
+ when 200..399
70
+ [response.code.to_i, JSON.parse(response.body)]
71
+ when 401
72
+ raise APIRequestUnauthorizedError, "#{response.code}\n#{response.body}"
73
+ when 404
74
+ raise APIRequestNotFoundError, "#{response.code}\n#{response.body}"
75
+ when 429
76
+ raise APIRequestThrottledError, "#{response.code}\n#{response.body}"
77
+ when 400..499
78
+ raise APIRequestClientError, "#{response.code}\n#{response.body}"
79
+ when 500..599
80
+ raise APIRequestServerError, "#{response.code}\n#{response.body}"
81
+ else
82
+ raise APIRequestUnexpectedError, "#{response.code}\n#{response.body}"
83
+ end
84
+ end.retry_after(APIRequestRetriableError, retries: 3) do |e|
85
+ sleep(1) if e.is_a?(APIRequestThrottledError)
86
+ end
87
+ end
88
+
89
+ def current_sha
90
+ @current_sha ||= Git.sha(dir: ShopifyCli::ROOT)
91
+ end
92
+
93
+ def default_headers
94
+ {
95
+ 'User-Agent' => "Shopify App CLI #{ShopifyCli::VERSION} #{current_sha} | #{ctx.uname}",
96
+ }.merge(auth_headers(token))
97
+ end
98
+
99
+ def auth_headers(token)
100
+ raise NotImplementedError if auth_header.nil?
101
+ { auth_header => token }
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+ require 'shopify_cli'
3
+
4
+ module ShopifyCli
5
+ class Command < CLI::Kit::BaseCommand
6
+ attr_writer :ctx
7
+ attr_accessor :options
8
+
9
+ class << self
10
+ attr_writer :ctx
11
+ attr_reader :hidden
12
+
13
+ def call(args, command_name)
14
+ subcommand, resolved_name = subcommand_registry.lookup_command(args.first)
15
+ if subcommand
16
+ subcommand.ctx = @ctx
17
+ subcommand.call(args, resolved_name, command_name)
18
+ else
19
+ cmd = new(@ctx)
20
+ cmd.options.parse(@_options, args)
21
+ return call_help(command_name) if cmd.options.help
22
+ cmd.call(args, command_name)
23
+ end
24
+ end
25
+
26
+ def hidden_command
27
+ @hidden = true
28
+ end
29
+
30
+ def options(&block)
31
+ @_options = block
32
+ end
33
+
34
+ def subcommand(const, cmd, path = nil)
35
+ autoload(const, path) if path
36
+ subcommand_registry.add(->() { const_get(const) }, cmd.to_s)
37
+ end
38
+
39
+ def subcommand_registry
40
+ @subcommand_registry ||= CLI::Kit::CommandRegistry.new(
41
+ default: nil,
42
+ contextual_resolver: nil,
43
+ )
44
+ end
45
+
46
+ def prerequisite_task(*tasks)
47
+ tasks.each do |task|
48
+ prerequisite_tasks[task] = ShopifyCli::Tasks::Registry[task]
49
+ end
50
+ end
51
+
52
+ def prerequisite_tasks
53
+ @prerequisite_tasks ||= {}
54
+ end
55
+
56
+ def call_help(*cmds)
57
+ help = Commands::Help.new(@ctx)
58
+ help.call(cmds, nil)
59
+ end
60
+ end
61
+
62
+ def initialize(ctx = nil)
63
+ @ctx = ctx || ShopifyCli::Context.new
64
+ self.options = Options.new
65
+ end
66
+ end
67
+ end