shopify-cli 1.7.1 → 1.11.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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +1 -0
  3. data/.github/workflows/build.yml +28 -0
  4. data/.rubocop_todo.yml +15 -2
  5. data/CHANGELOG.md +25 -0
  6. data/Gemfile.lock +15 -15
  7. data/README.md +2 -1
  8. data/dev.yml +3 -0
  9. data/lib/docgen/markdown.rb +1 -1
  10. data/lib/graphql/extension_create.graphql +17 -2
  11. data/lib/graphql/fetch_specifications.graphql +14 -0
  12. data/lib/project_types/extension/cli.rb +8 -10
  13. data/lib/project_types/extension/commands/create.rb +2 -2
  14. data/lib/project_types/extension/commands/extension_command.rb +10 -6
  15. data/lib/project_types/extension/commands/push.rb +2 -2
  16. data/lib/project_types/extension/commands/register.rb +6 -5
  17. data/lib/project_types/extension/commands/serve.rb +64 -22
  18. data/lib/project_types/extension/commands/tunnel.rb +3 -1
  19. data/lib/project_types/extension/extension_project.rb +20 -4
  20. data/lib/project_types/extension/extension_project_keys.rb +2 -1
  21. data/lib/project_types/extension/features/argo.rb +19 -44
  22. data/lib/project_types/extension/features/argo_runtime.rb +63 -0
  23. data/lib/project_types/extension/features/argo_serve.rb +79 -0
  24. data/lib/project_types/extension/features/argo_serve_options.rb +40 -0
  25. data/lib/project_types/extension/features/argo_setup.rb +1 -1
  26. data/lib/project_types/extension/forms/questions/ask_type.rb +16 -5
  27. data/lib/project_types/extension/messages/message_loading.rb +3 -1
  28. data/lib/project_types/extension/messages/messages.rb +9 -6
  29. data/lib/project_types/extension/models/npm_package.rb +14 -0
  30. data/lib/project_types/extension/models/registration.rb +1 -0
  31. data/lib/project_types/extension/models/specification.rb +7 -2
  32. data/lib/project_types/extension/models/specification_handlers/checkout_argo_extension.rb +18 -0
  33. data/lib/project_types/extension/models/specification_handlers/default.rb +35 -2
  34. data/lib/project_types/extension/models/specifications.rb +12 -1
  35. data/lib/project_types/extension/models/version.rb +1 -1
  36. data/lib/project_types/extension/tasks/choose_next_available_port.rb +36 -0
  37. data/lib/project_types/extension/tasks/configure_features.rb +5 -1
  38. data/lib/project_types/extension/tasks/converters/registration_converter.rb +2 -0
  39. data/lib/project_types/extension/tasks/fetch_specifications.rb +8 -28
  40. data/lib/project_types/extension/tasks/find_npm_packages.rb +106 -0
  41. data/lib/project_types/node/commands/generate.rb +0 -22
  42. data/lib/project_types/node/forms/create.rb +10 -1
  43. data/lib/project_types/node/messages/messages.rb +5 -4
  44. data/lib/project_types/rails/forms/create.rb +11 -1
  45. data/lib/project_types/rails/messages/messages.rb +5 -4
  46. data/lib/project_types/script/cli.rb +7 -8
  47. data/lib/project_types/script/commands/create.rb +0 -7
  48. data/lib/project_types/script/commands/push.rb +3 -3
  49. data/lib/project_types/script/config/extension_points.yml +17 -0
  50. data/lib/project_types/script/errors.rb +0 -19
  51. data/lib/project_types/script/forms/create.rb +3 -14
  52. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +5 -5
  53. data/lib/project_types/script/graphql/get_app_scripts.graphql +6 -0
  54. data/lib/project_types/script/graphql/script_service_proxy.graphql +1 -2
  55. data/lib/project_types/script/layers/application/build_script.rb +1 -2
  56. data/lib/project_types/script/layers/application/create_script.rb +32 -49
  57. data/lib/project_types/script/layers/application/extension_points.rb +3 -2
  58. data/lib/project_types/script/layers/application/push_script.rb +5 -5
  59. data/lib/project_types/script/layers/domain/errors.rb +0 -2
  60. data/lib/project_types/script/layers/domain/extension_point.rb +60 -45
  61. data/lib/project_types/script/layers/domain/metadata.rb +18 -25
  62. data/lib/project_types/script/layers/domain/push_package.rb +4 -4
  63. data/lib/project_types/script/layers/domain/script_project.rb +54 -0
  64. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +39 -14
  65. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +14 -43
  66. data/lib/project_types/script/layers/infrastructure/command_runner.rb +19 -0
  67. data/lib/project_types/script/layers/infrastructure/errors.rb +40 -20
  68. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +12 -13
  69. data/lib/project_types/script/layers/infrastructure/rust_project_creator.rb +9 -10
  70. data/lib/project_types/script/layers/infrastructure/rust_task_runner.rb +6 -7
  71. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +172 -0
  72. data/lib/project_types/script/layers/infrastructure/script_service.rb +25 -76
  73. data/lib/project_types/script/messages/messages.rb +20 -53
  74. data/lib/project_types/script/tasks/ensure_env.rb +85 -0
  75. data/lib/project_types/script/ui/error_handler.rb +32 -41
  76. data/lib/shopify-cli/context.rb +28 -0
  77. data/lib/shopify-cli/js_system.rb +2 -2
  78. data/lib/shopify-cli/messages/messages.rb +50 -45
  79. data/lib/shopify-cli/method_object.rb +4 -4
  80. data/lib/shopify-cli/oauth.rb +9 -3
  81. data/lib/shopify-cli/packager.rb +1 -1
  82. data/lib/shopify-cli/partners_api/organizations.rb +3 -3
  83. data/lib/shopify-cli/resolve_constant.rb +1 -1
  84. data/lib/shopify-cli/resources/env_file.rb +1 -1
  85. data/lib/shopify-cli/tasks/ensure_dev_store.rb +1 -1
  86. data/lib/shopify-cli/tasks/select_org_and_shop.rb +6 -4
  87. data/lib/shopify-cli/transform_data_structure.rb +2 -2
  88. data/lib/shopify-cli/tunnel.rb +22 -1
  89. data/lib/shopify-cli/version.rb +1 -1
  90. data/lib/shopify_cli.rb +0 -1
  91. data/vendor/deps/smart_properties/REVISION +1 -1
  92. data/vendor/deps/smart_properties/lib/smart_properties/property.rb +7 -1
  93. data/vendor/deps/smart_properties/lib/smart_properties/version.rb +1 -1
  94. metadata +17 -12
  95. data/.travis.yml +0 -14
  96. data/lib/project_types/script/commands/disable.rb +0 -25
  97. data/lib/project_types/script/commands/enable.rb +0 -80
  98. data/lib/project_types/script/graphql/shop_script_delete.graphql +0 -14
  99. data/lib/project_types/script/graphql/shop_script_update_or_create.graphql +0 -28
  100. data/lib/project_types/script/layers/application/disable_script.rb +0 -21
  101. data/lib/project_types/script/layers/application/enable_script.rb +0 -23
  102. data/lib/project_types/script/layers/infrastructure/config_ui_repository.rb +0 -46
  103. data/lib/project_types/script/script_project.rb +0 -64
