Skip to content

Commit 0f65ab9

Browse files
band-swi-release-engineering[bot]DX-Bandwidthckoegel
authored
SWI-9390 Update SDK Based on Recent Spec Changes (#191)
* Generate SDK with OpenAPI Generator Version * regenerate * unit test * support discriminator for anyOf * update rubocop * update multi channel smoke tests * fix unit test --------- Co-authored-by: DX-Bandwidth <dx@bandwidth.com> Co-authored-by: ckoegel <ckoegel1006@gmail.com>
1 parent 0eb0d2e commit 0f65ab9

21 files changed

+622
-238
lines changed

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ gemspec
55
group :development, :test do
66
gem 'rake', '~> 13.2.1'
77
gem 'pry-byebug'
8-
gem 'rubocop', '~> 1.52.0'
8+
gem 'rubocop', '~> 1.82.0'
99
gem 'simplecov', '~> 0.21.2'
1010
end

bandwidth.yml

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,13 @@ paths:
163163
/users/{accountId}/messages:
164164
get:
165165
summary: List Messages
166-
description: Returns a list of messages based on query parameters.
166+
description: >
167+
Returns a list of messages based on query parameters.
168+
169+
170+
**Rate Limit:** This endpoint is rate limited to 3500 requests per 5
171+
minutes per Source IP address. Exceeding the limit returns HTTP 429 with
172+
a `Retry-After` header.
167173
operationId: listMessages
168174
tags:
169175
- Messages
@@ -207,10 +213,12 @@ paths:
207213
$ref: '#/components/responses/messagingForbiddenError'
208214
'404':
209215
$ref: '#/components/responses/messagingNotFoundError'
216+
'405':
217+
$ref: '#/components/responses/messagingMethodNotAllowedError'
210218
'415':
211219
$ref: '#/components/responses/messagingInvalidMediaTypeError'
212220
'429':
213-
$ref: '#/components/responses/messagingTooManyRequestsError'
221+
$ref: '#/components/responses/listMessagesTooManyRequestsError'
214222
'500':
215223
$ref: '#/components/responses/messagingInternalServerError'
216224
post:
@@ -2257,7 +2265,9 @@ components:
22572265
type: object
22582266
properties:
22592267
media:
2260-
$ref: '#/components/schemas/rbmMessageContentFile'
2268+
type: array
2269+
items:
2270+
$ref: '#/components/schemas/rbmMessageContentFile'
22612271
suggestions:
22622272
$ref: '#/components/schemas/multiChannelFullActions'
22632273
required:
@@ -5875,15 +5885,13 @@ components:
58755885
type: object
58765886
internalTicketNumber:
58775887
type: string
5878-
format: uuid
58795888
description: >-
58805889
Unique identifier (UUID) generated by Bandwidth to assist in tracking
58815890
the verification status of a toll-free number - included in all webhook
58825891
payloads.
58835892
example: acde070d-8c4c-4f0d-9d8a-162843c10333
58845893
internalTicketNumberForWebhook:
58855894
type: string
5886-
format: uuid
58875895
description: >-
58885896
Unique identifier (UUID) generated by Bandwidth to assist in tracking
58895897
the verification status of a toll-free number.
@@ -5956,6 +5964,8 @@ components:
59565964
$ref: '#/components/schemas/helpMessageResponse'
59575965
ageGatedContent:
59585966
$ref: '#/components/schemas/ageGatedContent'
5967+
cvToken:
5968+
$ref: '#/components/schemas/cvToken'
59595969
verificationUpdateRequest:
59605970
type: object
59615971
required:
@@ -6005,6 +6015,8 @@ components:
60056015
$ref: '#/components/schemas/helpMessageResponse'
60066016
ageGatedContent:
60076017
$ref: '#/components/schemas/ageGatedContent'
6018+
cvToken:
6019+
$ref: '#/components/schemas/cvToken'
60086020
tfvBasicAuthentication:
60096021
type: object
60106022
properties:
@@ -6456,6 +6468,8 @@ components:
64566468
$ref: '#/components/schemas/blocked'
64576469
blockedReason:
64586470
$ref: '#/components/schemas/blockedReason'
6471+
cvToken:
6472+
$ref: '#/components/schemas/cvToken'
64596473
tfvSubmissionInfo:
64606474
type: object
64616475
properties:
@@ -6520,6 +6534,18 @@ components:
65206534
nullable: true
65216535
pattern: ^[ -~]{16,64}$
65226536
type: string
6537+
cvToken:
6538+
type: string
6539+
description: >-
6540+
The token provided by Campaign Verify to validate your political use
6541+
case. Only required for 527 political organizations. If you are not a
6542+
527 political organization, this field should be omitted. If you pass an
6543+
empty string, it will be passed along and potentially rejected.
6544+
minLength: 0
6545+
maxLength: 500
6546+
nullable: true
6547+
example: >-
6548+
cv.user123|sess456|mno|tfree|read_write|X7yZ9aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVw
65236549
responses:
65246550
createMessageResponse:
65256551
description: Accepted
@@ -6734,6 +6760,21 @@ components:
67346760
- type: internal-server-error
67356761
description: Internal server error. No further information available
67366762
source: {}
6763+
listMessagesTooManyRequestsError:
6764+
description: Too Many Requests
6765+
headers:
6766+
Retry-After:
6767+
description: The number of seconds to wait before retrying the request.
6768+
schema:
6769+
type: integer
6770+
example: 300
6771+
content:
6772+
application/json:
6773+
schema:
6774+
$ref: "#/components/schemas/messagingRequestError"
6775+
example:
6776+
type: rate_limit_exceeded
6777+
description: Rate limit exceeded. Wait for Retry-After time before sending another request.
67376778
createCallResponse:
67386779
description: Created
67396780
headers:

custom_templates/Gemfile.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ gemspec
55
group :development, :test do
66
gem 'rake', '~> 13.2.1'
77
gem 'pry-byebug'
8-
gem 'rubocop', '~> 1.52.0'
8+
gem 'rubocop', '~> 1.82.0'
99
gem 'simplecov', '~> 0.21.2'
1010
end
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
{{#description}}
2+
# {{{.}}}
3+
{{/description}}
4+
module {{classname}}
5+
class << self
6+
{{#anyOf}}
7+
{{#-first}}
8+
# List of class defined in anyOf (OpenAPI v3)
9+
def openapi_any_of
10+
[
11+
{{/-first}}
12+
:'{{{.}}}'{{^-last}},{{/-last}}
13+
{{#-last}}
14+
]
15+
end
16+
17+
{{/-last}}
18+
{{/anyOf}}
19+
{{#discriminator}}
20+
{{#propertyName}}
21+
# Discriminator's property name (OpenAPI v3)
22+
def openapi_discriminator_name
23+
:'{{{.}}}'
24+
end
25+
26+
{{/propertyName}}
27+
{{#mappedModels}}
28+
{{#-first}}
29+
# Discriminator's mapping (OpenAPI v3)
30+
def openapi_discriminator_mapping
31+
{
32+
{{/-first}}
33+
:'{{{mappingName}}}' => :'{{{modelName}}}'{{^-last}},{{/-last}}
34+
{{#-last}}
35+
}
36+
end
37+
38+
{{/-last}}
39+
{{/mappedModels}}
40+
{{/discriminator}}
41+
# Builds the object
42+
# @param [Mixed] Data to be matched against the list of anyOf items
43+
# @return [Object] Returns the model or the data itself
44+
def build(data)
45+
{{#discriminator}}
46+
discriminator_value = data[openapi_discriminator_name]
47+
return nil if discriminator_value.nil?
48+
{{#mappedModels}}
49+
{{#-first}}
50+
51+
klass = openapi_discriminator_mapping[discriminator_value.to_s.to_sym]
52+
return nil unless klass
53+
54+
{{moduleName}}.const_get(klass).build_from_hash(data)
55+
{{/-first}}
56+
{{/mappedModels}}
57+
{{^mappedModels}}
58+
{{moduleName}}.const_get(discriminator_value).build_from_hash(data)
59+
{{/mappedModels}}
60+
{{/discriminator}}
61+
{{^discriminator}}
62+
# Go through the list of anyOf items and attempt to identify the appropriate one.
63+
# Note:
64+
# - No advanced validation of types in some cases (e.g. "x: { type: string }" will happily match { x: 123 })
65+
# due to the way the deserialization is made in the base_object template (it just casts without verifying).
66+
# - TODO: scalar values are de facto behaving as if they were nullable.
67+
# - TODO: logging when debugging is set.
68+
openapi_any_of.each do |klass|
69+
begin
70+
next if klass == :AnyType # "nullable: true"
71+
return find_and_cast_into_type(klass, data)
72+
rescue # rescue all errors so we keep iterating even if the current item lookup raises
73+
end
74+
end
75+
76+
openapi_any_of.include?(:AnyType) ? data : nil
77+
{{/discriminator}}
78+
end
79+
{{^discriminator}}
80+
81+
private
82+
83+
SchemaMismatchError = Class.new(StandardError)
84+
85+
# Note: 'File' is missing here because in the regular case we get the data _after_ a call to JSON.parse.
86+
def find_and_cast_into_type(klass, data)
87+
return if data.nil?
88+
89+
case klass.to_s
90+
when 'Boolean'
91+
return data if data.instance_of?(TrueClass) || data.instance_of?(FalseClass)
92+
when 'Float'
93+
return data if data.instance_of?(Float)
94+
when 'Integer'
95+
return data if data.instance_of?(Integer)
96+
when 'Time'
97+
return Time.parse(data)
98+
when 'Date'
99+
return Date.iso8601(data)
100+
when 'String'
101+
return data if data.instance_of?(String)
102+
when 'Object' # "type: object"
103+
return data if data.instance_of?(Hash)
104+
when /\AArray<(?<sub_type>.+)>\z/ # "type: array"
105+
if data.instance_of?(Array)
106+
sub_type = Regexp.last_match[:sub_type]
107+
return data.map { |item| find_and_cast_into_type(sub_type, item) }
108+
end
109+
when /\AHash<String, (?<sub_type>.+)>\z/ # "type: object" with "additionalProperties: { ... }"
110+
if data.instance_of?(Hash) && data.keys.all? { |k| k.instance_of?(Symbol) || k.instance_of?(String) }
111+
sub_type = Regexp.last_match[:sub_type]
112+
return data.each_with_object({}) { |(k, v), hsh| hsh[k] = find_and_cast_into_type(sub_type, v) }
113+
end
114+
else # model
115+
const = {{moduleName}}.const_get(klass)
116+
if const
117+
if const.respond_to?(:openapi_any_of) # nested anyOf model
118+
model = const.build(data)
119+
return model if model
120+
else
121+
# raise if data contains keys that are not known to the model
122+
raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty?
123+
model = const.build_from_hash(data)
124+
return model if model
125+
end
126+
end
127+
end
128+
129+
raise # if no match by now, raise
130+
rescue
131+
raise SchemaMismatchError, "#{data} doesn't match the #{klass} type"
132+
end
133+
{{/discriminator}}
134+
end
135+
end

docs/MessagesApi.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ end
8989
9090
List Messages
9191

92-
Returns a list of messages based on query parameters.
92+
Returns a list of messages based on query parameters. **Rate Limit:** This endpoint is rate limited to 3500 requests per 5 minutes per Source IP address. Exceeding the limit returns HTTP 429 with a `Retry-After` header.
9393

9494
### Examples
9595

docs/RbmActionBase.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ require 'bandwidth-sdk'
1616
instance = Bandwidth::RbmActionBase.new(
1717
type: null,
1818
text: Hello world,
19-
postback_data: [B@13866865
19+
postback_data: [B@665f88e1
2020
)
2121
```
2222

docs/RbmMessageMedia.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
| Name | Type | Description | Notes |
66
| ---- | ---- | ----------- | ----- |
7-
| **media** | [**RbmMessageContentFile**](RbmMessageContentFile.md) | | |
7+
| **media** | [**Array&lt;RbmMessageContentFile&gt;**](RbmMessageContentFile.md) | | |
88
| **suggestions** | [**Array&lt;MultiChannelAction&gt;**](MultiChannelAction.md) | An array of suggested actions for the recipient. | [optional] |
99

1010
## Example

docs/RbmSuggestionResponse.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ require 'bandwidth-sdk'
1414

1515
instance = Bandwidth::RbmSuggestionResponse.new(
1616
text: Yes, I would like to proceed,
17-
postback_data: [B@13866865
17+
postback_data: [B@665f88e1
1818
)
1919
```
2020

docs/TfvStatus.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
| **submission** | [**TfvSubmissionInfo**](TfvSubmissionInfo.md) | | [optional] |
1515
| **blocked** | **Boolean** | Whether a Toll-Free Verification is blocked. This attribute will only be defined when the number is blocked. | [optional] |
1616
| **blocked_reason** | **String** | The reason why the Toll-Free Verification is blocked. This attribute will only be defined when the number is blocked. | [optional] |
17+
| **cv_token** | **String** | The token provided by Campaign Verify to validate your political use case. Only required for 527 political organizations. If you are not a 527 political organization, this field should be omitted. If you pass an empty string, it will be passed along and potentially rejected. | [optional] |
1718

1819
## Example
1920

@@ -30,7 +31,8 @@ instance = Bandwidth::TfvStatus.new(
3031
modified_date_time: 2021-06-08T06:45:13Z,
3132
submission: null,
3233
blocked: true,
33-
blocked_reason: Toll-free number was used to send spam messages
34+
blocked_reason: Toll-free number was used to send spam messages,
35+
cv_token: cv.user123|sess456|mno|tfree|read_write|X7yZ9aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVw
3436
)
3537
```
3638

docs/VerificationRequest.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
| **business_entity_type** | [**BusinessEntityTypeEnum**](BusinessEntityTypeEnum.md) | | [optional] |
2323
| **help_message_response** | **String** | A message that gets sent to users requesting help. | [optional] |
2424
| **age_gated_content** | **Boolean** | Indicates whether the content is age-gated. | [optional] |
25+
| **cv_token** | **String** | The token provided by Campaign Verify to validate your political use case. Only required for 527 political organizations. If you are not a 527 political organization, this field should be omitted. If you pass an empty string, it will be passed along and potentially rejected. | [optional] |
2526

2627
## Example
2728

@@ -46,7 +47,8 @@ instance = Bandwidth::VerificationRequest.new(
4647
business_registration_type: null,
4748
business_entity_type: null,
4849
help_message_response: Please contact support for assistance.,
49-
age_gated_content: false
50+
age_gated_content: false,
51+
cv_token: cv.user123|sess456|mno|tfree|read_write|X7yZ9aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789aBcDeFgHiJkLmNoPqRsTuVw
5052
)
5153
```
5254

0 commit comments

Comments
 (0)