voicetel 2.2.10 → 2.2.14
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.
- checksums.yaml +4 -4
- data/README.md +12 -11
- data/lib/voicetel/internal/transport.rb +4 -2
- data/lib/voicetel/models/account.rb +168 -0
- data/lib/voicetel/models/acl.rb +59 -0
- data/lib/voicetel/models/authentication.rb +52 -0
- data/lib/voicetel/models/base.rb +94 -0
- data/lib/voicetel/models/common.rb +20 -0
- data/lib/voicetel/models/e911.rb +72 -0
- data/lib/voicetel/models/gateways.rb +52 -0
- data/lib/voicetel/models/i_numbering.rb +140 -0
- data/lib/voicetel/models/lookups.rb +29 -0
- data/lib/voicetel/models/messaging.rb +143 -0
- data/lib/voicetel/models/numbers.rb +276 -0
- data/lib/voicetel/models/support.rb +191 -0
- data/lib/voicetel/models.rb +18 -0
- data/lib/voicetel/resources/account.rb +35 -12
- data/lib/voicetel/resources/acl.rb +14 -5
- data/lib/voicetel/resources/authentication.rb +14 -7
- data/lib/voicetel/resources/base.rb +6 -0
- data/lib/voicetel/resources/e911.rb +22 -6
- data/lib/voicetel/resources/gateways.rb +23 -6
- data/lib/voicetel/resources/i_numbering.rb +23 -8
- data/lib/voicetel/resources/lookups.rb +8 -2
- data/lib/voicetel/resources/messaging.rb +28 -8
- data/lib/voicetel/resources/numbers.rb +121 -28
- data/lib/voicetel/resources/support.rb +36 -10
- data/lib/voicetel/version.rb +3 -3
- data/lib/voicetel.rb +1 -0
- data/sig/voicetel.rbs +1252 -79
- data/voicetel.gemspec +3 -3
- metadata +18 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cd5f5ccd7a80002a64926f5f66347782ab00b632e69d2f95edd362f0c92b6b92
|
|
4
|
+
data.tar.gz: '096b5af3dad422f37189da7840bd343278ea2c2302a9a353b0d9a83f03b71709'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '088e89700e06dad3a4be905b29b7588cd70db76d574fbc25be78ec764f7873bdfe87d15edc07a028579b8aa978e704c711d795faab285375f749cfb33bcfb3e0'
|
|
7
|
+
data.tar.gz: e1439a7218f43a85bb60bcab70f3d7ff191a4af727336412cb7a6e440a0a2a4738efb717cbaf2a0cae35f8d751d360aa9252d2d26ca023b016daa4ba647ba829
|
data/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
The official Ruby client for the [VoiceTel REST API](https://voicetel.com/docs/api/v2.2/) — provision numbers, place orders, validate e911, send messages, and manage your account, all with idiomatic Ruby, structured errors, and zero codegen footprint.
|
|
4
4
|
|
|
5
|
-

|
|
6
|
+

|
|
7
7
|

|
|
8
8
|

|
|
9
9
|

|
|
@@ -28,6 +28,7 @@ The official Ruby client for the [VoiceTel REST API](https://voicetel.com/docs/a
|
|
|
28
28
|
### 🧱 Idiomatic Ruby End-to-End
|
|
29
29
|
- **Resource-style API** — `client.numbers.list`, `client.messaging.send_message(...)`, just like the official SDKs in every other language.
|
|
30
30
|
- **snake_case everywhere**, automatically camelCased on the wire so the spec's `fromNumber` / `toNumber` / `messagingBrandId` look like plain Ruby.
|
|
31
|
+
- **Typed Data models** — every response is an immutable `Data.define` object (`VoiceTel::Models::...`) with snake_case readers; unknown server fields are ignored, so the API can grow without breaking you.
|
|
31
32
|
- **RBS signatures** ship in `sig/` — opt into type-checking with Steep or Sorbet, or ignore them and stay dynamic.
|
|
32
33
|
|
|
33
34
|
### 🔁 Production-Grade Transport
|
|
@@ -63,10 +64,10 @@ gem install voicetel
|
|
|
63
64
|
Or in your `Gemfile`:
|
|
64
65
|
|
|
65
66
|
```ruby
|
|
66
|
-
gem "voicetel", "~> 2.2.
|
|
67
|
+
gem "voicetel", "~> 2.2.14"
|
|
67
68
|
```
|
|
68
69
|
|
|
69
|
-
Requires Ruby 3.
|
|
70
|
+
Requires Ruby 3.2 or later. Tested on 3.2, 3.3, and 3.4.
|
|
70
71
|
|
|
71
72
|
## 🏁 Quickstart
|
|
72
73
|
|
|
@@ -79,11 +80,11 @@ client = VoiceTel::Client.new
|
|
|
79
80
|
client.login(username: 1_000_000_001, password: "hunter2")
|
|
80
81
|
|
|
81
82
|
me = client.account.get
|
|
82
|
-
puts "Balance: $#{me
|
|
83
|
+
puts "Balance: $#{me.cash} | Caller ID: #{me.caller_id}"
|
|
83
84
|
|
|
84
85
|
# List your numbers
|
|
85
|
-
client.numbers.list
|
|
86
|
-
puts "#{n
|
|
86
|
+
client.numbers.list.numbers.each do |n|
|
|
87
|
+
puts "#{n.number} route=#{n.route} cnam=#{n.cnam} sms=#{n.sms_enabled}"
|
|
87
88
|
end
|
|
88
89
|
```
|
|
89
90
|
|
|
@@ -93,8 +94,8 @@ Or, if you already have an API key:
|
|
|
93
94
|
client = VoiceTel::Client.new(api_key: ENV.fetch("VOICETEL_API_KEY"))
|
|
94
95
|
|
|
95
96
|
coverage = client.i_numbering.coverage(state: "NJ")
|
|
96
|
-
coverage
|
|
97
|
-
puts "#{bucket
|
|
97
|
+
coverage.coverage.each do |bucket|
|
|
98
|
+
puts "#{bucket.npa}-#{bucket.nxx}: #{bucket.count} TNs available"
|
|
98
99
|
end
|
|
99
100
|
```
|
|
100
101
|
|
|
@@ -141,7 +142,7 @@ client.numbers.assign_campaign("2015551234", campaign_id: "CABC123")
|
|
|
141
142
|
# → PUT /v2.2/numbers/2015551234/messaging-campaign { "campaignId": "CABC123" }
|
|
142
143
|
```
|
|
143
144
|
|
|
144
|
-
Responses come back as
|
|
145
|
+
Responses come back as immutable `Data` model classes (`VoiceTel::Models::...`) with `snake_case` readers — `assign_campaign` returns a `Models::NumberMessagingCampaignAssignData` whose `status` is `"enabled"` (bound now, path B), `"already"` (was already bound — idempotent), or `"pending"` (path C, provisioning asynchronously — poll `client.numbers.get_messaging(n).pending` until its `state` is `"done"` or `"failed"`). Any model converts to a plain Hash with `#to_h` when you need to serialize or pretty-print it.
|
|
145
146
|
|
|
146
147
|
## 🚨 Error Handling
|
|
147
148
|
|
|
@@ -171,7 +172,7 @@ rescue VoiceTel::ApiError => e
|
|
|
171
172
|
end
|
|
172
173
|
```
|
|
173
174
|
|
|
174
|
-
`ApiError#body` preserves the parsed response payload — useful on 409 ACL conflicts, where the server returns structured `{added, removed, failed}` detail.
|
|
175
|
+
`ApiError#body` preserves the parsed response payload — useful on 409 ACL conflicts, where the server returns structured `{added, removed, failed}` detail (build a `Models::AclConflictData` from it to read the rows).
|
|
175
176
|
|
|
176
177
|
## ⏱️ Rate Limits
|
|
177
178
|
|
|
@@ -64,8 +64,8 @@ module VoiceTel
|
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
# Recursively transform a Ruby snake_case key Hash/Array structure into
|
|
67
|
-
# a Hash with camelCase keys, suitable for JSON serialization.
|
|
68
|
-
#
|
|
67
|
+
# a Hash with camelCase keys, suitable for JSON serialization. Data.define
|
|
68
|
+
# request models are converted to hashes; plain scalars pass through.
|
|
69
69
|
def camelize_keys(obj)
|
|
70
70
|
case obj
|
|
71
71
|
when Hash
|
|
@@ -74,6 +74,8 @@ module VoiceTel
|
|
|
74
74
|
end
|
|
75
75
|
when Array
|
|
76
76
|
obj.map { |v| camelize_keys(v) }
|
|
77
|
+
when Data
|
|
78
|
+
camelize_keys(obj.to_h)
|
|
77
79
|
else
|
|
78
80
|
obj
|
|
79
81
|
end
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module VoiceTel
|
|
6
|
+
module Models
|
|
7
|
+
# --------------------------------------------------------- shared subtypes ---
|
|
8
|
+
# Per-service rates on an account. Read-only for non-administrators.
|
|
9
|
+
AccountRates = Data.define(
|
|
10
|
+
:cnam, :intl_max, :nibble, :lrn, :fax, :tf_adj, :did, :mms, :sms
|
|
11
|
+
) do
|
|
12
|
+
extend Base
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Per-service feature flags on an account; true = enabled.
|
|
16
|
+
AccountServices = Data.define(
|
|
17
|
+
:e911, :cnam, :bypass_media, :intl, :mms, :dialer, :sms
|
|
18
|
+
) do
|
|
19
|
+
extend Base
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# A single credit row in AccountCreditsData.
|
|
23
|
+
CreditEntry = Data.define(:date, :paid, :amount) do
|
|
24
|
+
extend Base
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# A single payment row in AccountPaymentsData. status is one of
|
|
28
|
+
# "Completed", "Pending", "Reversed", "Refunded", "Failed", "Denied",
|
|
29
|
+
# or "Canceled_Reversal".
|
|
30
|
+
PaymentEntry = Data.define(:transaction_id, :date, :payer_email, :status, :amount) do
|
|
31
|
+
extend Base
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Per-call billing summary inside a CdrEntry. The numeric-looking fields
|
|
35
|
+
# (dur, ba, nr) are intentionally strings to preserve exact precision on
|
|
36
|
+
# the wire.
|
|
37
|
+
CdrEntryValue = Data.define(:dur, :dst, :ba, :nr, :cn, :ip, :cid) do
|
|
38
|
+
extend Base
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# A single CDR row. key is [accountUsername, startEpochUnixSeconds].
|
|
42
|
+
CdrEntry = Data.define(:id, :key, :value) do
|
|
43
|
+
extend Base
|
|
44
|
+
|
|
45
|
+
def self.nested
|
|
46
|
+
{ value: CdrEntryValue }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# A single monthly-recurring charge row in AccountMrcData.
|
|
51
|
+
MrcCharge = Data.define(:amount, :description) do
|
|
52
|
+
extend Base
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# ------------------------------------------------------------- requests ---
|
|
56
|
+
# Body for POST /v2.2/account — admin-only sub-account creation.
|
|
57
|
+
AccountAddRequest = Data.define(:username, :name, :email, :master_account) do
|
|
58
|
+
include Request
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Body for PUT /v2.2/account — partial account-settings update.
|
|
62
|
+
AccountPutRequest = Data.define(
|
|
63
|
+
:notify, :notify_threshold, :timezone, :caller_id, :e911, :intl, :sms, :mms, :ccs
|
|
64
|
+
) do
|
|
65
|
+
include Request
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Body for POST /v2.2/accounts — public sign-up.
|
|
69
|
+
AccountSignupRequest = Data.define(:name, :email, :promo) do
|
|
70
|
+
include Request
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Body for POST /v2.2/account/recovery — password recovery (no auth).
|
|
74
|
+
AccountRecoverRequest = Data.define(:email) do
|
|
75
|
+
include Request
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Body for POST /v2.2/account/api-key — username/password -> bearer.
|
|
79
|
+
ApiKeyRequest = Data.define(:username, :password) do
|
|
80
|
+
include Request
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# ------------------------------------------------------ response data ---
|
|
84
|
+
# Data payload from POST /v2.2/account (admin create).
|
|
85
|
+
AccountAddData = Data.define(:password, :email, :master_account, :username, :name) do
|
|
86
|
+
extend Base
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Data payload from POST /v2.2/account/api-key.
|
|
90
|
+
AccountApiKeyData = Data.define(:apikey) do
|
|
91
|
+
extend Base
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Data payload from GET /v2.2/account/cdr.
|
|
95
|
+
AccountCdrData = Data.define(:cdr, :start, :end_at) do
|
|
96
|
+
extend Base
|
|
97
|
+
|
|
98
|
+
def self.nested
|
|
99
|
+
{ cdr: CdrEntry }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def self.wire_keys
|
|
103
|
+
{ end_at: "end" }
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Data payload from GET /v2.2/account/credits.
|
|
108
|
+
AccountCreditsData = Data.define(:credits) do
|
|
109
|
+
extend Base
|
|
110
|
+
|
|
111
|
+
def self.nested
|
|
112
|
+
{ credits: CreditEntry }
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Data payload from GET /v2.2/account — the authenticated account's
|
|
117
|
+
# profile.
|
|
118
|
+
AccountData = Data.define(
|
|
119
|
+
:username, :name, :email, :enabled, :created, :cash, :caller_id, :timezone,
|
|
120
|
+
:auth_type, :ccs, :notify, :notify_threshold, :rates, :services
|
|
121
|
+
) do
|
|
122
|
+
extend Base
|
|
123
|
+
|
|
124
|
+
def self.nested
|
|
125
|
+
{ rates: AccountRates, services: AccountServices }
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Data payload from GET /v2.2/account/recurring-charges.
|
|
130
|
+
AccountMrcData = Data.define(:charges, :total) do
|
|
131
|
+
extend Base
|
|
132
|
+
|
|
133
|
+
def self.nested
|
|
134
|
+
{ charges: MrcCharge }
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Data payload from GET /v2.2/account/payments.
|
|
139
|
+
AccountPaymentsData = Data.define(:payments) do
|
|
140
|
+
extend Base
|
|
141
|
+
|
|
142
|
+
def self.nested
|
|
143
|
+
{ payments: PaymentEntry }
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Data payload from PUT /v2.2/account.
|
|
148
|
+
AccountPutData = Data.define(:updated) do
|
|
149
|
+
extend Base
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Data payload from POST /v2.2/account/recovery.
|
|
153
|
+
AccountRecoverData = Data.define(:message) do
|
|
154
|
+
extend Base
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Data payload from GET /v2.2/account/registration — current SIP
|
|
158
|
+
# registration.
|
|
159
|
+
AccountRegistrationData = Data.define(:agent, :uri, :expires) do
|
|
160
|
+
extend Base
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Data payload from POST /v2.2/accounts (public sign-up).
|
|
164
|
+
AccountSignupData = Data.define(:username, :name, :email, :password) do
|
|
165
|
+
extend Base
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "common"
|
|
5
|
+
|
|
6
|
+
module VoiceTel
|
|
7
|
+
module Models
|
|
8
|
+
# ------------------------------------------------------------- requests ---
|
|
9
|
+
# Body for POST /v2.2/acl (add) and DELETE /v2.2/acl (remove). Entries
|
|
10
|
+
# may be CidrEntry objects or plain { cidr: "..." } hashes.
|
|
11
|
+
AclModifyRequest = Data.define(:acl) do
|
|
12
|
+
include Request
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# ------------------------------------------------------ response data ---
|
|
16
|
+
# Data payload from GET /v2.2/acl.
|
|
17
|
+
AclListData = Data.define(:acl) do
|
|
18
|
+
extend Base
|
|
19
|
+
|
|
20
|
+
def self.nested
|
|
21
|
+
{ acl: CidrEntry }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Data payload from POST /v2.2/acl.
|
|
26
|
+
AclAddData = Data.define(:added) do
|
|
27
|
+
extend Base
|
|
28
|
+
|
|
29
|
+
def self.nested
|
|
30
|
+
{ added: CidrEntry }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Data payload from DELETE /v2.2/acl.
|
|
35
|
+
AclRemoveData = Data.define(:removed) do
|
|
36
|
+
extend Base
|
|
37
|
+
|
|
38
|
+
def self.nested
|
|
39
|
+
{ removed: CidrEntry }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# A CIDR that was rejected, with the reason. Appears in 409 bodies.
|
|
44
|
+
AclFailedEntry = Data.define(:cidr, :reason) do
|
|
45
|
+
extend Base
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Data payload included in a 409 response from POST/DELETE /v2.2/acl.
|
|
49
|
+
# Surfaces both the entries that succeeded and the ones that failed, so
|
|
50
|
+
# partial outcomes can be reconciled. Build one from ApiError#body.
|
|
51
|
+
AclConflictData = Data.define(:added, :removed, :failed) do
|
|
52
|
+
extend Base
|
|
53
|
+
|
|
54
|
+
def self.nested
|
|
55
|
+
{ added: CidrEntry, removed: CidrEntry, failed: AclFailedEntry }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "common"
|
|
5
|
+
|
|
6
|
+
module VoiceTel
|
|
7
|
+
module Models
|
|
8
|
+
# ------------------------------------------------------------- requests ---
|
|
9
|
+
# Body for PUT /v2.2/auth — update authentication mode and/or password.
|
|
10
|
+
# auth_type: 0=Digest, 1=Lax (Digest | IP), 2=IP, 3=Strict (Digest + IP).
|
|
11
|
+
AuthPutRequest = Data.define(:auth_type, :password) do
|
|
12
|
+
include Request
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# ------------------------------------------------------ response data ---
|
|
16
|
+
# Data payload from GET /v2.2/auth.
|
|
17
|
+
AuthGetData = Data.define(:auth_type, :auth_type_description, :acl) do
|
|
18
|
+
extend Base
|
|
19
|
+
|
|
20
|
+
# auth_type — 0=Digest, 1=Lax (Digest | IP), 2=IP, 3=Strict (Digest + IP).
|
|
21
|
+
def self.nested
|
|
22
|
+
{ acl: CidrEntry }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# One field's change record, returned by PUT /v2.2/auth. field is
|
|
27
|
+
# "authType" or "password"; value echoes the new setting when safe and
|
|
28
|
+
# is nil for password.
|
|
29
|
+
AuthUpdatedEntry = Data.define(:field, :value) do
|
|
30
|
+
extend Base
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Data payload from PUT /v2.2/auth.
|
|
34
|
+
AuthPutData = Data.define(:updated) do
|
|
35
|
+
extend Base
|
|
36
|
+
|
|
37
|
+
def self.nested
|
|
38
|
+
{ updated: AuthUpdatedEntry }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Data payload included in a 409 response from PUT /v2.2/auth — same
|
|
43
|
+
# shape as AuthPutData. Build one from ApiError#body.
|
|
44
|
+
AuthPutConflictData = Data.define(:updated) do
|
|
45
|
+
extend Base
|
|
46
|
+
|
|
47
|
+
def self.nested
|
|
48
|
+
{ updated: AuthUpdatedEntry }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module VoiceTel
|
|
4
|
+
# Response and request models, one file per resource. Every model is a
|
|
5
|
+
# Data.define class whose members are snake_case mirrors of the spec's
|
|
6
|
+
# camelCase wire fields. Base (class-side) builds responses, Request
|
|
7
|
+
# (instance-side) renders requests.
|
|
8
|
+
module Models
|
|
9
|
+
# Class-side plumbing for response models. .build turns an unwrapped API
|
|
10
|
+
# payload (a String-keyed, camelCase Hash) into a model instance:
|
|
11
|
+
#
|
|
12
|
+
# - Wire keys are mapped onto members by camelCasing the member name
|
|
13
|
+
# (campaign_id -> "campaignId"); .wire_keys overrides the exceptions
|
|
14
|
+
# (ticket_number -> "number").
|
|
15
|
+
# - Members declared in .nested are built recursively; a declared member
|
|
16
|
+
# accepts either a single object or a list of them on the wire.
|
|
17
|
+
# - Payload keys without a member are ignored, so the server can add
|
|
18
|
+
# fields without breaking this SDK; members absent from the payload
|
|
19
|
+
# are set to nil.
|
|
20
|
+
module Base
|
|
21
|
+
# Nested model classes by member — { job: MessagingEnablementJob }.
|
|
22
|
+
def nested
|
|
23
|
+
{}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Wire-key overrides for members whose spec name is not the camelCase
|
|
27
|
+
# form of the member — { ticket_number: "number" }.
|
|
28
|
+
def wire_keys
|
|
29
|
+
{}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Build a model from an unwrapped payload. nil builds to nil (204
|
|
33
|
+
# responses) and non-Hash payloads pass through untouched.
|
|
34
|
+
def build(payload)
|
|
35
|
+
return nil if payload.nil?
|
|
36
|
+
return payload unless payload.is_a?(Hash)
|
|
37
|
+
|
|
38
|
+
kwargs = {}
|
|
39
|
+
members.each do |member|
|
|
40
|
+
key = wire_keys.fetch(member) { Models.wire_key(member) }
|
|
41
|
+
value = payload.key?(key) ? convert(payload[key], nested[member]) : nil
|
|
42
|
+
kwargs[member] = value
|
|
43
|
+
end
|
|
44
|
+
new(**kwargs)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def convert(value, type)
|
|
50
|
+
return value if type.nil? || value.nil?
|
|
51
|
+
|
|
52
|
+
if value.is_a?(Array)
|
|
53
|
+
value.map { |item| item.is_a?(Hash) ? type.build(item) : item }
|
|
54
|
+
else
|
|
55
|
+
type.build(value)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Instance-side plumbing for request models. #to_payload renders the
|
|
61
|
+
# model as a plain snake_case Hash for the transport (which camelCases
|
|
62
|
+
# the keys on the wire); nil members are omitted so PATCH/PUT bodies keep
|
|
63
|
+
# "field absent" and "explicit value" distinguishable. There are no
|
|
64
|
+
# defaults — pass nil for anything you don't want to send.
|
|
65
|
+
module Request
|
|
66
|
+
def to_payload
|
|
67
|
+
self.class.members.each_with_object({}) do |member, out|
|
|
68
|
+
value = public_send(member)
|
|
69
|
+
out[member] = Models.plain(value) unless value.nil?
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
module_function
|
|
75
|
+
|
|
76
|
+
# camelCase wire key for a snake_case member.
|
|
77
|
+
def wire_key(member)
|
|
78
|
+
s = member.to_s
|
|
79
|
+
return s unless s.include?("_")
|
|
80
|
+
|
|
81
|
+
head, *rest = s.split("_")
|
|
82
|
+
head + rest.map(&:capitalize).join
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Recursively render nested request models as plain Ruby structures.
|
|
86
|
+
def plain(value)
|
|
87
|
+
case value
|
|
88
|
+
when Hash then value.transform_values { |v| plain(v) }
|
|
89
|
+
when Array then value.map { |v| plain(v) }
|
|
90
|
+
else value.respond_to?(:to_payload) ? value.to_payload : value
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module VoiceTel
|
|
6
|
+
module Models
|
|
7
|
+
# A single CIDR row used by the ACL endpoints. Masks must be /8, /16,
|
|
8
|
+
# /24, or /32 and must describe a routable public address.
|
|
9
|
+
CidrEntry = Data.define(:cidr) do
|
|
10
|
+
extend Base
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Generic error body returned for non-2xx responses. The SDK raises
|
|
14
|
+
# ApiError rather than returning this; the model exists so callers can
|
|
15
|
+
# build one from ApiError#body when it carries a JSON payload.
|
|
16
|
+
ErrorResponse = Data.define(:status, :code, :message, :error, :details) do
|
|
17
|
+
extend Base
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module VoiceTel
|
|
6
|
+
module Models
|
|
7
|
+
# Request bodies take a 10-digit TN in `dn`; responses return the
|
|
8
|
+
# 11-digit E.164 US form (leading 1). This mirrors the spec exactly.
|
|
9
|
+
|
|
10
|
+
# ------------------------------------------------------------- requests ---
|
|
11
|
+
# Body for POST /v2.2/e911/validations — validate an address.
|
|
12
|
+
E911AddressRequest = Data.define(:address1, :address2, :city, :state, :zip) do
|
|
13
|
+
include Request
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Body for POST /v2.2/e911 — validate and provision in one call.
|
|
17
|
+
E911CreateRequest = Data.define(
|
|
18
|
+
:dn, :callername, :address1, :address2, :city, :state, :zip
|
|
19
|
+
) do
|
|
20
|
+
include Request
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Body for PUT /v2.2/e911/{dn} — provision using a previously-validated
|
|
24
|
+
# addressid from POST /v2.2/e911/validations.
|
|
25
|
+
E911ProvisionByIdRequest = Data.define(:callername, :addressid) do
|
|
26
|
+
include Request
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------- entities & data ---
|
|
30
|
+
# An e911 record bound to a TN. Returned by GET/POST/PUT /v2.2/e911(/{dn}).
|
|
31
|
+
E911Entry = Data.define(
|
|
32
|
+
:dn, :callername, :address1, :address2, :city, :state, :zip
|
|
33
|
+
) do
|
|
34
|
+
extend Base
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Validation result from POST /v2.2/e911/validations. addressid is
|
|
38
|
+
# passed to E911ProvisionByIdRequest to provision the address.
|
|
39
|
+
E911ValidatedAddress = Data.define(
|
|
40
|
+
:addressid, :address1, :address2, :city, :state, :zip
|
|
41
|
+
) do
|
|
42
|
+
extend Base
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Data payload from GET /v2.2/e911.
|
|
46
|
+
E911AllData = Data.define(:records) do
|
|
47
|
+
extend Base
|
|
48
|
+
|
|
49
|
+
def self.nested
|
|
50
|
+
{ records: E911Entry }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Data payload from GET/POST/PUT /v2.2/e911/{dn}.
|
|
55
|
+
E911RecordData = Data.define(:record) do
|
|
56
|
+
extend Base
|
|
57
|
+
|
|
58
|
+
def self.nested
|
|
59
|
+
{ record: E911Entry }
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Data payload from POST /v2.2/e911/validations.
|
|
64
|
+
E911ValidateData = Data.define(:address) do
|
|
65
|
+
extend Base
|
|
66
|
+
|
|
67
|
+
def self.nested
|
|
68
|
+
{ address: E911ValidatedAddress }
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module VoiceTel
|
|
6
|
+
module Models
|
|
7
|
+
# ------------------------------------------------------------- requests ---
|
|
8
|
+
# Body for POST /v2.2/gateways.
|
|
9
|
+
GatewayAddRequest = Data.define(:gateway, :prefix, :limit) do
|
|
10
|
+
include Request
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Body for PUT /v2.2/gateways/{id}.
|
|
14
|
+
GatewayUpdateRequest = Data.define(:gateway, :prefix, :limit) do
|
|
15
|
+
include Request
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# -------------------------------------------------- entities & data ---
|
|
19
|
+
# A single gateway row. gateway holds an IP:port for customer gateways
|
|
20
|
+
# or a system route name (USER, T30, VOICEML, DID, T38, RTC_BRIDGE, SDN)
|
|
21
|
+
# for built-in routes; limit is nil on system routes.
|
|
22
|
+
GatewayEntry = Data.define(:id, :gateway, :prefix, :limit, :system) do
|
|
23
|
+
extend Base
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# One number bound to a gateway, as returned by
|
|
27
|
+
# GET /v2.2/gateways/{id}/numbers.
|
|
28
|
+
GatewayNumberSummary = Data.define(
|
|
29
|
+
:number, :translated, :forward, :forward_to, :cnam, :carrier, :sms_enabled, :fax_enabled
|
|
30
|
+
) do
|
|
31
|
+
extend Base
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Data payload from GET /v2.2/gateways.
|
|
35
|
+
GatewaysListData = Data.define(:gateways) do
|
|
36
|
+
extend Base
|
|
37
|
+
|
|
38
|
+
def self.nested
|
|
39
|
+
{ gateways: GatewayEntry }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Data payload from GET /v2.2/gateways/{id}/numbers.
|
|
44
|
+
GatewayNumbersData = Data.define(:numbers) do
|
|
45
|
+
extend Base
|
|
46
|
+
|
|
47
|
+
def self.nested
|
|
48
|
+
{ numbers: GatewayNumberSummary }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|