@@ -33,9 +33,7 @@ module Script
33
33
 
34
34
  def dependencies_installed?
35
35
  # Assuming if node_modules folder exist at root of script folder, all deps are installed
36
- return false unless ctx.dir_exist?("node_modules")
37
- check_if_ep_dependencies_up_to_date!
38
- true
36
+ ctx.dir_exist?("node_modules")
39
37
  end
40
38
 
41
39
  def metadata
@@ -65,9 +63,7 @@ module Script
65
63
 
66
64
  def compile
67
65
  check_compilation_dependencies!
68
-
69
- out, status = ctx.capture2e(SCRIPT_SDK_BUILD)
70
- raise Domain::Errors::ServiceFailureError, out unless status.success?
66
+ CommandRunner.new(ctx: ctx).call(SCRIPT_SDK_BUILD)
71
67
  end
72
68
 
73
69
  def check_compilation_dependencies!
@@ -83,47 +79,22 @@ module Script
83
79
  end
84
80
 
85
81
  def bytecode
86
- blob = format(BYTECODE_FILE, name: script_name)
87
- raise Errors::WebAssemblyBinaryNotFoundError unless @ctx.file_exist?(blob)
82
+ legacy_filename = format(BYTECODE_FILE, name: script_name)
83
+ filename = format(BYTECODE_FILE, name: "script")
84
+
85
+ bytecode_file = if ctx.file_exist?(filename)
86
+ filename
87
+ elsif ctx.file_exist?(legacy_filename)
88
+ legacy_filename
89
+ else
90
+ raise Errors::WebAssemblyBinaryNotFoundError
91
+ end
88
92
 
