Skip to content

Commit

Permalink
Add support for stringArray parameters + operationContextParams (#3050)
Browse files Browse the repository at this point in the history
  • Loading branch information
alextwoods authored Jun 20, 2024
1 parent d517281 commit fe91026
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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'
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
2 changes: 2 additions & 0 deletions gems/aws-sdk-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
6 changes: 5 additions & 1 deletion gems/aws-sdk-core/lib/aws-sdk-core/endpoints/matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit fe91026

Please sign in to comment.