appnigma-sdk 0.1.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 (63) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +359 -0
  4. data/lib/appnigma/checks.rb +90 -0
  5. data/lib/appnigma/client.rb +118 -0
  6. data/lib/appnigma/connections.rb +31 -0
  7. data/lib/appnigma/constants.rb +80 -0
  8. data/lib/appnigma/crm.rb +40 -0
  9. data/lib/appnigma/errors.rb +135 -0
  10. data/lib/appnigma/generated/api/default_api.rb +972 -0
  11. data/lib/appnigma/generated/api_client.rb +441 -0
  12. data/lib/appnigma/generated/api_error.rb +58 -0
  13. data/lib/appnigma/generated/api_model_base.rb +88 -0
  14. data/lib/appnigma/generated/configuration.rb +392 -0
  15. data/lib/appnigma/generated/models/connection_status.rb +41 -0
  16. data/lib/appnigma/generated/models/create_connection_link_input.rb +265 -0
  17. data/lib/appnigma/generated/models/create_connection_link_output.rb +205 -0
  18. data/lib/appnigma/generated/models/crm_company.rb +322 -0
  19. data/lib/appnigma/generated/models/crm_company_get_output.rb +177 -0
  20. data/lib/appnigma/generated/models/crm_company_list_output.rb +192 -0
  21. data/lib/appnigma/generated/models/crm_contact.rb +404 -0
  22. data/lib/appnigma/generated/models/crm_contact_get_output.rb +177 -0
  23. data/lib/appnigma/generated/models/crm_contact_list_output.rb +192 -0
  24. data/lib/appnigma/generated/models/crm_custom_field.rb +189 -0
  25. data/lib/appnigma/generated/models/crm_deal.rb +479 -0
  26. data/lib/appnigma/generated/models/crm_deal_get_output.rb +177 -0
  27. data/lib/appnigma/generated/models/crm_deal_list_output.rb +192 -0
  28. data/lib/appnigma/generated/models/crm_email.rb +249 -0
  29. data/lib/appnigma/generated/models/crm_get_input.rb +272 -0
  30. data/lib/appnigma/generated/models/crm_labelled.rb +190 -0
  31. data/lib/appnigma/generated/models/crm_list_input.rb +303 -0
  32. data/lib/appnigma/generated/models/crm_phone.rb +249 -0
  33. data/lib/appnigma/generated/models/function_entry.rb +346 -0
  34. data/lib/appnigma/generated/models/function_owner.rb +105 -0
  35. data/lib/appnigma/generated/models/function_owner_one_of.rb +201 -0
  36. data/lib/appnigma/generated/models/function_owner_one_of1.rb +227 -0
  37. data/lib/appnigma/generated/models/get_connection_output.rb +251 -0
  38. data/lib/appnigma/generated/models/get_guide_output.rb +178 -0
  39. data/lib/appnigma/generated/models/json_value.rb +106 -0
  40. data/lib/appnigma/generated/models/list_functions_output.rb +179 -0
  41. data/lib/appnigma/generated/models/platform_request_input.rb +314 -0
  42. data/lib/appnigma/generated/models/platform_request_input_body.rb +104 -0
  43. data/lib/appnigma/generated/models/platform_request_output.rb +221 -0
  44. data/lib/appnigma/generated/models/post_input.rb +327 -0
  45. data/lib/appnigma/generated/models/post_input_record_value.rb +104 -0
  46. data/lib/appnigma/generated/models/post_output.rb +203 -0
  47. data/lib/appnigma/generated/models/pull_input.rb +335 -0
  48. data/lib/appnigma/generated/models/pull_output.rb +191 -0
  49. data/lib/appnigma/generated/models/pull_output_records_inner.rb +205 -0
  50. data/lib/appnigma/generated/models/run_input.rb +332 -0
  51. data/lib/appnigma/generated/models/run_output.rb +164 -0
  52. data/lib/appnigma/generated/models/sdk_error.rb +177 -0
  53. data/lib/appnigma/generated/models/sdk_error_error.rb +254 -0
  54. data/lib/appnigma/generated/models/sdk_error_error_details.rb +211 -0
  55. data/lib/appnigma/generated/models/sdk_problem.rb +205 -0
  56. data/lib/appnigma/generated/version.rb +15 -0
  57. data/lib/appnigma/generated.rb +82 -0
  58. data/lib/appnigma/names.rb +62 -0
  59. data/lib/appnigma/retry.rb +80 -0
  60. data/lib/appnigma/transport.rb +138 -0
  61. data/lib/appnigma/version.rb +9 -0
  62. data/lib/appnigma.rb +16 -0
  63. metadata +155 -0
