Skip to content

Commit fe91026

Browse files
authored
Add support for stringArray parameters + operationContextParams (#3050)
1 parent d517281 commit fe91026

File tree

8 files changed

+291
-13
lines changed

8 files changed

+291
-13
lines changed

build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/views/endpoint_parameters_class.rb

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,20 @@ def initialize(name, definition={})
5959
# @return [Boolean]
6060
attr_reader :required
6161

62-
# @return [String,Boolean]
63-
attr_reader :default
62+
# @return [String,Boolean,Array]
63+
def default
64+
case @default
65+
when String
66+
"\"#{@default}\""
67+
else
68+
@default.to_s
69+
end
70+
end
6471

6572
def default?
6673
!@default.nil?
6774
end
6875

69-
def boolean_default?
70-
default? && (@default == true || @default == false)
71-
end
72-
7376
def underscore_name
7477
Underscore.underscore(name)
7578
end

build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/views/endpoints_module.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def endpoint_parameters_for_operation(operation)
9494
# Most to least
9595
# staticContextParams
9696
# contextParam
97+
# operationContextParams
9798
# clientContextParams
9899
# Built-In Bindings
99100
# Built-in binding default values
@@ -104,6 +105,9 @@ def endpoint_parameter_value(operation, param_name, param_data)
104105
value, source = [
105106
context_param_value(operation, param_name), 'contextParam'
106107
] unless value
108+
value, source = [
109+
operation_context_param_value(operation, param_name), 'operationContextParam'
110+
] unless value
107111
value, source = [
108112
client_context_param_value(param_name, param_data),
109113
'clientContextParam'
@@ -168,6 +172,16 @@ def context_param_value(operation, param_name)
168172
end
169173
end
170174

175+
def operation_context_param_value(operation, param_name)
176+
return nil unless operation['input']
177+
178+
binding = operation.fetch('operationContextParams', {})[param_name]
179+
180+
return nil unless binding
181+
182+
"JMESPath.search(\"#{Underscore.underscore_jmespath(binding['path'])}\", context.params)"
183+
end
184+
171185
def static_context_param(operation, param_name)
172186
operation.fetch('staticContextParams', {})
173187
.fetch(param_name, {}).fetch('value', nil)
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
{
2+
"metadata": {
3+
"endpointPrefix": "svcname",
4+
"serviceId": "sample_svc",
5+
"jsonVersion":"1.1",
6+
"protocol":"json"
7+
},
8+
"operations": {
9+
"NoBindingsOperation": {
10+
"input": { "shape": "EmptyOperationRequest" },
11+
"http": {
12+
"method": "POST",
13+
"requestUri": "/"
14+
}
15+
},
16+
"EmptyStaticContextOperation": {
17+
"input": { "shape": "EmptyOperationRequest" },
18+
"staticContextParams": {
19+
"stringArrayParam": {
20+
"value": []
21+
}
22+
},
23+
"http": {
24+
"method": "POST",
25+
"requestUri": "/"
26+
}
27+
},
28+
"StaticContextOperation": {
29+
"input": { "shape": "EmptyOperationRequest" },
30+
"staticContextParams": {
31+
"stringArrayParam": {
32+
"value": ["staticValue1"]
33+
}
34+
},
35+
"http": {
36+
"method": "POST",
37+
"requestUri": "/"
38+
}
39+
},
40+
"ListOfObjectsOperation": {
41+
"input": { "shape": "ListOfObjectsOperationRequest" },
42+
"operationContextParams": {
43+
"stringArrayParam": {
44+
"path": "nested.listOfObjects[*].key"
45+
}
46+
},
47+
"http": {
48+
"method": "POST",
49+
"requestUri": "/"
50+
}
51+
},
52+
"MapOperation": {
53+
"input": { "shape": "MapOperationRequest" },
54+
"operationContextParams": {
55+
"stringArrayParam": {
56+
"path": "keys(map)"
57+
}
58+
},
59+
"http": {
60+
"method": "POST",
61+
"requestUri": "/"
62+
}
63+
}
64+
},
65+
"shapes": {
66+
"EmptyOperationRequest": {
67+
"type": "structure",
68+
"members": {}
69+
},
70+
"ListOfObjectsOperationRequest": {
71+
"type": "structure",
72+
"members": {
73+
"nested":{"shape":"Nested"}
74+
}
75+
},
76+
"Nested": {
77+
"type": "structure",
78+
"members": {
79+
"listOfObjects":{"shape":"ListOfObjects"}
80+
}
81+
},
82+
"ListOfObjects": {
83+
"type": "list",
84+
"member":{"shape":"ObjectMember"}
85+
},
86+
"ObjectMember": {
87+
"type": "structure",
88+
"members": {
89+
"key":{"shape":"String"}
90+
}
91+
},
92+
"MapOperationRequest": {
93+
"type": "structure",
94+
"members": {
95+
"map":{"shape":"Map"}
96+
}
97+
},
98+
"Map":{
99+
"type":"map",
100+
"key":{"shape":"String"},
101+
"value":{"shape":"String"}
102+
},
103+
"String":{
104+
"type":"string"
105+
}
106+
}
107+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"version": "1.0",
3+
"parameters": {
4+
"stringArrayParam": {
5+
"type": "stringArray",
6+
"required": true,
7+
"default": ["defaultValue1", "defaultValue2"],
8+
"documentation": "docs"
9+
}
10+
},
11+
"rules": [
12+
{
13+
"documentation": "Template first array value into URI if set",
14+
"conditions": [
15+
{
16+
"fn": "getAttr",
17+
"argv": [
18+
{
19+
"ref": "stringArrayParam"
20+
},
21+
"[0]"
22+
],
23+
"assign": "arrayValue"
24+
}
25+
],
26+
"endpoint": {
27+
"url": "https://example.com/{arrayValue}"
28+
},
29+
"type": "endpoint"
30+
},
31+
{
32+
"conditions": [],
33+
"documentation": "error fallthrough",
34+
"error": "no array values set",
35+
"type": "error"
36+
}
37+
]
38+
}

build_tools/aws-sdk-code-generator/spec/interfaces/plugins/endpoints_spec.rb

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,5 +151,120 @@
151151
end
152152
end
153153

154+
context 'String Array Parameters' do
155+
before(:all) do
156+
SpecHelper.generate_service(['EndpointsStringArray'], multiple_files: false)
157+
end
158+
159+
let(:client) do
160+
EndpointsStringArray::Client.new(
161+
stub_responses: true,
162+
)
163+
end
164+
165+
let(:provider) do
166+
EndpointsStringArray::EndpointProvider.new
167+
end
168+
169+
context 'Default array values used' do
170+
let(:expected) do
171+
{"endpoint"=>{"url"=>"https://example.com/defaultValue1"}}
172+
end
173+
174+
it 'produces the expected output from the EndpointProvider' do
175+
params = EndpointsStringArray::EndpointParameters.new(**{})
176+
endpoint = provider.resolve_endpoint(params)
177+
expect(endpoint.url).to eq(expected['endpoint']['url'])
178+
expect(endpoint.headers).to eq(expected['endpoint']['headers'] || {})
179+
expect(endpoint.properties).to eq(expected['endpoint']['properties'] || {})
180+
end
181+
182+
it 'produces the correct output from the client when calling no_bindings_operation' do
183+
resp = client.no_bindings_operation
184+
expected_uri = URI.parse(expected['endpoint']['url'])
185+
expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.host)
186+
expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.scheme)
187+
expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.path)
188+
end
189+
end
190+
191+
context 'Empty array' do
192+
let(:expected) do
193+
{"error"=>"no array values set"}
194+
end
195+
196+
it 'produces the expected output from the EndpointProvider' do
197+
params = EndpointsStringArray::EndpointParameters.new(**{:string_array_param=>[]})
198+
expect do
199+
provider.resolve_endpoint(params)
200+
end.to raise_error(ArgumentError, expected['error'])
201+
end
202+
203+
it 'produces the correct output from the client when calling empty_static_context_operation' do
204+
expect do
205+
client.empty_static_context_operation
206+
end.to raise_error(ArgumentError, expected['error'])
207+
end
208+
end
209+
210+
context 'Static value' do
211+
let(:expected) do
212+
{"endpoint"=>{"url"=>"https://example.com/staticValue1"}}
213+
end
214+
215+
it 'produces the expected output from the EndpointProvider' do
216+
params = EndpointsStringArray::EndpointParameters.new(**{:string_array_param=>["staticValue1"]})
217+
endpoint = provider.resolve_endpoint(params)
218+
expect(endpoint.url).to eq(expected['endpoint']['url'])
219+
expect(endpoint.headers).to eq(expected['endpoint']['headers'] || {})
220+
expect(endpoint.properties).to eq(expected['endpoint']['properties'] || {})
221+
end
222+
223+
it 'produces the correct output from the client when calling static_context_operation' do
224+
resp = client.static_context_operation
225+
expected_uri = URI.parse(expected['endpoint']['url'])
226+
expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.host)
227+
expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.scheme)
228+
expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.path)
229+
end
230+
end
231+
232+
context 'bound value from input' do
233+
let(:expected) do
234+
{"endpoint"=>{"url"=>"https://example.com/key1"}}
235+
end
236+
237+
it 'produces the expected output from the EndpointProvider' do
238+
params = EndpointsStringArray::EndpointParameters.new(**{:string_array_param=>["key1"]})
239+
endpoint = provider.resolve_endpoint(params)
240+
expect(endpoint.url).to eq(expected['endpoint']['url'])
241+
expect(endpoint.headers).to eq(expected['endpoint']['headers'] || {})
242+
expect(endpoint.properties).to eq(expected['endpoint']['properties'] || {})
243+
end
244+
245+
it 'produces the correct output from the client when calling list_of_objects_operation' do
246+
resp = client.list_of_objects_operation(
247+
nested: {:list_of_objects=>[{:key=>"key1"}]},
248+
)
249+
expected_uri = URI.parse(expected['endpoint']['url'])
250+
expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.host)
251+
expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.scheme)
252+
expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.path)
253+
end
254+
255+
it 'produces the correct output from the client when calling map_operation' do
256+
resp = client.map_operation(
257+
map: {:key1=>"value1"},
258+
)
259+
expected_uri = URI.parse(expected['endpoint']['url'])
260+
expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.host)
261+
expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.scheme)
262+
expect(resp.context.http_request.endpoint.to_s).to include(expected_uri.path)
263+
end
264+
end
265+
266+
267+
end
268+
154269
end
155270
end

