aws-sdk-core 2.0.18 → 2.0.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/apis/CloudFormation.resources.json +55 -20
  3. data/apis/CloudFront.waiters.json +35 -17
  4. data/apis/DynamoDB.waiters.json +27 -17
  5. data/apis/EC2.api.json +5 -0
  6. data/apis/EC2.resources.json +449 -319
  7. data/apis/EC2.waiters.json +389 -139
  8. data/apis/{EMR.waiters2.json → EMR.waiters.json} +0 -0
  9. data/apis/ElasticTranscoder.api.json +34 -3
  10. data/apis/ElasticTranscoder.waiters.json +22 -8
  11. data/apis/Glacier.resources.json +215 -117
  12. data/apis/Glacier.waiters.json +31 -15
  13. data/apis/IAM.resources.json +380 -169
  14. data/apis/{Kinesis.waiters2.json → Kinesis.waiters.json} +0 -0
  15. data/apis/OpsWorks.resources.json +51 -17
  16. data/apis/RDS.waiters.json +87 -26
  17. data/apis/Redshift.waiters.json +54 -29
  18. data/apis/S3.resources.json +404 -143
  19. data/apis/S3.waiters.json +47 -16
  20. data/apis/SES.waiters.json +11 -7
  21. data/apis/SNS.resources.json +67 -33
  22. data/apis/SQS.resources.json +57 -30
  23. data/lib/aws-sdk-core.rb +1 -0
  24. data/lib/aws-sdk-core/api/documenter.rb +4 -3
  25. data/lib/aws-sdk-core/api/service_customizations.rb +0 -6
  26. data/lib/aws-sdk-core/client_waiters.rb +65 -50
  27. data/lib/aws-sdk-core/emr.rb +1 -0
  28. data/lib/aws-sdk-core/errors.rb +4 -0
  29. data/lib/aws-sdk-core/instance_profile_credentials.rb +1 -0
  30. data/lib/aws-sdk-core/kinesis.rb +1 -0
  31. data/lib/aws-sdk-core/paging/pager.rb +5 -1
  32. data/lib/aws-sdk-core/refreshing_credentials.rb +2 -0
  33. data/lib/aws-sdk-core/version.rb +1 -1
  34. data/lib/aws-sdk-core/waiters/errors.rb +50 -12
  35. data/lib/aws-sdk-core/waiters/poller.rb +105 -0
  36. data/lib/aws-sdk-core/waiters/provider.rb +11 -30
  37. data/lib/aws-sdk-core/waiters/waiter.rb +42 -102
  38. metadata +5 -12
  39. data/apis/CloudFront.waiters2.json +0 -47
  40. data/apis/DynamoDB.waiters2.json +0 -35
  41. data/apis/EC2.waiters2.json +0 -341
  42. data/apis/ElasticTranscoder.waiters2.json +0 -30
  43. data/apis/RDS.waiters2.json +0 -97
  44. data/apis/Redshift.waiters2.json +0 -97
  45. data/apis/S3.waiters2.json +0 -63
  46. data/apis/SES.waiters2.json +0 -18
@@ -6,14 +6,26 @@ module Aws
6
6
  RAISE_HANDLER = Seahorse::Client::Plugins::RaiseResponseErrors::Handler
7
7
 
8
8
  # @api private
9
- def initialize(definition = {})
10
- @definition = definition
11
- @interval = definition['interval']
12
- @max_attempts = definition['max_attempts']
9
+ def initialize(options = {})
10
+ @poller = options[:poller]
11
+ @max_attempts = options[:max_attempts]
12
+ @delay = options[:delay]
13
13
  @before_attempt = []
14
14
  @before_wait = []
15
15
  end
16
16
 
17
+ # @api private
18
+ attr_reader :poller
19
+
20
+ # @return [Integer]
21
+ attr_accessor :max_attempts
22
+
23
+ # @return [Float]
24
+ attr_accessor :delay
25
+
26
+ alias interval delay
27
+ alias interval= delay=
28
+
17
29
  # Register a callback that is invoked before every polling attempt.
18
30
  # Yields the number of attempts made so far.
19
31
  #
@@ -71,120 +83,48 @@ module Aws
71
83
  @before_wait << Proc.new