89
- contents = File.read(blob)
90
- @ctx.rm(blob)
93
+ contents = ctx.binread(bytecode_file)
94
+ ctx.rm(bytecode_file)
91
95
 
92
96
  contents
93
97
  end
94
-
95
- def check_if_ep_dependencies_up_to_date!
96
- return true if ENV["SHOPIFY_CLI_SCRIPTS_IGNORE_OUTDATED"]
97
-
98
- # ignore exit code since it will not be 0 unless every package is up to date which they probably won't be
99
- out, _ = ctx.capture2e("npm", "outdated", "--json", "--depth", "0")
100
- parsed_outdated_check = JSON.parse(out)
101
- outdated_ep_packages = parsed_outdated_check
102
- .select { |package_name, _| package_name.start_with?("@shopify/extension-point-as-") }
103
- .select { |_, version_info| !package_is_up_to_date?(version_info) }
104
- .keys
105
- raise Errors::PackagesOutdatedError.new(outdated_ep_packages),
106
- "NPM packages out of date: #{outdated_ep_packages.join(", ")}" unless outdated_ep_packages.empty?
107
- end
108
-
109
- def package_is_up_to_date?(version_info)
110
- require "semantic/semantic"
111
- current_version = version_info["current"]
112
- latest_version = version_info["latest"]
113
-
114
- # making an assumption that the script developer knows what they're doing if they're not referencing a
115
- # semver version
116
- begin
117
- current_version = ::Semantic::Version.new(current_version)
118
- latest_version = ::Semantic::Version.new(latest_version)
119
- rescue ArgumentError
120
- return true
121
- end
122
-
123
- return false if current_version.major < latest_version.major
124
- return false if latest_version.major == 0 && current_version.minor < latest_version.minor
125
- true
126
- end
127
98
  end
128
99
  end
129
100
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Script
4
+ module Layers
5
+ module Infrastructure
6
+ class CommandRunner
7
+ include SmartProperties
8
+
9
+ property! :ctx, accepts: ShopifyCli::Context
10
+
11
+ def call(cmd)
12
+ out, status = ctx.capture2e(cmd)
13
+ raise Errors::SystemCallFailureError.new(out: out.chomp, cmd: cmd) unless status.success?
14
+ out
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -5,8 +5,6 @@ module Script
5
5
  module Infrastructure
6
6
  module Errors
7
7
  class AppNotInstalledError < ScriptProjectError; end
8
- class AppScriptNotPushedError < ScriptProjectError; end
9
- class AppScriptUndefinedError < ScriptProjectError; end
10
8
  class BuildError < ScriptProjectError; end
11
9
  class ConfigUiSyntaxError < ScriptProjectError; end
12
10
 
@@ -19,6 +17,15 @@ module Script
19
17
  end
20
18
  end
21
19
 
20
+ class ConfigUiInvalidInputModeError < ScriptProjectError
21
+ attr_reader :filename, :valid_input_modes
22
+ def initialize(filename, valid_input_modes)
23
+ super()
24
+ @filename = filename
25
+ @valid_input_modes = valid_input_modes
26
+ end
27
+ end
28
+
22
29
  class ConfigUiFieldsMissingKeysError < ScriptProjectError
23
30
  attr_reader :filename, :missing_keys
24
31
  def initialize(filename, missing_keys)
@@ -28,9 +35,29 @@ module Script
28
35
  end
29
36
  end
30
37
 
38
+ class ConfigUiFieldsInvalidTypeError < ScriptProjectError
39
+ attr_reader :filename, :valid_types
40
+ def initialize(filename, valid_types)
41
+ super()
42
+ @filename = filename
43
+ @valid_types = valid_types
44
+ end
45
+ end
46
+
31
47
  class DependencyInstallError < ScriptProjectError; end
48
+ class DeprecatedEPError < ScriptProjectError; end
32
49
  class EmptyResponseError < ScriptProjectError; end
33
50
  class ForbiddenError < ScriptProjectError; end
51
+ class InvalidContextError < ScriptProjectError; end
52
+
53
+ class InvalidLanguageError < ScriptProjectError
54
+ attr_reader :language, :extension_point_type
55
+ def initialize(language, extension_point_type)
56
+ super()
57
+ @language = language
58
+ @extension_point_type = extension_point_type
59
+ end
60
+ end
34
61
 
35
62
  class GraphqlError < ScriptProjectError
36
63
  attr_reader :errors
@@ -42,33 +69,26 @@ module Script
42
69
 