@@ -0,0 +1,192 @@
1
+ =begin
2
+ #Appnigma SDK API
3
+
4
+ #The routes a customer's product calls through an Appnigma SDK. Every answer carries a request id in X-Request-Id; quote it when you write to support.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.26.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module Appnigma::Generated
17
+ class CrmCompanyListOutput < ApiModelBase
18
+ attr_accessor :records
19
+
20
+ # Pass it as cursor for the next page; null on the last page
21
+ attr_accessor :_next
22
+
23
+ # Attribute mapping from ruby-style variable name to JSON key.
24
+ def self.attribute_map
25
+ {
26
+ :'records' => :'records',
27
+ :'_next' => :'next'
28
+ }
29
+ end
30
+
31
+ # Returns attribute mapping this model knows about
32
+ def self.acceptable_attribute_map
33
+ attribute_map
34
+ end
35
+
36
+ # Returns all the JSON keys this model knows about
37
+ def self.acceptable_attributes
38
+ acceptable_attribute_map.values
39
+ end
40
+
41
+ # Attribute type mapping.
42
+ def self.openapi_types
43
+ {
44
+ :'records' => :'Array<CrmCompany>',
45
+ :'_next' => :'String'
46
+ }
47
+ end
48
+
49
+ # List of attributes with nullable: true
50
+ def self.openapi_nullable
51
+ Set.new([
52
+ :'_next'
53
+ ])
54
+ end
55
+
56
+ # Returns attribute type mapping
57
+ def self.acceptable_openapi_types
58
+ openapi_types
59
+ end
60
+
61
+ # Returns the nullable attributes
62
+ def self.acceptable_openapi_nullable
63
+ openapi_nullable
64
+ end
65
+
66
+ # Initializes the object
67
+ # @param [Hash] attributes Model attributes in the form of hash
68
+ def initialize(attributes = {})
69
+ if (!attributes.is_a?(Hash))
70
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Appnigma::Generated::CrmCompanyListOutput` initialize method"
71
+ end
72
+
73
+ # check to see if the attribute exists and convert string to symbol for hash key
74
+ acceptable_attribute_map = self.class.acceptable_attribute_map
75
+ attributes = attributes.each_with_object({}) { |(k, v), h|
76
+ if (!acceptable_attribute_map.key?(k.to_sym))
77
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Appnigma::Generated::CrmCompanyListOutput`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
78
+ end
79
+ h[k.to_sym] = v
80
+ }
81
+
82
+ if attributes.key?(:'records')
83
+ if (value = attributes[:'records']).is_a?(Array)
84
+ self.records = value
85
+ end
86
+ else
87
+ self.records = nil
88
+ end
89
+
90
+ if attributes.key?(:'_next')
91
+ self._next = attributes[:'_next']
92
+ else
93
+ self._next = nil
94
+ end
95
+ end
96
+
97
+ # Show invalid properties with the reasons. Usually used together with valid?
98
+ # @return Array for valid properties with the reasons
99
+ def list_invalid_properties
100
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
101
+ invalid_properties = Array.new
102
+ if @records.nil?
103
+ invalid_properties.push('invalid value for "records", records cannot be nil.')
104
+ end
105
+
106
+ invalid_properties
107
+ end
108
+
109
+ # Check to see if the all the properties in the model are valid
110
+ # @return true if the model is valid
111
+ def valid?
112
+ warn '[DEPRECATED] the `valid?` method is obsolete'
113
+ return false if @records.nil?
114
+ true
115
+ end
116
+
117
+ # Custom attribute writer method with validation
118
+ # @param [Object] records Value to be assigned
119
+ def records=(records)
120
+ if records.nil?
121
+ fail ArgumentError, 'records cannot be nil'
122
+ end
123
+
124
+ @records = records
125
+ end
126
+
127
+ # Checks equality by comparing each attribute.
128
+ # @param [Object] Object to be compared
129
+ def ==(o)
130
+ return true if self.equal?(o)
131
+ self.class == o.class &&
132
+ records == o.records &&
133
+ _next == o._next
134
+ end
135
+
136
+ # @see the `==` method
137
+ # @param [Object] Object to be compared
138
+ def eql?(o)
139
+ self == o
140
+ end
141
+
142
+ # Calculates hash code according to all attributes.
143
+ # @return [Integer] Hash code
144
+ def hash
145
+ [records, _next].hash
146
+ end
147
+
148
+ # Builds the object from hash
149
+ # @param [Hash] attributes Model attributes in the form of hash
150
+ # @return [Object] Returns the model itself
151
+ def self.build_from_hash(attributes)
152
+ return nil unless attributes.is_a?(Hash)
153
+ attributes = attributes.transform_keys(&:to_sym)
154
+ # include attributes inherited from allOf parents (the nearest declaration wins)
155
+ map = acceptable_attribute_map
156
+ transformed_hash = {}
157
+ acceptable_openapi_types.each_pair do |key, type|
158
+ if attributes.key?(map[key]) && attributes[map[key]].nil?
159
+ transformed_hash["#{key}"] = nil
160
+ elsif type =~ /\AArray<(.*)>/i
161
+ # check to ensure the input is an array given that the attribute
162
+ # is documented as an array but the input is not
163
+ if attributes[map[key]].is_a?(Array)
164
+ transformed_hash["#{key}"] = attributes[map[key]].map { |v| _deserialize($1, v) }
165
+ end
166
+ elsif !attributes[map[key]].nil?
167
+ transformed_hash["#{key}"] = _deserialize(type, attributes[map[key]])
168
+ end
169
+ end
170
+ new(transformed_hash)
171
+ end
172
+
173
+ # Returns the object in the form of hash
174
+ # @return [Hash] Returns the object in the form of hash
175
+ def to_hash
176
+ hash = {}
177
+ # include attributes inherited from allOf parents (the nearest declaration wins)
178
+ self.class.acceptable_attribute_map.each_pair do |attr, param|
179
+ value = self.send(attr)
180
+ if value.nil?
181
+ is_nullable = self.class.acceptable_openapi_nullable.include?(attr)
182
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
183
+ end
184
+
185
+ hash[param] = _to_hash(value)
186
+ end
187
+ hash
188
+ end
189
+
190
+ end
191
+
192
+ end
@@ -0,0 +1,404 @@
1
+ =begin
2
+ #Appnigma SDK API
3
+
4
+ #The routes a customer's product calls through an Appnigma SDK. Every answer carries a request id in X-Request-Id; quote it when you write to support.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.26.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module Appnigma::Generated
17
+ class CrmContact < ApiModelBase
18
+ # The record's id in the CRM
19
+ attr_accessor :id
20
+
21
+ attr_accessor :platform
22
+
23
+ # ISO 8601, in UTC
24
+ attr_accessor :created_at
25
+
26
+ # ISO 8601, in UTC
27
+ attr_accessor :updated_at
28
+
29
+ # The CRM's custom properties, by their internal name
30
+ attr_accessor :custom_fields
31
+
32
+ # The record exactly as the CRM sent it
33
+ attr_accessor :raw
34
+
35
+ attr_accessor :first_name
36
+
37
+ attr_accessor :last_name
38
+
39
+ attr_accessor :full_name
40
+
41
+ # The primary one first
42
+ attr_accessor :emails
43
+
44
+ # The primary one first
45
+ attr_accessor :phones
46
+
47
+ attr_accessor :company_id
48
+
49
+ attr_accessor :owner_id
50
+
51
+ # Attribute mapping from ruby-style variable name to JSON key.
52
+ def self.attribute_map
53
+ {
54
+ :'id' => :'id',
55
+ :'platform' => :'platform',
56
+ :'created_at' => :'createdAt',
57
+ :'updated_at' => :'updatedAt',
58
+ :'custom_fields' => :'customFields',
59
+ :'raw' => :'raw',
60
+ :'first_name' => :'firstName',
61
+ :'last_name' => :'lastName',
62
+ :'full_name' => :'fullName',
63
+ :'emails' => :'emails',
64
+ :'phones' => :'phones',
65
+ :'company_id' => :'companyId',
66
+ :'owner_id' => :'ownerId'
67
+ }
68
+ end
69
+
70
+ # Returns attribute mapping this model knows about
71
+ def self.acceptable_attribute_map
72
+ attribute_map
73
+ end
74
+
75
+ # Returns all the JSON keys this model knows about
76
+ def self.acceptable_attributes
77
+ acceptable_attribute_map.values
78
+ end
79
+
80
+ # Attribute type mapping.
81
+ def self.openapi_types
82
+ {
83
+ :'id' => :'String',
84
+ :'platform' => :'String',
85
+ :'created_at' => :'String',
86
+ :'updated_at' => :'String',
87
+ :'custom_fields' => :'Hash<String, CrmCustomField>',
88
+ :'raw' => :'Object',
89
+ :'first_name' => :'String',
90
+ :'last_name' => :'String',
91
+ :'full_name' => :'String',
92
+ :'emails' => :'Array<CrmEmail>',
93
+ :'phones' => :'Array<CrmPhone>',
94
+ :'company_id' => :'String',
95
+ :'owner_id' => :'String'
96
+ }
97
+ end
98
+
99
+ # List of attributes with nullable: true
100
+ def self.openapi_nullable
101
+ Set.new([
102
+ :'created_at',
103
+ :'updated_at',
104
+ :'first_name',
105
+ :'last_name',
106
+ :'full_name',
107
+ :'company_id',
108
+ :'owner_id'
109
+ ])
110
+ end
111
+
112
+ # Returns attribute type mapping
113
+ def self.acceptable_openapi_types
114
+ openapi_types
115
+ end
116
+
117
+ # Returns the nullable attributes
118
+ def self.acceptable_openapi_nullable
119
+ openapi_nullable
120
+ end
121
+
122
+ # Initializes the object
123
+ # @param [Hash] attributes Model attributes in the form of hash
124
+ def initialize(attributes = {})
125
+ if (!attributes.is_a?(Hash))
126
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Appnigma::Generated::CrmContact` initialize method"
127
+ end
128
+
129
+ # check to see if the attribute exists and convert string to symbol for hash key
130
+ acceptable_attribute_map = self.class.acceptable_attribute_map
131
+ attributes = attributes.each_with_object({}) { |(k, v), h|
132
+ if (!acceptable_attribute_map.key?(k.to_sym))
133
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Appnigma::Generated::CrmContact`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
134
+ end
135
+ h[k.to_sym] = v
136
+ }
137
+
138
+ if attributes.key?(:'id')
139
+ self.id = attributes[:'id']
140
+ else
141
+ self.id = nil
142
+ end
143
+
144
+ if attributes.key?(:'platform')
145
+ self.platform = attributes[:'platform']
146
+ else
147
+ self.platform = nil
148
+ end
149
+
150
+ if attributes.key?(:'created_at')
151
+ self.created_at = attributes[:'created_at']
152
+ else
153
+ self.created_at = nil
154
+ end
155
+
156
+ if attributes.key?(:'updated_at')
157
+ self.updated_at = attributes[:'updated_at']
158
+ else
159
+ self.updated_at = nil
160
+ end
161
+
162
+ if attributes.key?(:'custom_fields')
163
+ if (value = attributes[:'custom_fields']).is_a?(Hash)
164
+ self.custom_fields = value
165
+ end
166
+ else
167
+ self.custom_fields = nil
168
+ end
169
+
170
+ if attributes.key?(:'raw')
171
+ self.raw = attributes[:'raw']
172
+ else
173
+ self.raw = nil
174
+ end
175
+
176
+ if attributes.key?(:'first_name')
177
+ self.first_name = attributes[:'first_name']
178
+ else
179
+ self.first_name = nil
180
+ end
181
+
182
+ if attributes.key?(:'last_name')
183
+ self.last_name = attributes[:'last_name']
184
+ else
185
+ self.last_name = nil
186
+ end
187
+
188
+ if attributes.key?(:'full_name')
189
+ self.full_name = attributes[:'full_name']
190
+ else
191
+ self.full_name = nil
192
+ end
193
+
194
+ if attributes.key?(:'emails')
195
+ if (value = attributes[:'emails']).is_a?(Array)
196
+ self.emails = value
197
+ end
198
+ else
199
+ self.emails = nil
200
+ end
201
+
202
+ if attributes.key?(:'phones')
203
+ if (value = attributes[:'phones']).is_a?(Array)
204
+ self.phones = value
205
+ end
206
+ else
207
+ self.phones = nil
208
+ end
209
+
210
+ if attributes.key?(:'company_id')
211
+ self.company_id = attributes[:'company_id']
212
+ else
213
+ self.company_id = nil
214
+ end
215
+
216
+ if attributes.key?(:'owner_id')
217
+ self.owner_id = attributes[:'owner_id']
218
+ else
219
+ self.owner_id = nil
220
+ end
221
+ end
222
+
223
+ # Show invalid properties with the reasons. Usually used together with valid?
224
+ # @return Array for valid properties with the reasons
225
+ def list_invalid_properties
226
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
227
+ invalid_properties = Array.new
228
+ if @id.nil?
229
+ invalid_properties.push('invalid value for "id", id cannot be nil.')
230
+ end
231
+
232
+ if @platform.nil?
233
+ invalid_properties.push('invalid value for "platform", platform cannot be nil.')
234
+ end
235
+
236
+ if @custom_fields.nil?
237
+ invalid_properties.push('invalid value for "custom_fields", custom_fields cannot be nil.')
238
+ end
239
+
240
+ if @raw.nil?
241
+ invalid_properties.push('invalid value for "raw", raw cannot be nil.')
242
+ end
243
+
244
+ if @emails.nil?
245
+ invalid_properties.push('invalid value for "emails", emails cannot be nil.')
246
+ end
247
+
248
+ if @phones.nil?
249
+ invalid_properties.push('invalid value for "phones", phones cannot be nil.')
250
+ end
251
+
252
+ invalid_properties
253
+ end
254
+
255
+ # Check to see if the all the properties in the model are valid
256
+ # @return true if the model is valid
257
+ def valid?
258
+ warn '[DEPRECATED] the `valid?` method is obsolete'
259
+ return false if @id.nil?
260
+ return false if @platform.nil?
261
+ return false if @custom_fields.nil?
262
+ return false if @raw.nil?
263
+ return false if @emails.nil?
264
+ return false if @phones.nil?
265
+ true
266
+ end
267
+
268
+ # Custom attribute writer method with validation
269
+ # @param [Object] id Value to be assigned
270
+ def id=(id)
271
+ if id.nil?
272
+ fail ArgumentError, 'id cannot be nil'
273
+ end
274
+
275
+ @id = id
276
+ end
277
+
278
+ # Custom attribute writer method with validation
279
+ # @param [Object] platform Value to be assigned
280
+ def platform=(platform)
281
+ if platform.nil?
282
+ fail ArgumentError, 'platform cannot be nil'
283
+ end
284
+
285
+ @platform = platform
286
+ end
287
+
288
+ # Custom attribute writer method with validation
289
+ # @param [Object] custom_fields Value to be assigned
290
+ def custom_fields=(custom_fields)
291
+ if custom_fields.nil?
292
+ fail ArgumentError, 'custom_fields cannot be nil'
293
+ end
294
+
295
+ @custom_fields = custom_fields
296
+ end
297
+
298
+ # Custom attribute writer method with validation
299
+ # @param [Object] raw Value to be assigned
300
+ def raw=(raw)
301
+ if raw.nil?
302
+ fail ArgumentError, 'raw cannot be nil'
303
+ end
304
+
305
+ @raw = raw
306
+ end
307
+
308
+ # Custom attribute writer method with validation
309
+ # @param [Object] emails Value to be assigned
310
+ def emails=(emails)
311
+ if emails.nil?
312
+ fail ArgumentError, 'emails cannot be nil'
313
+ end
314
+
315
+ @emails = emails
316
+ end
317
+
318
+ # Custom attribute writer method with validation
319
+ # @param [Object] phones Value to be assigned
320
+ def phones=(phones)
321
+ if phones.nil?
322
+ fail ArgumentError, 'phones cannot be nil'
323
+ end
324
+
325
+ @phones = phones
326
+ end
327
+
328
+ # Checks equality by comparing each attribute.
329
+ # @param [Object] Object to be compared
330
+ def ==(o)
331
+ return true if self.equal?(o)
332
+ self.class == o.class &&
333
+ id == o.id &&
334
+ platform == o.platform &&
335
+ created_at == o.created_at &&
336
+ updated_at == o.updated_at &&
337
+ custom_fields == o.custom_fields &&
338
+ raw == o.raw &&
339
+ first_name == o.first_name &&
340
+ last_name == o.last_name &&
341
+ full_name == o.full_name &&
342
+ emails == o.emails &&
343
+ phones == o.phones &&
344
+ company_id == o.company_id &&
345
+ owner_id == o.owner_id
346
+ end
347
+
348
+ # @see the `==` method
349
+ # @param [Object] Object to be compared
350
+ def eql?(o)
351
+ self == o
352
+ end
353
+
354
+ # Calculates hash code according to all attributes.
355
+ # @return [Integer] Hash code
356
+ def hash
357
+ [id, platform, created_at, updated_at, custom_fields, raw, first_name, last_name, full_name, emails, phones, company_id, owner_id].hash
358
+ end
359
+
360
+ # Builds the object from hash
361
+ # @param [Hash] attributes Model attributes in the form of hash
362
+ # @return [Object] Returns the model itself
363
+ def self.build_from_hash(attributes)
364
+ return nil unless attributes.is_a?(Hash)
365
+ attributes = attributes.transform_keys(&:to_sym)
366
+ # include attributes inherited from allOf parents (the nearest declaration wins)
367
+ map = acceptable_attribute_map
368
+ transformed_hash = {}
369
+ acceptable_openapi_types.each_pair do |key, type|
370
+ if attributes.key?(map[key]) && attributes[map[key]].nil?
371
+ transformed_hash["#{key}"] = nil
372
+ elsif type =~ /\AArray<(.*)>/i
373
+ # check to ensure the input is an array given that the attribute
374
+ # is documented as an array but the input is not
375
+ if attributes[map[key]].is_a?(Array)
376
+ transformed_hash["#{key}"] = attributes[map[key]].map { |v| _deserialize($1, v) }
377
+ end
378
+ elsif !attributes[map[key]].nil?
379
+ transformed_hash["#{key}"] = _deserialize(type, attributes[map[key]])
380
+ end
381
+ end
382
+ new(transformed_hash)
383
+ end
384
+
385
+ # Returns the object in the form of hash
386
+ # @return [Hash] Returns the object in the form of hash
387
+ def to_hash
388
+ hash = {}
389
+ # include attributes inherited from allOf parents (the nearest declaration wins)
390
+ self.class.acceptable_attribute_map.each_pair do |attr, param|
391
+ value = self.send(attr)
392
+ if value.nil?
393
+ is_nullable = self.class.acceptable_openapi_nullable.include?(attr)
394
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
395
+ end
396
+
397
+ hash[param] = _to_hash(value)
398
+ end
399
+ hash
400
+ end
401
+
402
+ end
403
+
404
+ end