72
84
  end
73
85
 
74
- # @return [Float]
75
- attr_accessor :interval
76
-
77
- # @return [Integer]
78
- attr_accessor :max_attempts
79
-
80
- # @param [Client] client
81
- # @param [Hash] params
82
- def wait(client, params)
83
- attempts = 0
86
+ # @option options [Client] :client
87
+ # @option options [Hash] :params
88
+ def wait(options)
84
89
  catch(:success) do
85
- failure = catch(:failure) do
86
- loop do
87
- trigger_callbacks(@before_attempt, attempts)
88
- attempts += 1
89
- resp = send_request(client, params)
90
- throw :success, resp if successful?(resp)
91
- throw :failure if failure?(resp)
92
- throw :failure, resp.error unless error_ignored?(resp)
93
- throw :failure, too_many(attempts) if attempts == max_attempts
94
- trigger_callbacks(@before_wait, attempts, resp)
95
- sleep(interval)
96
- end
90
+ failure_msg = catch(:failure) do
91
+ poll(options)
92
+ return true
97
93
  end
98
- failure = 'waiter failed' if failure.nil?
99
- raise String === failure ? Errors::WaiterFailed.new(failure) : failure
94
+ raise Errors::WaiterFailed.new(failure_msg || 'waiter failed')
100
95
  end
101
96
  end
102
97
 
103
98
  private
104
99
 
105
- def successful?(response)
106
- acceptor_matches?(:success, response)
107
- end
108
-
109
- def failure?(response)
110
- acceptor_matches?(:failure, response)
111
- end
112
-
113
- def trigger_callbacks(callbacks, *args)
114
- callbacks.each { |block| block.call(*args) }
115
- end
116
-
117
- def send_request(client, params)
118
- req = client.build_request(operation_name, params)
119
- req.handlers.remove(RAISE_HANDLER)
120
- req.send_request
121
- end
122
-
123
- def operation_name
124
- underscore(@definition['operation']).to_sym
125
- end
126
-
127
- def acceptor_matches?(acceptor, resp)
128
- case type(acceptor)
129
- when 'output' then output_matches?(resp, values(acceptor), path(acceptor))
130
- when 'error' then error_matches?(resp.error, values(acceptor))
131
- end
132
- end
100
+ def poll(options)
101
+ n = 0
102
+ loop do
103
+ trigger_before_attempt(n)
133
104
 
134
- def type(acceptor)
135
- @definition["#{acceptor}_type"] || @definition['acceptor_type']
136
- end
105
+ state, resp = @poller.call(options)
106
+ n += 1
137
107
 
138
- def path(acceptor)
139
- underscore(@definition["#{acceptor}_path"] || @definition['acceptor_path'])
140
- end
141
-
142
- def values(acceptor)
143
- values = @definition["#{acceptor}_value"] || @definition['acceptor_value']
144
- values.is_a?(Array) ? values : [values]
145
- end
146
-
147
- def output_matches?(resp, value, path)
148
- if resp.error
149
- false
150
- elsif path
151
- output_value_matches?(value, JMESPath.search(path, resp.data))
152
- else
153
- true
154
- end
155
- end
156
-
157
- def output_value_matches?(expected, results)
158
- if results.is_a?(Array)
159
- results.all? { |result| expected.include?(result) }
160
- else
161
- expected.include?(results)
162
- end
163
- end
108
+ case state
109
+ when :retry
110
+ when :success then return resp
111
+ when :failure then raise Errors::FailureStateError.new(resp)
112
+ when :error then raise Errors::UnexpectedError.new(resp.error)
113
+ end
164
114
 
165
- def error_ignored?(resp)
166
- if resp.error
167
- error_matches?(resp.error, @definition['ignore_errors'] || [])
168
- else
169
- true
170
- end
171
- end
115
+ raise Errors::TooManyAttemptsError.new(n) if n == @max_attempts
172
116
 