43
70
  class ProjectCreatorNotFoundError < ScriptProjectError; end
44
71
 
45
- class ScriptRepushError < ScriptProjectError
46
- attr_reader :api_key
47
- def initialize(api_key)
72
+ class SystemCallFailureError < ScriptProjectError
73
+ attr_reader :out, :cmd
74
+ def initialize(out:, cmd:)
48
75
  super()
49
- @api_key = api_key
76
+ @out = out
77
+ @cmd = cmd
50
78
  end
51
79
  end
52
80
 
53
- class ScriptServiceUserError < ScriptProjectError
54
- def initialize(query_name, errors)
55
- super("Failed performing #{query_name}. Errors: #{errors}.")
81
+ class ScriptRepushError < ScriptProjectError
82
+ attr_reader :uuid
83
+ def initialize(uuid)
84
+ super()
85
+ @uuid = uuid
56
86
  end
57
87
  end
58
88
 
89
+ class ScriptProjectAlreadyExistsError < ScriptProjectError; end
59
90
  class ShopAuthenticationError < ScriptProjectError; end
60
- class ShopScriptConflictError < ScriptProjectError; end
61
- class ShopScriptUndefinedError < ScriptProjectError; end
62
91
  class TaskRunnerNotFoundError < ScriptProjectError; end
63
-
64
- class PackagesOutdatedError < ScriptProjectError
65
- attr_reader :outdated_packages
66
- def initialize(outdated_packages)
67
- super("EP packages are outdated and need to be updated: #{outdated_packages.join(", ")}")
68
- @outdated_packages = outdated_packages
69
- end
70
- end
71
-
72
92
  class BuildScriptNotFoundError < ScriptProjectError; end
73
93
  class InvalidBuildScriptError < ScriptProjectError; end
74
94
 
@@ -7,37 +7,36 @@ module Script
7
7
  include SmartProperties
8
8
  property! :ctx, accepts: ShopifyCli::Context
9
9
 
10
- def create_push_package(script_project:, script_content:, compiled_type:, metadata:, config_ui:)
11
- build_file_path = file_path(script_project.script_name, compiled_type)
10
+ def create_push_package(script_project:, script_content:, compiled_type:, metadata:)
11
+ build_file_path = file_path(script_project.id, compiled_type)
12
12
  write_to_path(build_file_path, script_content)
13
13
 
14
14
  Domain::PushPackage.new(
15
15
  id: build_file_path,
16
+ uuid: script_project.uuid,
16
17
  extension_point_type: script_project.extension_point_type,
17
18
  script_name: script_project.script_name,
18
- description: script_project.description,
19
19
  script_content: script_content,
20
20
  compiled_type: compiled_type,
21
21
  metadata: metadata,
22
- config_ui: config_ui,
22
+ config_ui: script_project.config_ui,
23
23
  )
24
24
  end
25
25
 
26
- def get_push_package(script_project:, compiled_type:, metadata:, config_ui:)
27
- build_file_path = file_path(script_project.script_name, compiled_type)
26
+ def get_push_package(script_project:, compiled_type:, metadata:)
27
+ build_file_path = file_path(script_project.id, compiled_type)
28
28
  raise Domain::PushPackageNotFoundError unless ctx.file_exist?(build_file_path)
29
29
 
30
- script_content = File.read(build_file_path)
31
-
30
+ script_content = ctx.binread(build_file_path)
32
31
  Domain::PushPackage.new(
33
32
  id: build_file_path,
33
+ uuid: script_project.uuid,
34
34
  extension_point_type: script_project.extension_point_type,
35
35
  script_name: script_project.script_name,
36
- description: script_project.description,
37
36
  script_content: script_content,
38
37
  compiled_type: compiled_type,
39
38
  metadata: metadata,
40
- config_ui: config_ui,
39
+ config_ui: script_project.config_ui,
41
40
  )
42
41
  end
43
42
 
@@ -45,11 +44,11 @@ module Script
45
44
 
46
45
  def write_to_path(path, content)
47
46
  ctx.mkdir_p(File.dirname(path))
48
- ctx.write(path, content)
47
+ ctx.binwrite(path, content)
49
48
  end
50
49
 
51
- def file_path(script_name, compiled_type)
52
- "#{ScriptProject.current.directory}/build/#{script_name}.#{compiled_type}"
50
+ def file_path(path_to_script, compiled_type)
51
+ "#{path_to_script}/build/script.#{compiled_type}"
53
52
  end
54
53
  end
55
54
  end
@@ -27,28 +27,27 @@ module Script
27
27
 
