Skip to content

Commit 76c1bde

Browse files
band-swi-release-engineering[bot]DX-Bandwidthckoegel
authored
SWI-9387 Update SDK Based on Recent Spec Changes (#281)
* Generate SDK with OpenAPI Generator Version * remove custom error * remove doc * ticket number unit tests * rbm media unit tests * update rbm smoke tests * support discriminator * update template * update test * oneOf discriminator --------- Co-authored-by: DX-Bandwidth <dx@bandwidth.com> Co-authored-by: ckoegel <ckoegel1006@gmail.com>
1 parent 009e0d3 commit 76c1bde

26 files changed

+973
-128
lines changed

bandwidth.yml

Lines changed: 28 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.
@@ -6752,6 +6760,21 @@ components:
67526760
- type: internal-server-error
67536761
description: Internal server error. No further information available
67546762
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.
67556778
createCallResponse:
67566779
description: Created
67576780
headers:

bandwidth/api/messages_api.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ def list_messages(
412412
) -> MessagesList:
413413
"""List Messages
414414
415-
Returns a list of messages based on query parameters.
415+
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.
416416
417417
:param account_id: Your Bandwidth Account ID. (required)
418418
:type account_id: str
@@ -533,6 +533,7 @@ def list_messages(
533533
'401': "MessagingRequestError",
534534
'403': "MessagingRequestError",
535535
'404': "MessagingRequestError",
536+
'405': "MessagingRequestError",
536537
'415': "MessagingRequestError",
537538
'429': "MessagingRequestError",
538539
'500': "MessagingRequestError",
@@ -594,7 +595,7 @@ def list_messages_with_http_info(
594595
) -> ApiResponse[MessagesList]:
595596
"""List Messages
596597
597-
Returns a list of messages based on query parameters.
598+
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.
598599
599600
:param account_id: Your Bandwidth Account ID. (required)
600601
:type account_id: str
@@ -715,6 +716,7 @@ def list_messages_with_http_info(
715716
'401': "MessagingRequestError",
716717
'403': "MessagingRequestError",
717718
'404': "MessagingRequestError",
719+
'405': "MessagingRequestError",
718720
'415': "MessagingRequestError",
719721
'429': "MessagingRequestError",
720722
'500': "MessagingRequestError",
@@ -776,7 +778,7 @@ def list_messages_without_preload_content(
776778
) -> RESTResponseType:
777779
"""List Messages
778780
779-
Returns a list of messages based on query parameters.
781+
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.
780782
781783
:param account_id: Your Bandwidth Account ID. (required)
782784
:type account_id: str
@@ -897,6 +899,7 @@ def list_messages_without_preload_content(
897899
'401': "MessagingRequestError",
898900
'403': "MessagingRequestError",
899901
'404': "MessagingRequestError",
902+
'405': "MessagingRequestError",
900903
'415': "MessagingRequestError",
901904
'429': "MessagingRequestError",
902905
'500': "MessagingRequestError",

bandwidth/models/blocked_webhook.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr, field_validator
2222
from typing import Any, ClassVar, Dict, List, Optional
2323
from typing_extensions import Annotated
24-
from uuid import UUID
2524
from bandwidth.models.tfv_callback_status_enum import TfvCallbackStatusEnum
2625
from typing import Optional, Set
2726
from typing_extensions import Self
@@ -33,7 +32,7 @@ class BlockedWebhook(BaseModel):
3332
account_id: Optional[StrictStr] = Field(default=None, description="User's account ID.", alias="accountId")
3433
phone_number: Optional[Annotated[str, Field(min_length=12, strict=True, max_length=12)]] = Field(default=None, description="Toll-free telephone number in E.164 format.", alias="phoneNumber")
3534
status: Optional[TfvCallbackStatusEnum] = None
36-
internal_ticket_number: Optional[UUID] = Field(default=None, description="Unique identifier (UUID) generated by Bandwidth to assist in tracking the verification status of a toll-free number.", alias="internalTicketNumber")
35+
internal_ticket_number: Optional[StrictStr] = Field(default=None, description="Unique identifier (UUID) generated by Bandwidth to assist in tracking the verification status of a toll-free number.", alias="internalTicketNumber")
3736
blocked: Optional[StrictBool] = Field(default=None, description="Whether a Toll-Free Verification is blocked. This attribute will only be defined when the number is blocked.")
3837
blocked_reason: Optional[StrictStr] = Field(default=None, description="The reason why the Toll-Free Verification is blocked. This attribute will only be defined when the number is blocked.", alias="blockedReason")
3938
additional_properties: Dict[str, Any] = {}

bandwidth/models/callback.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,18 @@ class Callback(BaseModel):
4242
protected_namespaces=(),
4343
)
4444

45-
46-
discriminator_value_class_map: Dict[str, str] = {
45+
# Discriminator's property name (OpenAPI v3)
46+
__openapi_discriminator_name__ = 'type'
47+
48+
# Discriminator's mapping (OpenAPI v3)
49+
__discriminator_value_class_map__ = {
50+
'message-delivered': 'StatusCallback',
51+
'message-failed': 'StatusCallback',
52+
'message-read': 'StatusCallback',
53+
'message-received': 'InboundCallback',
54+
'message-sent': 'StatusCallback',
55+
'request-location-response': 'InboundCallback',
56+
'suggestion-response': 'InboundCallback'
4757
}
4858

4959
def __init__(self, *args, **kwargs) -> None:
@@ -88,6 +98,20 @@ def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self:
8898
def from_json(cls, json_str: str) -> Self:
8999
"""Returns the object represented by the json string"""
90100
instance = cls.model_construct()
101+
102+
# Try to deserialize using the discriminator
103+
json_obj = json.loads(json_str)
104+
discriminator_value = json_obj.get(cls.__openapi_discriminator_name__)
105+
106+
if discriminator_value and discriminator_value in cls.__discriminator_value_class_map__:
107+
class_name = cls.__discriminator_value_class_map__[discriminator_value]
108+
target_class = globals()[class_name]
109+
try:
110+
instance.actual_instance = target_class.from_json(json_str)
111+
return instance
112+
except (ValidationError, ValueError) as e:
113+
raise ValueError(f"Failed to deserialize using discriminator '{discriminator_value}' -> {class_name}: {str(e)}")
114+
91115
error_messages = []
92116
match = 0
93117

bandwidth/models/failure_webhook.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator
2222
from typing import Any, ClassVar, Dict, List, Optional
2323
from typing_extensions import Annotated
24-
from uuid import UUID
2524
from typing import Optional, Set
2625
from typing_extensions import Self
2726

@@ -34,7 +33,7 @@ class FailureWebhook(BaseModel):
3433
error_code: Optional[StrictStr] = Field(default=None, description="An error code indicating what error was encountered. This code can be interpreted as an HTTP status code in regards to the error that was encountered.", alias="errorCode")
3534
error_message: Optional[StrictStr] = Field(default=None, description="A description of the error that was encountered.", alias="errorMessage")
3635
errors: Optional[List[StrictStr]] = Field(default=None, description="Details of the errors that were encountered when processing the request.")
37-
internal_ticket_number: Optional[UUID] = Field(default=None, description="Unique identifier (UUID) generated by Bandwidth to assist in tracking the verification status of a toll-free number.", alias="internalTicketNumber")
36+
internal_ticket_number: Optional[StrictStr] = Field(default=None, description="Unique identifier (UUID) generated by Bandwidth to assist in tracking the verification status of a toll-free number.", alias="internalTicketNumber")
3837
additional_properties: Dict[str, Any] = {}
3938
__properties: ClassVar[List[str]] = ["accountId", "phoneNumber", "errorCode", "errorMessage", "errors", "internalTicketNumber"]
4039

bandwidth/models/multi_channel_action.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,17 @@ class MultiChannelAction(BaseModel):
5757
"protected_namespaces": (),
5858
}
5959

60-
discriminator_value_class_map: Dict[str, str] = {
60+
# Discriminator's property name (OpenAPI v3)
61+
__openapi_discriminator_name__ = 'type'
62+
63+
# Discriminator's mapping (OpenAPI v3)
64+
__discriminator_value_class_map__ = {
65+
'CREATE_CALENDAR_EVENT': 'MultiChannelActionCalendarEvent',
66+
'DIAL_PHONE': 'RbmActionDial',
67+
'OPEN_URL': 'RbmActionOpenUrl',
68+
'REPLY': 'RbmActionBase',
69+
'REQUEST_LOCATION': 'RbmActionBase',
70+
'SHOW_LOCATION': 'RbmActionViewLocation'
6171
}
6272

6373
def __init__(self, *args, **kwargs) -> None:
@@ -118,6 +128,20 @@ def from_dict(cls, obj: Dict[str, Any]) -> Self:
118128
def from_json(cls, json_str: str) -> Self:
119129
"""Returns the object represented by the json string"""
120130
instance = cls.model_construct()
131+
132+
# Try to deserialize using the discriminator
133+
json_obj = json.loads(json_str)
134+
discriminator_value = json_obj.get(cls.__openapi_discriminator_name__)
135+
136+
if discriminator_value and discriminator_value in cls.__discriminator_value_class_map__:
137+
class_name = cls.__discriminator_value_class_map__[discriminator_value]
138+
target_class = globals()[class_name]
139+
try:
140+
instance.actual_instance = target_class.from_json(json_str)
141+
return instance
142+
except (ValidationError, ValueError) as e:
143+
raise ValueError(f"Failed to deserialize using discriminator '{discriminator_value}' -> {class_name}: {str(e)}")
144+
121145
error_messages = []
122146
# anyof_schema_1_validator: Optional[RbmActionBase] = None
123147
try:

bandwidth/models/multi_channel_channel_list_request_object.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,14 @@ class MultiChannelChannelListRequestObject(BaseModel):
5151
"protected_namespaces": (),
5252
}
5353

54-
discriminator_value_class_map: Dict[str, str] = {
54+
# Discriminator's property name (OpenAPI v3)
55+
__openapi_discriminator_name__ = 'channel'
56+
57+
# Discriminator's mapping (OpenAPI v3)
58+
__discriminator_value_class_map__ = {
59+
'MMS': 'MultiChannelChannelListMMSObject',
60+
'RBM': 'MultiChannelChannelListRBMObject',
61+
'SMS': 'MultiChannelChannelListSMSObject'
5562
}
5663

5764
def __init__(self, *args, **kwargs) -> None:
@@ -100,6 +107,20 @@ def from_dict(cls, obj: Dict[str, Any]) -> Self:
100107
def from_json(cls, json_str: str) -> Self:
101108
"""Returns the object represented by the json string"""
102109
instance = cls.model_construct()
110+
111+
# Try to deserialize using the discriminator
112+
json_obj = json.loads(json_str)
113+
discriminator_value = json_obj.get(cls.__openapi_discriminator_name__)
114+
115+
if discriminator_value and discriminator_value in cls.__discriminator_value_class_map__:
116+
class_name = cls.__discriminator_value_class_map__[discriminator_value]
117+
target_class = globals()[class_name]
118+
try:
119+
instance.actual_instance = target_class.from_json(json_str)
120+
return instance
121+
except (ValidationError, ValueError) as e:
122+
raise ValueError(f"Failed to deserialize using discriminator '{discriminator_value}' -> {class_name}: {str(e)}")
123+
103124
error_messages = []
104125
# anyof_schema_1_validator: Optional[MultiChannelChannelListRBMObject] = None
105126
try:

bandwidth/models/multi_channel_channel_list_response_object.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,14 @@ class MultiChannelChannelListResponseObject(BaseModel):
5151
"protected_namespaces": (),
5252
}
5353

54-
discriminator_value_class_map: Dict[str, str] = {
54+
# Discriminator's property name (OpenAPI v3)
55+
__openapi_discriminator_name__ = 'channel'
56+
57+
# Discriminator's mapping (OpenAPI v3)
58+
__discriminator_value_class_map__ = {
59+
'MMS': 'MultiChannelChannelListMMSResponseObject',
60+
'RBM': 'MultiChannelChannelListRBMResponseObject',
61+
'SMS': 'MultiChannelChannelListSMSResponseObject'
5562
}
5663

5764
def __init__(self, *args, **kwargs) -> None:
@@ -100,6 +107,20 @@ def from_dict(cls, obj: Dict[str, Any]) -> Self:
100107
def from_json(cls, json_str: str) -> Self:
101108
"""Returns the object represented by the json string"""
102109
instance = cls.model_construct()
110+
111+
# Try to deserialize using the discriminator
112+
json_obj = json.loads(json_str)
113+
discriminator_value = json_obj.get(cls.__openapi_discriminator_name__)
114+
115+
if discriminator_value and discriminator_value in cls.__discriminator_value_class_map__:
116+
class_name = cls.__discriminator_value_class_map__[discriminator_value]
117+
target_class = globals()[class_name]
118+
try:
119+
instance.actual_instance = target_class.from_json(json_str)
120+
return instance
121+
except (ValidationError, ValueError) as e:
122+
raise ValueError(f"Failed to deserialize using discriminator '{discriminator_value}' -> {class_name}: {str(e)}")
123+
103124
error_messages = []
104125
# anyof_schema_1_validator: Optional[MultiChannelChannelListRBMResponseObject] = None
105126
try:

bandwidth/models/rbm_message_media.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class RbmMessageMedia(BaseModel):
3030
"""
3131
RbmMessageMedia
3232
""" # noqa: E501
33-
media: RbmMessageContentFile
33+
media: List[RbmMessageContentFile]
3434
suggestions: Optional[Annotated[List[MultiChannelAction], Field(max_length=11)]] = Field(default=None, description="An array of suggested actions for the recipient.")
3535
additional_properties: Dict[str, Any] = {}
3636
__properties: ClassVar[List[str]] = ["media", "suggestions"]
@@ -76,9 +76,13 @@ def to_dict(self) -> Dict[str, Any]:
7676
exclude=excluded_fields,
7777
exclude_none=True,
7878
)
79-
# override the default output from pydantic by calling `to_dict()` of media
79+
# override the default output from pydantic by calling `to_dict()` of each item in media (list)
80+
_items = []
8081
if self.media:
81-
_dict['media'] = self.media.to_dict()
82+
for _item_media in self.media:
83+
if _item_media:
84+
_items.append(_item_media.to_dict())
85+
_dict['media'] = _items
8286
# override the default output from pydantic by calling `to_dict()` of each item in suggestions (list)
8387
_items = []
8488
if self.suggestions:
@@ -103,7 +107,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
103107
return cls.model_validate(obj)
104108

105109
_obj = cls.model_validate({
106-
"media": RbmMessageContentFile.from_dict(obj["media"]) if obj.get("media") is not None else None,
110+
"media": [RbmMessageContentFile.from_dict(_item) for _item in obj["media"]] if obj.get("media") is not None else None,
107111
"suggestions": [MultiChannelAction.from_dict(_item) for _item in obj["suggestions"]] if obj.get("suggestions") is not None else None
108112
})
109113
# store additional fields in additional_properties

bandwidth/models/tfv_status.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr, field_validator
2323
from typing import Any, ClassVar, Dict, List, Optional
2424
from typing_extensions import Annotated
25-
from uuid import UUID
2625
from bandwidth.models.tfv_status_enum import TfvStatusEnum
2726
from bandwidth.models.tfv_submission_info import TfvSubmissionInfo
2827
from typing import Optional, Set
@@ -34,7 +33,7 @@ class TfvStatus(BaseModel):
3433
""" # noqa: E501
3534
phone_number: Optional[Annotated[str, Field(min_length=12, strict=True, max_length=12)]] = Field(default=None, description="Toll-free telephone number in E.164 format.", alias="phoneNumber")
3635
status: Optional[TfvStatusEnum] = None
37-
internal_ticket_number: Optional[UUID] = Field(default=None, description="Unique identifier (UUID) generated by Bandwidth to assist in tracking the verification status of a toll-free number - included in all webhook payloads.", alias="internalTicketNumber")
36+
internal_ticket_number: Optional[StrictStr] = Field(default=None, description="Unique identifier (UUID) generated by Bandwidth to assist in tracking the verification status of a toll-free number - included in all webhook payloads.", alias="internalTicketNumber")
3837
decline_reason_description: Optional[StrictStr] = Field(default=None, description="Explanation for why a verification request was declined.", alias="declineReasonDescription")
3938
resubmit_allowed: Optional[StrictBool] = Field(default=None, description="Whether a Toll-Free Verification request qualifies for resubmission via PUT.", alias="resubmitAllowed")
4039
created_date_time: Optional[datetime] = Field(default=None, description="Date and time the verification request was created.", alias="createdDateTime")

0 commit comments

Comments
 (0)