173
- def error_matches?(error, errors)
174
- if error
175
- errors.any? { |pattern| error.class.name.match(/#{pattern}$/) }
176
- else
177
- false
117
+ trigger_before_wait(n, resp)
118
+ sleep(@delay)
178
119
  end
179
120
  end
180
121
 
181
- def too_many(attempts)
182
- "too many attempts made, #{attempts} attempts made without " +
183
- "success or failure"
122
+ def trigger_before_attempt(attempts)
123
+ @before_attempt.each { |block| block.call(attempts) }
184
124
  end
185
125
 
186
- def underscore(str)
187
- str.gsub(/\w+/) { |part| Seahorse::Util.underscore(part) } if str
126
+ def trigger_before_wait(attempts, response)
127
+ @before_wait.each { |block| block.call(attempts, response) }
188
128
  end
189
129
 
190
130
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws-sdk-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.18
4
+ version: 2.0.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amazon Web Services
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-08 00:00:00.000000000 Z
11
+ date: 2015-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -81,7 +81,6 @@ files:
81
81
  - apis/CloudFront.api.json
82
82
  - apis/CloudFront.paginators.json
83
83
  - apis/CloudFront.waiters.json
84
- - apis/CloudFront.waiters2.json
85
84
  - apis/CloudHSM.api.json
86
85
  - apis/CloudSearch.api.json
87
86
  - apis/CloudSearch.paginators.json
@@ -105,16 +104,14 @@ files:
105
104
  - apis/DynamoDB.api.json
106
105
  - apis/DynamoDB.paginators.json
107
106
  - apis/DynamoDB.waiters.json
108
- - apis/DynamoDB.waiters2.json
109
107
  - apis/EC2.api.json
110
108
  - apis/EC2.paginators.json
111
109
  - apis/EC2.resources.json
112
110
  - apis/EC2.waiters.json
113
- - apis/EC2.waiters2.json
114
111
  - apis/ECS.api.json
115
112
  - apis/EMR.api.json
116
113
  - apis/EMR.paginators.json
117
- - apis/EMR.waiters2.json
114
+ - apis/EMR.waiters.json
118
115
  - apis/ElastiCache.api.json
119
116
  - apis/ElastiCache.paginators.json
120
117
  - apis/ElasticBeanstalk.api.json
@@ -124,7 +121,6 @@ files:
124
121
  - apis/ElasticTranscoder.api.json
125
122
  - apis/ElasticTranscoder.paginators.json
126
123
  - apis/ElasticTranscoder.waiters.json
127
- - apis/ElasticTranscoder.waiters2.json
128
124
  - apis/Glacier.api.json
129
125
  - apis/Glacier.paginators.json
130
126
  - apis/Glacier.resources.json
@@ -138,7 +134,7 @@ files:
138
134
  - apis/KMS.paginators.json
139
135
  - apis/Kinesis.api.json
140
136
  - apis/Kinesis.paginators.json
141
- - apis/Kinesis.waiters2.json
137
+ - apis/Kinesis.waiters.json
142
138
  - apis/Lambda.api.json
143
139
  - apis/Lambda.paginators.json
144
140
  - apis/OpsWorks.api.json
@@ -147,11 +143,9 @@ files:
147
143
  - apis/RDS.api.json
148
144
  - apis/RDS.paginators.json
149
145
  - apis/RDS.waiters.json
150
- - apis/RDS.waiters2.json
151
146
  - apis/Redshift.api.json
152
147
  - apis/Redshift.paginators.json
153
148
  - apis/Redshift.waiters.json
154
- - apis/Redshift.waiters2.json
155
149
  - apis/Route53.api.json
156
150
  - apis/Route53.paginators.json
157
151
  - apis/Route53Domains.api.json
@@ -159,11 +153,9 @@ files:
159
153
  - apis/S3.paginators.json
160
154
  - apis/S3.resources.json
161
155
  - apis/S3.waiters.json
162
- - apis/S3.waiters2.json
163
156
  - apis/SES.api.json
164
157
  - apis/SES.paginators.json
165
158
  - apis/SES.waiters.json
166
- - apis/SES.waiters2.json
167
159
  - apis/SNS.api.json
168
160
  - apis/SNS.paginators.json
169
161
  - apis/SNS.resources.json
@@ -310,6 +302,7 @@ files:
310
302
  - lib/aws-sdk-core/version.rb
311
303
  - lib/aws-sdk-core/waiters/errors.rb
312
304
  - lib/aws-sdk-core/waiters/null_provider.rb
305
+ - lib/aws-sdk-core/waiters/poller.rb
313
306
  - lib/aws-sdk-core/waiters/provider.rb
314
307
  - lib/aws-sdk-core/waiters/waiter.rb
315
308
  - lib/aws-sdk-core/xml/builder.rb
@@ -1,47 +0,0 @@
1
- {
2
- "version": 2,
3
- "waiters": {
4
- "DistributionDeployed": {
5
- "delay": 60,
6
- "operation": "GetDistribution",
7
- "maxAttempts": 25,
8
- "description": "Wait until a distribution is deployed.",
9
- "acceptors": [
10
- {
11
- "expected": "Deployed",
12
- "matcher": "path",
13
- "state": "success",
14
- "argument": "Status"
15
- }
16
- ]
17
- },
18
- "InvalidationCompleted": {
19
- "delay": 20,
20
- "operation": "GetInvalidation",
21
- "maxAttempts": 30,
22
- "description": "Wait until an invalidation has completed.",
23
- "acceptors": [
24
- {
25
- "expected": "Completed",
26
- "matcher": "path",
27
- "state": "success",
28
- "argument": "Status"
29
- }
30
- ]
31
- },
32
- "StreamingDistributionDeployed": {
33
- "delay": 60,
34
- "operation": "GetStreamingDistribution",
35
- "maxAttempts": 25,
36
- "description": "Wait until a streaming distribution is deployed.",
37
- "acceptors": [
38
- {
39
- "expected": "Deployed",
40
- "matcher": "path",
41
- "state": "success",
42
- "argument": "Status"
43
- }
44
- ]
45
- }
46
- }
47
- }
@@ -1,35 +0,0 @@
1
- {
2
- "version": 2,
3
- "waiters": {
4
- "TableExists": {
5
- "delay": 20,
6
- "operation": "DescribeTable",
7
- "maxAttempts": 25,
8
- "acceptors": [
9
- {
10
- "expected": "ACTIVE",
11
- "matcher": "path",
12
- "state": "success",
13
- "argument": "Table.TableStatus"
14
- },
15
- {
16
- "expected": "ResourceNotFoundException",
17
- "matcher": "error",
18
- "state": "retry"
19
- }
20
- ]
21
- },
22
- "TableNotExists": {
23
- "delay": 20,
24
- "operation": "DescribeTable",
25
- "maxAttempts": 25,
26
- "acceptors": [
27
- {
28
- "expected": "ResourceNotFoundException",
29
- "matcher": "error",
30
- "state": "success"
31
- }
32
- ]
33
- }
34
- }
35
- }
@@ -1,341 +0,0 @@
1
- {
2
- "version": 2,
3
- "waiters": {
4
- "BundleTaskComplete": {
5
- "delay": 15,
6
- "operation": "DescribeBundleTasks",
7
- "maxAttempts": 40,
8
- "acceptors": [
9
- {
10
- "expected": "complete",
11
- "matcher": "pathAll",
12
- "state": "success",
13
- "argument": "BundleTasks[].State"
14
- },
15
- {
16
- "expected": "failed",
17
- "matcher": "pathAny",
18
- "state": "failure",
19
- "argument": "BundleTasks[].State"
20
- }
21
- ]
22
- },
23
- "ConversionTaskCancelled": {
24
- "delay": 15,
25
- "operation": "DescribeConversionTasks",
26
- "maxAttempts": 40,
27
- "acceptors": [
28
- {
29
- "expected": "cancelled",
30
- "matcher": "pathAll",
31
- "state": "success",
32
- "argument": "ConversionTasks[].State"
33
- }
34
- ]
35
- },
36
- "ConversionTaskCompleted": {
37
- "delay": 15,
38
- "operation": "DescribeConversionTasks",
39
- "maxAttempts": 40,
40
- "acceptors": [
41
- {
42
- "expected": "completed",
43
- "matcher": "pathAll",
44
- "state": "success",
45
- "argument": "ConversionTasks[].State"
46
- },
47
- {
48
- "expected": "cancelled",
49
- "matcher": "pathAny",
50
- "state": "failure",
51
- "argument": "ConversionTasks[].State"
52
- },
53
- {
54
- "expected": "cancelling",
55
- "matcher": "pathAny",
56
- "state": "failure",
57
- "argument": "ConversionTasks[].State"
58
- }
59
- ]
60
- },
61
- "ConversionTaskDeleted": {
62
- "delay": 15,
63
- "operation": "DescribeConversionTasks",
64
- "maxAttempts": 40,
65
- "acceptors": [
66
- {
67
- "expected": "deleted",
68
- "matcher": "pathAll",
69
- "state": "success",
70
- "argument": "ConversionTasks[].State"
71
- }
72
- ]
73
- },
74
- "CustomerGatewayAvailable": {
75
- "delay": 15,
76
- "operation": "DescribeCustomerGateways",
77
- "maxAttempts": 40,
78
- "acceptors": [
79
- {
80
- "expected": "available",
81
- "matcher": "pathAll",
82
- "state": "success",
83
- "argument": "CustomerGateways[].State"
84
- },
85
- {
86
- "expected": "deleted",
87
- "matcher": "pathAny",
88
- "state": "failure",
89
- "argument": "CustomerGateways[].State"
90
- },
91
- {
92
- "expected": "deleting",
93
- "matcher": "pathAny",
94
- "state": "failure",
95
- "argument": "CustomerGateways[].State"
96
- }
97
- ]
98
- },
99
- "ExportTaskCancelled": {
100
- "delay": 15,
101
- "operation": "DescribeExportTasks",
102
- "maxAttempts": 40,
103
- "acceptors": [
104
- {
105
- "expected": "cancelled",
106
- "matcher": "pathAll",
107
- "state": "success",
108
- "argument": "ExportTasks[].State"
109
- }
110
- ]
111
- },
112
- "ExportTaskCompleted": {
113
- "delay": 15,
114
- "operation": "DescribeExportTasks",
115
- "maxAttempts": 40,
116
- "acceptors": [
117
- {
118
- "expected": "completed",
119
- "matcher": "pathAll",
120
- "state": "success",
121
- "argument": "ExportTasks[].State"
122
- }
123
- ]
124
- },
125
- "InstanceRunning": {
126
- "delay": 15,
127
- "operation": "DescribeInstances",
128
- "maxAttempts": 40,
129
- "acceptors": [
130
- {
131
- "expected": "running",
132
- "matcher": "pathAll",
133
- "state": "success",
134
- "argument": "Reservations[].Instances[].State.Name"
135
- },
136
- {
137
- "expected": "shutting-down",
138
- "matcher": "pathAny",
139
- "state": "failure",
140
- "argument": "Reservations[].Instances[].State.Name"
141
- },
142
- {
143
- "expected": "terminated",
144
- "matcher": "pathAny",
145
- "state": "failure",
146
- "argument": "Reservations[].Instances[].State.Name"
147
- },
148
- {
149
- "expected": "stopping",
150
- "matcher": "pathAny",
151
- "state": "failure",
152
- "argument": "Reservations[].Instances[].State.Name"
153
- }
154
- ]
155
- },
156
- "InstanceStopped": {
157
- "delay": 15,
158
- "operation": "DescribeInstances",
159
- "maxAttempts": 40,
160
- "acceptors": [
161
- {
162
- "expected": "stopped",
163
- "matcher": "pathAll",
164
- "state": "success",
165
- "argument": "Reservations[].Instances[].State.Name"
166
- },
167
- {
168
- "expected": "pending",
169
- "matcher": "pathAny",
170
- "state": "failure",
171
- "argument": "Reservations[].Instances[].State.Name"
172
- },
173
- {
174
- "expected": "terminated",
175
- "matcher": "pathAny",
176
- "state": "failure",
177
- "argument": "Reservations[].Instances[].State.Name"
178
- }
179
- ]
180
- },
181
- "InstanceTerminated": {
182
- "delay": 15,
183
- "operation": "DescribeInstances",
184
- "maxAttempts": 40,
185
- "acceptors": [
186
- {
187
- "expected": "terminated",
188
- "matcher": "pathAll",
189
- "state": "success",
190
- "argument": "Reservations[].Instances[].State.Name"
191
- },
192
- {
193
- "expected": "pending",
194
- "matcher": "pathAny",
195
- "state": "failure",
196
- "argument": "Reservations[].Instances[].State.Name"
197
- },
198
- {
199
- "expected": "stopping",
200
- "matcher": "pathAny",
201
- "state": "failure",
202
- "argument": "Reservations[].Instances[].State.Name"
203
- }
204
- ]
205
- },
206
- "SnapshotCompleted": {
207
- "delay": 15,
208
- "operation": "DescribeSnapshots",
209
- "maxAttempts": 40,
210
- "acceptors": [
211
- {
212
- "expected": "completed",
213
- "matcher": "pathAll",
214
- "state": "success",
215
- "argument": "Snapshots[].State"
216
- }
217
- ]
218
- },
219
- "SubnetAvailable": {
220
- "delay": 15,
221
- "operation": "DescribeSubnets",
222
- "maxAttempts": 40,
223
- "acceptors": [
224
- {
225
- "expected": "available",
226
- "matcher": "pathAll",
227
- "state": "success",
228
- "argument": "Subnets[].State"
229
- }
230
- ]
231
- },
232
- "VolumeAvailable": {
233
- "delay": 15,
234
- "operation": "DescribeVolumes",
235
- "maxAttempts": 40,
236
- "acceptors": [
237
- {
238
- "expected": "available",
239
- "matcher": "pathAll",
240
- "state": "success",
241
- "argument": "Volumes[].State"
242
- },
243
- {
244
- "expected": "deleted",
245
- "matcher": "pathAny",
246
- "state": "failure",
247
- "argument": "Volumes[].State"
248
- }
249
- ]
250
- },
251
- "VolumeDeleted": {
252
- "delay": 15,
253
- "operation": "DescribeVolumes",
254
- "maxAttempts": 40,
255
- "acceptors": [
256
- {
257
- "expected": "deleted",
258
- "matcher": "pathAll",
259
- "state": "success",
260
- "argument": "Volumes[].State"
261
- }
262
- ]
263
- },
264
- "VolumeInUse": {
265
- "delay": 15,
266
- "operation": "DescribeVolumes",
267
- "maxAttempts": 40,
268
- "acceptors": [
269
- {
270
- "expected": "in-use",
271
- "matcher": "pathAll",
272
- "state": "success",
273
- "argument": "Volumes[].State"
274
- },
275
- {
276
- "expected": "deleted",
277
- "matcher": "pathAny",
278
- "state": "failure",
279
- "argument": "Volumes[].State"
280
- }
281
- ]
282
- },
283
- "VpcAvailable": {
284
- "delay": 15,
285
- "operation": "DescribeVpcs",
286
- "maxAttempts": 40,
287
- "acceptors": [
288
- {
289
- "expected": "available",
290
- "matcher": "pathAll",
291
- "state": "success",
292
- "argument": "Vpcs[].State"
293
- }
294
- ]
295
- },
296
- "VpnConnectionAvailable": {
297
- "delay": 15,
298
- "operation": "DescribeVpnConnections",
299
- "maxAttempts": 40,
300
- "acceptors": [
301
- {
302
- "expected": "available",
303
- "matcher": "pathAll",
304
- "state": "success",
305
- "argument": "VpnConnections[].State"
306
- },
307
- {
308
- "expected": "deleting",
309
- "matcher": "pathAny",
310
- "state": "failure",
311
- "argument": "VpnConnections[].State"
312
- },
313
- {
314
- "expected": "deleted",
315
- "matcher": "pathAny",
316
- "state": "failure",
317
- "argument": "VpnConnections[].State"
318
- }
319
- ]
320
- },
321
- "VpnConnectionDeleted": {
322
- "delay": 15,
323
- "operation": "DescribeVpnConnections",
324
- "maxAttempts": 40,
325
- "acceptors": [
326
- {
327
- "expected": "deleted",
328
- "matcher": "pathAll",
329
- "state": "success",
330
- "argument": "VpnConnections[].State"
331
- },
332
- {
333
- "expected": "pending",
334
- "matcher": "pathAny",
335
- "state": "failure",
336
- "argument": "VpnConnections[].State"
337
- }
338
- ]
339
- }
340
- }
341
- }