28
28
  private
29
29
 
30
+ def command_runner
31
+ @command_runner ||= CommandRunner.new(ctx: ctx)
32
+ end
33
+
30
34
  def git_init
31
- out, status = ctx.capture2e("git init")
32
- raise Domain::Errors::ServiceFailureError, out unless status.success?
35
+ command_runner.call("git init")
33
36
  end
34
37
 
35
38
  def setup_remote
36
39
  repo = extension_point.sdks.rust.package
37
- out, status = ctx.capture2e("git remote add -f origin #{repo}")
38
- raise Domain::Errors::ServiceFailureError, out unless status.success?
40
+ command_runner.call("git remote add -f origin #{repo}")
39
41
  end
40
42
 
41
43
  def setup_sparse_checkout
42
44
  type = extension_point.type
43
- out, status = ctx.capture2e("git config core.sparsecheckout true")
44
- raise Domain::Errors::ServiceFailureError, out unless status.success?
45
- out, status = ctx.capture2e("echo #{type}/#{SAMPLE_PATH} >> .git/info/sparse-checkout")
46
- raise Domain::Errors::ServiceFailureError, out unless status.success?
45
+ command_runner.call("git config core.sparsecheckout true")
46
+ command_runner.call("echo #{type}/#{SAMPLE_PATH} >> .git/info/sparse-checkout")
47
47
  end
48
48
 
49
49
  def pull
50
- out, status = ctx.capture2e("git pull origin #{ORIGIN_BRANCH}")
51
- raise Domain::Errors::ServiceFailureError, out unless status.success?
50
+ command_runner.call("git pull origin #{ORIGIN_BRANCH}")
52
51
  end
53
52
 
54
53
  def clean
@@ -3,14 +3,14 @@ module Script
3
3
  module Layers
4
4
  module Infrastructure
5
5
  class RustTaskRunner
6
- attr_reader :ctx, :script_name
6
+ attr_reader :ctx
7
7
 
8
8
  BUILD_TARGET = "wasm32-unknown-unknown"
9
9
  METADATA_FILE = "build/metadata.json"
10
+ CARGO_BUILD_CMD = "cargo build --target=#{BUILD_TARGET} --release"
10
11
 
11
- def initialize(ctx, script_name)
12
+ def initialize(ctx, _)
12
13
  @ctx = ctx
13
- @script_name = script_name
14
14
  end
15
15
 
16
16
  def dependencies_installed?
@@ -42,16 +42,15 @@ module Script
42
42
  private
43
43
 
44
44
  def compile
45
- out, status = ctx.capture2e("cargo build --target=#{BUILD_TARGET} --release")
46
- raise Domain::Errors::ServiceFailureError, out unless status.success?
45
+ CommandRunner.new(ctx: ctx).call(CARGO_BUILD_CMD)
47
46
  end
48
47
 
49
48
  def bytecode
50
- binary_name = "#{script_name}.wasm"
49
+ binary_name = "script.wasm"
51
50
  binary_path = "target/#{BUILD_TARGET}/release/#{binary_name}"
52
51
  raise Errors::WebAssemblyBinaryNotFoundError unless ctx.file_exist?(binary_path)
53
52
 
54
- File.read(binary_path)
53
+ ctx.binread(binary_path)
55
54
  end
56
55
  end
