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.
- checksums.yaml +7 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/CODE_OF_CONDUCT.md +73 -0
- data/.github/CONTRIBUTING.md +51 -0
- data/.github/DESIGN.md +153 -0
- data/.github/ISSUE_TEMPLATE.md +38 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +22 -0
- data/.github/probots.yml +3 -0
- data/.gitignore +19 -0
- data/.rubocop.yml +47 -0
- data/.ruby-version +1 -0
- data/.travis.yml +12 -0
- data/Gemfile +22 -0
- data/Gemfile.lock +77 -0
- data/LICENSE.md +7 -0
- data/README.md +13 -0
- data/Rakefile +101 -0
- data/SECURITY.md +59 -0
- data/Vagrantfile +17 -0
- data/bin/load_shopify.rb +20 -0
- data/bin/shopify +32 -0
- data/dev.yml +17 -0
- data/docs/Gemfile +5 -0
- data/docs/Gemfile.lock +248 -0
- data/docs/_config.yml +16 -0
- data/docs/_data/nav.yml +26 -0
- data/docs/_includes/footer.html +15 -0
- data/docs/_includes/head.html +19 -0
- data/docs/_includes/sidebar_nav.html +22 -0
- data/docs/_includes/toc.html +112 -0
- data/docs/_layouts/default.html +79 -0
- data/docs/app/node/commands/index.md +82 -0
- data/docs/app/node/index.md +35 -0
- data/docs/app/rails/commands/index.md +80 -0
- data/docs/app/rails/index.md +36 -0
- data/docs/core/index.md +70 -0
- data/docs/css/docs.css +157 -0
- data/docs/getting-started/index.md +61 -0
- data/docs/help/start-app/index.md +6 -0
- data/docs/images/header.png +0 -0
- data/docs/index.md +27 -0
- data/docs/installing-ruby.md +28 -0
- data/ext/shopify-cli/extconf.rb +27 -0
- data/install.sh +7 -0
- data/lib/docgen/class_template.md.erb +81 -0
- data/lib/docgen/index_template.md.erb +5 -0
- data/lib/docgen/markdown.rb +101 -0
- data/lib/graphql/admin_introspection.graphql +87 -0
- data/lib/graphql/all_organizations.graphql +19 -0
- data/lib/graphql/all_orgs_with_apps.graphql +30 -0
- data/lib/graphql/api_versions.graphql +6 -0
- data/lib/graphql/convert_dev_to_test_store.graphql +10 -0
- data/lib/graphql/create_app.graphql +20 -0
- data/lib/graphql/create_customer.graphql +9 -0
- data/lib/graphql/create_draft_order.graphql +8 -0
- data/lib/graphql/create_product.graphql +9 -0
- data/lib/graphql/extension_create.graphql +21 -0
- data/lib/graphql/extension_update_draft.graphql +18 -0
- data/lib/graphql/find_organization.graphql +17 -0
- data/lib/graphql/get_app_urls.graphql +6 -0
- data/lib/graphql/update_dashboard_urls.graphql +8 -0
- data/lib/project_types/extension/cli.rb +71 -0
- data/lib/project_types/extension/commands/build.rb +29 -0
- data/lib/project_types/extension/commands/create.rb +49 -0
- data/lib/project_types/extension/commands/extension_command.rb +22 -0
- data/lib/project_types/extension/commands/push.rb +69 -0
- data/lib/project_types/extension/commands/register.rb +78 -0
- data/lib/project_types/extension/commands/serve.rb +24 -0
- data/lib/project_types/extension/commands/tunnel.rb +69 -0
- data/lib/project_types/extension/extension_project.rb +85 -0
- data/lib/project_types/extension/extension_project_keys.rb +10 -0
- data/lib/project_types/extension/features/argo.rb +48 -0
- data/lib/project_types/extension/features/argo_dependencies.rb +28 -0
- data/lib/project_types/extension/features/argo_setup.rb +54 -0
- data/lib/project_types/extension/features/argo_setup_step.rb +31 -0
- data/lib/project_types/extension/features/argo_setup_steps.rb +53 -0
- data/lib/project_types/extension/features/tunnel_url.rb +20 -0
- data/lib/project_types/extension/forms/create.rb +52 -0
- data/lib/project_types/extension/forms/register.rb +48 -0
- data/lib/project_types/extension/messages/message_loading.rb +37 -0
- data/lib/project_types/extension/messages/messages.rb +126 -0
- data/lib/project_types/extension/models/app.rb +14 -0
- data/lib/project_types/extension/models/registration.rb +19 -0
- data/lib/project_types/extension/models/type.rb +76 -0
- data/lib/project_types/extension/models/types/checkout_post_purchase.rb +20 -0
- data/lib/project_types/extension/models/types/subscription_management.rb +20 -0
- data/lib/project_types/extension/models/validation_error.rb +17 -0
- data/lib/project_types/extension/models/version.rb +15 -0
- data/lib/project_types/extension/tasks/converters/registration_converter.rb +26 -0
- data/lib/project_types/extension/tasks/converters/validation_error_converter.rb +25 -0
- data/lib/project_types/extension/tasks/converters/version_converter.rb +28 -0
- data/lib/project_types/extension/tasks/create_extension.rb +31 -0
- data/lib/project_types/extension/tasks/get_apps.rb +34 -0
- data/lib/project_types/extension/tasks/update_draft.rb +29 -0
- data/lib/project_types/extension/tasks/user_errors.rb +45 -0
- data/lib/project_types/node/cli.rb +37 -0
- data/lib/project_types/node/commands/create.rb +117 -0
- data/lib/project_types/node/commands/deploy.rb +22 -0
- data/lib/project_types/node/commands/deploy/heroku.rb +91 -0
- data/lib/project_types/node/commands/generate.rb +51 -0
- data/lib/project_types/node/commands/generate/billing.rb +37 -0
- data/lib/project_types/node/commands/generate/page.rb +55 -0
- data/lib/project_types/node/commands/generate/webhook.rb +33 -0
- data/lib/project_types/node/commands/open.rb +16 -0
- data/lib/project_types/node/commands/populate.rb +23 -0
- data/lib/project_types/node/commands/populate/customer.rb +31 -0
- data/lib/project_types/node/commands/populate/draft_order.rb +28 -0
- data/lib/project_types/node/commands/populate/product.rb +30 -0
- data/lib/project_types/node/commands/serve.rb +45 -0
- data/lib/project_types/node/commands/tunnel.rb +39 -0
- data/lib/project_types/node/forms/create.rb +87 -0
- data/lib/project_types/node/messages/messages.rb +260 -0
- data/lib/project_types/rails/cli.rb +41 -0
- data/lib/project_types/rails/commands/create.rb +126 -0
- data/lib/project_types/rails/commands/deploy.rb +22 -0
- data/lib/project_types/rails/commands/deploy/heroku.rb +113 -0
- data/lib/project_types/rails/commands/generate.rb +49 -0
- data/lib/project_types/rails/commands/generate/webhook.rb +39 -0
- data/lib/project_types/rails/commands/open.rb +16 -0
- data/lib/project_types/rails/commands/populate.rb +23 -0
- data/lib/project_types/rails/commands/populate/customer.rb +31 -0
- data/lib/project_types/rails/commands/populate/draft_order.rb +28 -0
- data/lib/project_types/rails/commands/populate/product.rb +30 -0
- data/lib/project_types/rails/commands/serve.rb +47 -0
- data/lib/project_types/rails/commands/tunnel.rb +39 -0
- data/lib/project_types/rails/forms/create.rb +116 -0
- data/lib/project_types/rails/gem.rb +56 -0
- data/lib/project_types/rails/messages/messages.rb +283 -0
- data/lib/project_types/rails/ruby.rb +17 -0
- data/lib/project_types/script/cli.rb +76 -0
- data/lib/project_types/script/commands/create.rb +45 -0
- data/lib/project_types/script/commands/disable.rb +36 -0
- data/lib/project_types/script/commands/enable.rb +46 -0
- data/lib/project_types/script/commands/push.rb +39 -0
- data/lib/project_types/script/config/extension_points.yml +18 -0
- data/lib/project_types/script/errors.rb +16 -0
- data/lib/project_types/script/forms/create.rb +29 -0
- data/lib/project_types/script/forms/enable.rb +24 -0
- data/lib/project_types/script/forms/push.rb +19 -0
- data/lib/project_types/script/forms/script_form.rb +66 -0
- data/lib/project_types/script/graphql/app_script_update_or_create.graphql +27 -0
- data/lib/project_types/script/graphql/script_service_proxy.graphql +8 -0
- data/lib/project_types/script/graphql/shop_script_delete.graphql +14 -0
- data/lib/project_types/script/graphql/shop_script_update_or_create.graphql +28 -0
- data/lib/project_types/script/layers/application/build_script.rb +43 -0
- data/lib/project_types/script/layers/application/create_script.rb +47 -0
- data/lib/project_types/script/layers/application/disable_script.rb +19 -0
- data/lib/project_types/script/layers/application/enable_script.rb +21 -0
- data/lib/project_types/script/layers/application/extension_points.rb +17 -0
- data/lib/project_types/script/layers/application/project_dependencies.rb +34 -0
- data/lib/project_types/script/layers/application/push_script.rb +30 -0
- data/lib/project_types/script/layers/domain/errors.rb +25 -0
- data/lib/project_types/script/layers/domain/extension_point.rb +29 -0
- data/lib/project_types/script/layers/domain/push_package.rb +29 -0
- data/lib/project_types/script/layers/domain/script.rb +18 -0
- data/lib/project_types/script/layers/infrastructure/assemblyscript_dependency_manager.rb +73 -0
- data/lib/project_types/script/layers/infrastructure/assemblyscript_tsconfig.rb +38 -0
- data/lib/project_types/script/layers/infrastructure/assemblyscript_wasm_builder.rb +39 -0
- data/lib/project_types/script/layers/infrastructure/dependency_manager.rb +36 -0
- data/lib/project_types/script/layers/infrastructure/errors.rb +38 -0
- data/lib/project_types/script/layers/infrastructure/extension_point_repository.rb +31 -0
- data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +47 -0
- data/lib/project_types/script/layers/infrastructure/script_builder.rb +34 -0
- data/lib/project_types/script/layers/infrastructure/script_repository.rb +89 -0
- data/lib/project_types/script/layers/infrastructure/script_service.rb +165 -0
- data/lib/project_types/script/layers/infrastructure/test_suite_repository.rb +59 -0
- data/lib/project_types/script/messages/messages.rb +204 -0
- data/lib/project_types/script/script_project.rb +37 -0
- data/lib/project_types/script/templates/ts/as-pect.config.js +21 -0
- data/lib/project_types/script/ui/error_handler.rb +136 -0
- data/lib/project_types/script/ui/strict_spinner.rb +22 -0
- data/lib/rubygems_plugin.rb +18 -0
- data/lib/shopify-cli/admin_api.rb +99 -0
- data/lib/shopify-cli/admin_api/populate_resource_command.rb +165 -0
- data/lib/shopify-cli/admin_api/schema.rb +32 -0
- data/lib/shopify-cli/api.rb +104 -0
- data/lib/shopify-cli/command.rb +67 -0
- data/lib/shopify-cli/commands.rb +28 -0
- data/lib/shopify-cli/commands/connect.rb +108 -0
- data/lib/shopify-cli/commands/create.rb +50 -0
- data/lib/shopify-cli/commands/help.rb +79 -0
- data/lib/shopify-cli/commands/logout.rb +23 -0
- data/lib/shopify-cli/commands/system.rb +135 -0
- data/lib/shopify-cli/commands/version.rb +15 -0
- data/lib/shopify-cli/context.rb +372 -0
- data/lib/shopify-cli/core.rb +9 -0
- data/lib/shopify-cli/core/entry_point.rb +40 -0
- data/lib/shopify-cli/core/executor.rb +21 -0
- data/lib/shopify-cli/core/help_resolver.rb +20 -0
- data/lib/shopify-cli/core/monorail.rb +118 -0
- data/lib/shopify-cli/db.rb +114 -0
- data/lib/shopify-cli/form.rb +40 -0
- data/lib/shopify-cli/git.rb +141 -0
- data/lib/shopify-cli/helpers.rb +5 -0
- data/lib/shopify-cli/helpers/haikunator.rb +92 -0
- data/lib/shopify-cli/heroku.rb +97 -0
- data/lib/shopify-cli/js_deps.rb +110 -0
- data/lib/shopify-cli/js_system.rb +98 -0
- data/lib/shopify-cli/messages/messages.rb +287 -0
- data/lib/shopify-cli/oauth.rb +192 -0
- data/lib/shopify-cli/oauth/servlet.rb +61 -0
- data/lib/shopify-cli/options.rb +40 -0
- data/lib/shopify-cli/packager.rb +116 -0
- data/lib/shopify-cli/partners_api.rb +114 -0
- data/lib/shopify-cli/partners_api/organizations.rb +32 -0
- data/lib/shopify-cli/process_supervision.rb +187 -0
- data/lib/shopify-cli/project.rb +191 -0
- data/lib/shopify-cli/project_type.rb +83 -0
- data/lib/shopify-cli/resources.rb +5 -0
- data/lib/shopify-cli/resources/env_file.rb +96 -0
- data/lib/shopify-cli/sub_command.rb +15 -0
- data/lib/shopify-cli/task.rb +10 -0
- data/lib/shopify-cli/tasks.rb +32 -0
- data/lib/shopify-cli/tasks/create_api_client.rb +29 -0
- data/lib/shopify-cli/tasks/ensure_dev_store.rb +41 -0
- data/lib/shopify-cli/tasks/ensure_env.rb +31 -0
- data/lib/shopify-cli/tasks/ensure_loopback_url.rb +20 -0
- data/lib/shopify-cli/tasks/update_dashboard_urls.rb +44 -0
- data/lib/shopify-cli/tunnel.rb +154 -0
- data/lib/shopify-cli/version.rb +3 -0
- data/lib/shopify_cli.rb +132 -0
- data/shopify-cli.gemspec +40 -0
- data/shopify.fish +12 -0
- data/shopify.sh +11 -0
- data/vendor/deps/cli-kit/REVISION +1 -0
- data/vendor/deps/cli-kit/lib/cli/kit.rb +60 -0
- data/vendor/deps/cli-kit/lib/cli/kit/autocall.rb +21 -0
- data/vendor/deps/cli-kit/lib/cli/kit/base_command.rb +49 -0
- data/vendor/deps/cli-kit/lib/cli/kit/command_registry.rb +94 -0
- data/vendor/deps/cli-kit/lib/cli/kit/config.rb +133 -0
- data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +115 -0
- data/vendor/deps/cli-kit/lib/cli/kit/executor.rb +81 -0
- data/vendor/deps/cli-kit/lib/cli/kit/ini.rb +102 -0
- data/vendor/deps/cli-kit/lib/cli/kit/levenshtein.rb +82 -0
- data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +76 -0
- data/vendor/deps/cli-kit/lib/cli/kit/resolver.rb +60 -0
- data/vendor/deps/cli-kit/lib/cli/kit/ruby_backports/enumerable.rb +6 -0
- data/vendor/deps/cli-kit/lib/cli/kit/support.rb +9 -0
- data/vendor/deps/cli-kit/lib/cli/kit/support/test_helper.rb +244 -0
- data/vendor/deps/cli-kit/lib/cli/kit/system.rb +207 -0
- data/vendor/deps/cli-kit/lib/cli/kit/util.rb +189 -0
- data/vendor/deps/cli-kit/lib/cli/kit/version.rb +5 -0
- data/vendor/deps/cli-ui/REVISION +1 -0
- data/vendor/deps/cli-ui/lib/cli/ui.rb +187 -0
- data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +153 -0
- data/vendor/deps/cli-ui/lib/cli/ui/box.rb +15 -0
- data/vendor/deps/cli-ui/lib/cli/ui/color.rb +79 -0
- data/vendor/deps/cli-ui/lib/cli/ui/formatter.rb +179 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +310 -0
- data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +78 -0
- data/vendor/deps/cli-ui/lib/cli/ui/progress.rb +88 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +248 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +472 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/options_handler.rb +24 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +48 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner/async.rb +40 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +241 -0
- data/vendor/deps/cli-ui/lib/cli/ui/stdout_router.rb +227 -0
- data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +36 -0
- data/vendor/deps/cli-ui/lib/cli/ui/truncater.rb +102 -0
- data/vendor/deps/cli-ui/lib/cli/ui/version.rb +5 -0
- data/vendor/deps/smart_properties/REVISION +1 -0
- data/vendor/deps/smart_properties/lib/smart_properties.rb +174 -0
- data/vendor/deps/smart_properties/lib/smart_properties/errors.rb +114 -0
- data/vendor/deps/smart_properties/lib/smart_properties/property.rb +162 -0
- data/vendor/deps/smart_properties/lib/smart_properties/property_collection.rb +83 -0
- data/vendor/deps/smart_properties/lib/smart_properties/validations.rb +8 -0
- data/vendor/deps/smart_properties/lib/smart_properties/validations/ancestor.rb +27 -0
- data/vendor/deps/smart_properties/lib/smart_properties/version.rb +3 -0
- data/vendor/lib/semantic/LICENSE +20 -0
- data/vendor/lib/semantic/semantic.rb +4 -0
- data/vendor/lib/semantic/version.rb +180 -0
- 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
|