From fe9102698b27fc2a8aa4d91f05ca1d9bf7a03392 Mon Sep 17 00:00:00 2001 From: Alex Woods Date: Thu, 20 Jun 2024 10:32:44 -0700 Subject: [PATCH] Add support for stringArray parameters + operationContextParams (#3050) --- .../views/endpoint_parameters_class.rb | 15 ++- .../views/endpoints_module.rb | 14 +++ .../endpoints_string_array/api.json | 107 ++++++++++++++++ .../endpoint-rule-set.json | 38 ++++++ .../spec/interfaces/plugins/endpoints_spec.rb | 115 ++++++++++++++++++ .../endpoint_parameters_class.mustache | 7 +- gems/aws-sdk-core/CHANGELOG.md | 2 + .../lib/aws-sdk-core/endpoints/matchers.rb | 6 +- 8 files changed, 291 insertions(+), 13 deletions(-) create mode 100644 build_tools/aws-sdk-code-generator/spec/fixtures/interfaces/endpoints_string_array/api.json create mode 100644 build_tools/aws-sdk-code-generator/spec/fixtures/interfaces/endpoints_string_array/endpoint-rule-set.json diff --git a/build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/views/endpoint_parameters_class.rb b/build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/views/endpoint_parameters_class.rb index c17c79ba86a..e0afcbb2786 100644 --- a/build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/views/endpoint_parameters_class.rb +++ b/build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/views/endpoint_parameters_class.rb @@ -59,17 +59,20 @@ def initialize(name, definition={}) # @return [Boolean] attr_reader :required - # @return [String,Boolean] - attr_reader :default + # @return [String,Boolean,Array] + def default + case @default + when String + "\"#{@default}\"" + else + @default.to_s + end + end def default? !@default.nil? end - def boolean_default? - default? && (@default == true || @default == false) - end - def underscore_name Underscore.underscore(name) end diff --git a/build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/views/endpoints_module.rb b/build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/views/endpoints_module.rb index cd4a59a6d04..1dfab1d7550 100644 --- a/build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/views/endpoints_module.rb +++ b/build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/views/endpoints_module.rb @@ -94,6 +94,7 @@ def endpoint_parameters_for_operation(operation) # Most to least # staticContextParams # contextParam + # operationContextParams # clientContextParams # Built-In Bindings # Built-in binding default values @@ -104,6 +105,9 @@ def endpoint_parameter_value(operation, param_name, param_data) value, source = [ context_param_value(operation, param_name), 'contextParam' ] unless value + value, source = [ + operation_context_param_value(operation, param_name), 'operationContextParam' + ] unless value value, source = [ client_context_param_value(param_name, param_data), 'clientContextParam' @@ -168,6 +172,16 @@ def context_param_value(operation, param_name) end end + def operation_context_param_value(operation, param_name) + return nil unless operation['input'] + + binding = operation.fetch('operationContextParams', {})[param_name] + + return nil unless binding + + "JMESPath.search(\"#{Underscore.underscore_jmespath(binding['path'])}\", context.params)" + end + def static_context_param(operation, param_name) operation.fetch('staticContextParams', {}) .fetch(param_name, {}).fetch('value', nil) diff --git a/build_tools/aws-sdk-code-generator/spec/fixtures/interfaces/endpoints_string_array/api.json b/build_tools/aws-sdk-code-generator/spec/fixtures/interfaces/endpoints_string_array/api.json new file mode 100644 index 00000000000..74dadae687f --- /dev/null +++ b/build_tools/aws-sdk-code-generator/spec/fixtures/interfaces/endpoints_string_array/api.json @@ -0,0 +1,107 @@ +{ + "metadata": { + "endpointPrefix": "svcname", + "serviceId": "sample_svc", + "jsonVersion":"1.1", + "protocol":"json" + }, + "operations": { + "NoBindingsOperation": { + "input": { "shape": "EmptyOperationRequest" }, + "http": { + "method": "POST", + "requestUri": "/" + } + }, + "EmptyStaticContextOperation": { + "input": { "shape": "EmptyOperationRequest" }, + "staticContextParams": { + "stringArrayParam": { + "value": [] + } + }, + "http": { + "method": "POST", + "requestUri": "/" + } + }, + "StaticContextOperation": { + "input": { "shape": "EmptyOperationRequest" }, + "staticContextParams": { + "stringArrayParam": { + "value": ["staticValue1"] + } + }, + "http": { + "method": "POST", + "requestUri": "/" + } + }, + "ListOfObjectsOperation": { + "input": { "shape": "ListOfObjectsOperationRequest" }, + "operationContextParams": { + "stringArrayParam": { + "path": "nested.listOfObjects[*].key" + } + }, + "http": { + "method": "POST", + "requestUri": "/" + } + }, + "MapOperation": { + "input": { "shape": "MapOperationRequest" }, + "operationContextParams": { + "stringArrayParam": { + "path": "keys(map)" + } + }, + "http": { + "method": "POST", + "requestUri": "/" + } + } + }, + "shapes": { + "EmptyOperationRequest": { + "type": "structure", + "members": {} + }, + "ListOfObjectsOperationRequest": { + "type": "structure", + "members": { + "nested":{"shape":"Nested"} + } + }, + "Nested": { + "type": "structure", + "members": { + "listOfObjects":{"shape":"ListOfObjects"} + } + }, + "ListOfObjects": { + "type": "list", + "member":{"shape":"ObjectMember"} + }, + "ObjectMember": { + "type": "structure", + "members": { + "key":{"shape":"String"} + } + }, + "MapOperationRequest": { + "type": "structure", + "members": { + "map":{"shape":"Map"} + } + }, + "Map":{ + "type":"map", + "key":{"shape":"String"}, + "value":{"shape":"String"} + }, + "String":{ + "type":"string" + } + } +} diff --git a/build_tools/aws-sdk-code-generator/spec/fixtures/interfaces/endpoints_string_array/endpoint-rule-set.json b/build_tools/aws-sdk-code-generator/spec/fixtures/interfaces/endpoints_string_array/endpoint-rule-set.json new file mode 100644 index 00000000000..c3cab529249 --- /dev/null +++ b/build_tools/aws-sdk-code-generator/spec/fixtures/interfaces/endpoints_string_array/endpoint-rule-set.json @@ -0,0 +1,38 @@ +{ + "version": "1.0", + "parameters": { + "stringArrayParam": { + "type": "stringArray", + "required": true, + "default": ["defaultValue1", "defaultValue2"], + "documentation": "docs" + } + }, + "rules": [ + { + "documentation": "Template first array value into URI if set", + "conditions": [ + { + "fn": "getAttr", + "argv": [ + { + "ref": "stringArrayParam" + }, + "[0]" + ], + "assign": "arrayValue" + } + ], + "endpoint": { + "url": "https://example.com/{arrayValue}" + }, + "type": "endpoint" + }, + { + "conditions": [], + "documentation": "error fallthrough", + "error": "no array values set", + "type": "error" + } + ] +} \ No newline at end of file diff --git a/build_tools/aws-sdk-code-generator/spec/interfaces/plugins/endpoints_spec.rb b/build_tools/aws-sdk-code-generator/spec/interfaces/plugins/endpoints_spec.rb index 381b96965ad..a0ed2113ddc 100644 --- a/build_tools/aws-sdk-code-generator/spec/interfaces/plugins/endpoints_spec.rb +++ b/build_tools/aws-sdk-code-generator/spec/interfaces/plugins/endpoints_spec.rb @@ -151,5 +151,120 @@ end end + context 'String Array Parameters' do + before(:all) do + SpecHelper.generate_service(['EndpointsStringArray'], multiple_files: false) + end + + let(:client) do + EndpointsStringArray::Client.new( + stub_responses: true, + ) + end + + let(:provider) do + EndpointsStringArray::EndpointProvider.new + end + + context 'Default array values used' do + let(:expected) do + {"endpoint"=>{"url"=>"https://example.com/defaultValue1"}} + end + + it 'produces the expected output from the EndpointProvider' do + params = EndpointsStringArray::EndpointParameters.new(**{}) + endpoint = provider.resolve_endpoint(params) + expect(endpoint.url).to eq(expected['endpoint']['url']) + expect(endpoint.headers).to eq(expected['endpoint']['headers'] || {}) + expect(endpoint.properties).to eq(expected['endpoint']['properties'] || {}) + end + + it 'produces the correct output from the client when calling no_bindings_operation' do + resp = client.no_bindings_operation + expected_uri = URI.parse(expected['endpoint']['url']) + expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.host) + expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.scheme) + expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.path) + end + end + + context 'Empty array' do + let(:expected) do + {"error"=>"no array values set"} + end + + it 'produces the expected output from the EndpointProvider' do + params = EndpointsStringArray::EndpointParameters.new(**{:string_array_param=>[]}) + expect do + provider.resolve_endpoint(params) + end.to raise_error(ArgumentError, expected['error']) + end + + it 'produces the correct output from the client when calling empty_static_context_operation' do + expect do + client.empty_static_context_operation + end.to raise_error(ArgumentError, expected['error']) + end + end + + context 'Static value' do + let(:expected) do + {"endpoint"=>{"url"=>"https://example.com/staticValue1"}} + end + + it 'produces the expected output from the EndpointProvider' do + params = EndpointsStringArray::EndpointParameters.new(**{:string_array_param=>["staticValue1"]}) + endpoint = provider.resolve_endpoint(params) + expect(endpoint.url).to eq(expected['endpoint']['url']) + expect(endpoint.headers).to eq(expected['endpoint']['headers'] || {}) + expect(endpoint.properties).to eq(expected['endpoint']['properties'] || {}) + end + + it 'produces the correct output from the client when calling static_context_operation' do + resp = client.static_context_operation + expected_uri = URI.parse(expected['endpoint']['url']) + expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.host) + expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.scheme) + expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.path) + end + end + + context 'bound value from input' do + let(:expected) do + {"endpoint"=>{"url"=>"https://example.com/key1"}} + end + + it 'produces the expected output from the EndpointProvider' do + params = EndpointsStringArray::EndpointParameters.new(**{:string_array_param=>["key1"]}) + endpoint = provider.resolve_endpoint(params) + expect(endpoint.url).to eq(expected['endpoint']['url']) + expect(endpoint.headers).to eq(expected['endpoint']['headers'] || {}) + expect(endpoint.properties).to eq(expected['endpoint']['properties'] || {}) + end + + it 'produces the correct output from the client when calling list_of_objects_operation' do + resp = client.list_of_objects_operation( + nested: {:list_of_objects=>[{:key=>"key1"}]}, + ) + expected_uri = URI.parse(expected['endpoint']['url']) + expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.host) + expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.scheme) + expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.path) + end + + it 'produces the correct output from the client when calling map_operation' do + resp = client.map_operation( + map: {:key1=>"value1"}, + ) + expected_uri = URI.parse(expected['endpoint']['url']) + expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.host) + expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.scheme) + expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.path) + end + end + + + end + end end diff --git a/build_tools/aws-sdk-code-generator/templates/endpoint_parameters_class.mustache b/build_tools/aws-sdk-code-generator/templates/endpoint_parameters_class.mustache index e8a180f6bb5..0b9b3278791 100644 --- a/build_tools/aws-sdk-code-generator/templates/endpoint_parameters_class.mustache +++ b/build_tools/aws-sdk-code-generator/templates/endpoint_parameters_class.mustache @@ -32,12 +32,7 @@ module {{module_name}} {{#parameters}} self[:{{underscore_name}}] = options[:{{underscore_name}}] {{#default?}} - {{#boolean_default?}} - self[:{{underscore_name}}] = {{default}} if self[:{{underscore_name}}].nil? - {{/boolean_default?}} - {{^boolean_default?}} - self[:{{underscore_name}}] ||= '{{default}}' if self[:{{underscore_name}}].nil? - {{/boolean_default?}} + self[:{{underscore_name}}] = {{{default}}} if self[:{{underscore_name}}].nil? {{/default?}} {{#required}} if self[:{{underscore_name}}].nil? diff --git a/gems/aws-sdk-core/CHANGELOG.md b/gems/aws-sdk-core/CHANGELOG.md index 3a77880e044..363765a84fe 100644 --- a/gems/aws-sdk-core/CHANGELOG.md +++ b/gems/aws-sdk-core/CHANGELOG.md @@ -1,6 +1,8 @@ Unreleased Changes ------------------ +* Issue - fix issue in Endpoint `attr` matcher when path is only an array index. + * Issue - Fix trailing slash in endpoint URLs for rest-json and rest-xml services. 3.197.1 (2024-06-19) diff --git a/gems/aws-sdk-core/lib/aws-sdk-core/endpoints/matchers.rb b/gems/aws-sdk-core/lib/aws-sdk-core/endpoints/matchers.rb index 8c241bb1394..542b0abb73c 100644 --- a/gems/aws-sdk-core/lib/aws-sdk-core/endpoints/matchers.rb +++ b/gems/aws-sdk-core/lib/aws-sdk-core/endpoints/matchers.rb @@ -28,7 +28,11 @@ def self.attr(value, path) val = if (index = parts.first[BRACKET_REGEX, 1]) # remove brackets and index from part before indexing - value[parts.first.gsub(BRACKET_REGEX, '')][index.to_i] + if (base = parts.first.gsub(BRACKET_REGEX, '')) && !base.empty? + value[base][index.to_i] + else + value[index.to_i] + end else value[parts.first] end