57
56
  end
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Script
4
+ module Layers
5
+ module Infrastructure
6
+ class ScriptProjectRepository
7
+ include SmartProperties
8
+ property! :ctx, accepts: ShopifyCli::Context
9
+
10
+ DEFAULT_CONFIG_UI_FILENAME = "config-ui.yml"
11
+ MUTABLE_ENV_VALUES = %i(uuid)
12
+
13
+ def create(script_name:, extension_point_type:, language:, no_config_ui:)
14
+ validate_metadata!(extension_point_type, language)
15
+
16
+ config_ui_file = nil
17
+ optional_identifiers = {}
18
+ optional_identifiers.merge!(config_ui_file: DEFAULT_CONFIG_UI_FILENAME) unless no_config_ui
19
+
20
+ ShopifyCli::Project.write(
21
+ ctx,
22
+ project_type: :script,
23
+ organization_id: nil,
24
+ extension_point_type: extension_point_type,
25
+ script_name: script_name,
26
+ language: language,
27
+ **optional_identifiers
28
+ )
29
+
30
+ Domain::ScriptProject.new(
31
+ id: ctx.root,
32
+ env: project.env,
33
+ script_name: script_name,
34
+ extension_point_type: extension_point_type,
35
+ language: language,
36
+ config_ui: config_ui_file
37
+ )
38
+ end
39
+
40
+ def get
41
+ validate_metadata!(extension_point_type, language)
42
+
43
+ config_ui = ConfigUiRepository.new(ctx: ctx).get(config_ui_file)
44
+
45
+ Domain::ScriptProject.new(
46
+ id: project.directory,
47
+ env: project.env,
48
+ script_name: script_name,
49
+ extension_point_type: extension_point_type,
50
+ language: language,
51
+ config_ui: config_ui
52
+ )
53
+ end
54
+
55
+ def update_env(**args)
56
+ capture_io do
57
+ args.slice(*MUTABLE_ENV_VALUES).each do |key, value|
58
+ project.env.extra[key.to_s.upcase] = value
59
+ project.env.update(ctx, :extra, project.env.extra)
60
+ end
61
+ end
62
+
63
+ Domain::ScriptProject.new(
64
+ id: ctx.root,
65
+ env: project.env,
66
+ script_name: script_name,
67
+ extension_point_type: extension_point_type,
68
+ language: language,
69
+ config_ui: ConfigUiRepository.new(ctx: ctx).get(config_ui_file),
70
+ )
71
+ end
72
+
73
+ def create_env(api_key:, secret:, uuid:)
74
+ ShopifyCli::Resources::EnvFile.new(
75
+ api_key: api_key,
76
+ secret: secret,
77
+ extra: {
78
+ Domain::ScriptProject::UUID_ENV_KEY => uuid,
79
+ }
80
+ ).write(ctx)
81
+
82
+ Domain::ScriptProject.new(
83
+ id: ctx.root,
84
+ env: project.env,
85
+ script_name: script_name,
86
+ extension_point_type: extension_point_type,
87
+ language: language,
88
+ config_ui: ConfigUiRepository.new(ctx: ctx).get(config_ui_file),
89
+ )
90
+ end
91
+
92
+ private
93
+
94
+ def capture_io(&block)
95
+ CLI::UI::StdoutRouter::Capture.new(&block).run
96
+ end
97
+
98
+ def extension_point_type
99
+ project_config_value!("extension_point_type")
100
+ end
101
+
102
+ def script_name
103
+ project_config_value!("script_name")
104
+ end
105
+
106
+ def config_ui_file
107
+ project_config_value("config_ui_file")
108
+ end
109
+
110
+ def language
111
+ project_config_value("language")&.downcase || default_language
112
+ end
113
+
114
+ def project_config_value(key)
115
+ return nil unless project.config.key?(key)
116
+ project.config[key]
117
+ end
118
+
119
+ def project_config_value!(key)
120
+ raise Errors::InvalidContextError, key unless project.config.key?(key)
121
+ project.config[key]
122
+ end
123
+
124
+ def project
125
+ @project ||= ShopifyCli::Project.current(force_reload: true)
126
+ end
127
+
128
+ def default_language
129
+ Domain::ExtensionPoint::ExtensionPointAssemblyScriptSDK.language
130
+ end
131
+
132
+ def validate_metadata!(extension_point_type, language)
133
+ if Application::ExtensionPoints.deprecated_types.include?(extension_point_type)
134
+ raise Errors::DeprecatedEPError, extension_point_type
135
+ elsif !Application::ExtensionPoints.supported_language?(type: extension_point_type, language: language)
136
+ raise Errors::InvalidLanguageError.new(language, extension_point_type)
137
+ end
138
+ end
139
+
140
+ class ConfigUiRepository
141
+ include SmartProperties
142
+ property! :ctx, accepts: ShopifyCli::Context
143
+
144
+ def get(filename)
145
+ return nil unless filename
146
+
147
+ path = File.join(ctx.root, filename)
148
+ raise Domain::Errors::MissingSpecifiedConfigUiDefinitionError, filename unless File.exist?(path)
149
+
150
+ content = File.read(path)
151
+ raise Domain::Errors::InvalidConfigUiDefinitionError, filename unless valid_config_ui?(content)
152
+
153
+ Domain::ConfigUi.new(
154
+ filename: filename,
155
+ content: content,
156
+ )
157
+ end
158
+
159
+ private
160
+
161
+ def valid_config_ui?(raw_yaml)
162
+ require "yaml" # takes 20ms, so deferred as late as possible.
163
+ YAML.safe_load(raw_yaml)
164
+ true
165
+ rescue Psych::SyntaxError
166
+ false
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end