build_tools/aws-sdk-code-generator/templates/endpoint_parameters_class.mustache

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,7 @@ module {{module_name}}
3232
{{#parameters}}
3333
self[:{{underscore_name}}] = options[:{{underscore_name}}]
3434
{{#default?}}
35-
{{#boolean_default?}}
36-
self[:{{underscore_name}}] = {{default}} if self[:{{underscore_name}}].nil?
37-
{{/boolean_default?}}
38-
{{^boolean_default?}}
39-
self[:{{underscore_name}}] ||= '{{default}}' if self[:{{underscore_name}}].nil?
40-
{{/boolean_default?}}
35+
self[:{{underscore_name}}] = {{{default}}} if self[:{{underscore_name}}].nil?
4136
{{/default?}}
4237
{{#required}}
4338
if self[:{{underscore_name}}].nil?

gems/aws-sdk-core/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Unreleased Changes
22
------------------
33

4+
* Issue - fix issue in Endpoint `attr` matcher when path is only an array index.
5+
46
* Issue - Fix trailing slash in endpoint URLs for rest-json and rest-xml services.
57

68
3.197.1 (2024-06-19)

gems/aws-sdk-core/lib/aws-sdk-core/endpoints/matchers.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ def self.attr(value, path)
2828

2929
val = if (index = parts.first[BRACKET_REGEX, 1])
3030
# remove brackets and index from part before indexing
31-
value[parts.first.gsub(BRACKET_REGEX, '')][index.to_i]
31+
if (base = parts.first.gsub(BRACKET_REGEX, '')) && !base.empty?
32+
value[base][index.to_i]
33+
else
34+
value[index.to_i]
35+
end
3236
else
3337
value[parts.first]
3438
end

0 commit comments

Comments
 (0)