conversant 1.0.16
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 +7 -0
- data/.env.example +39 -0
- data/.gitignore +52 -0
- data/.gitlab-ci.yml +108 -0
- data/.rspec +3 -0
- data/.rubocop.yml +16 -0
- data/.yardopts +7 -0
- data/CHANGELOG.md +487 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +860 -0
- data/RELEASE.md +726 -0
- data/Rakefile +21 -0
- data/conversant.gemspec +49 -0
- data/examples/inheritance_integration.rb +348 -0
- data/examples/rails_initializer.rb +69 -0
- data/lib/conversant/configuration.rb +132 -0
- data/lib/conversant/v3/base.rb +47 -0
- data/lib/conversant/v3/http_client.rb +456 -0
- data/lib/conversant/v3/mixins/authentication.rb +221 -0
- data/lib/conversant/v3/services/authorization.rb +194 -0
- data/lib/conversant/v3/services/cdn/analytics.rb +483 -0
- data/lib/conversant/v3/services/cdn/audit.rb +71 -0
- data/lib/conversant/v3/services/cdn/business.rb +122 -0
- data/lib/conversant/v3/services/cdn/certificate.rb +180 -0
- data/lib/conversant/v3/services/cdn/dashboard.rb +109 -0
- data/lib/conversant/v3/services/cdn/domain.rb +223 -0
- data/lib/conversant/v3/services/cdn/monitoring.rb +65 -0
- data/lib/conversant/v3/services/cdn/partner/analytics.rb +233 -0
- data/lib/conversant/v3/services/cdn/partner.rb +60 -0
- data/lib/conversant/v3/services/cdn.rb +221 -0
- data/lib/conversant/v3/services/lms/dashboard.rb +99 -0
- data/lib/conversant/v3/services/lms/domain.rb +108 -0
- data/lib/conversant/v3/services/lms/job.rb +211 -0
- data/lib/conversant/v3/services/lms/partner/analytics.rb +266 -0
- data/lib/conversant/v3/services/lms/partner/business.rb +151 -0
- data/lib/conversant/v3/services/lms/partner/report.rb +170 -0
- data/lib/conversant/v3/services/lms/partner.rb +58 -0
- data/lib/conversant/v3/services/lms/preset.rb +57 -0
- data/lib/conversant/v3/services/lms.rb +173 -0
- data/lib/conversant/v3/services/oss/partner/analytics.rb +105 -0
- data/lib/conversant/v3/services/oss/partner.rb +48 -0
- data/lib/conversant/v3/services/oss.rb +128 -0
- data/lib/conversant/v3/services/portal/dashboard.rb +114 -0
- data/lib/conversant/v3/services/portal.rb +219 -0
- data/lib/conversant/v3/services/vms/analytics.rb +114 -0
- data/lib/conversant/v3/services/vms/business.rb +190 -0
- data/lib/conversant/v3/services/vms/partner/analytics.rb +133 -0
- data/lib/conversant/v3/services/vms/partner/business.rb +90 -0
- data/lib/conversant/v3/services/vms/partner.rb +57 -0
- data/lib/conversant/v3/services/vms/transcoding.rb +184 -0
- data/lib/conversant/v3/services/vms.rb +166 -0
- data/lib/conversant/v3.rb +36 -0
- data/lib/conversant/version.rb +5 -0
- data/lib/conversant.rb +108 -0
- data/publish.sh +107 -0
- data/sig/conversant/v3/services/authorization.rbs +34 -0
- data/sig/conversant/v3/services/cdn.rbs +123 -0
- data/sig/conversant/v3/services/lms.rbs +80 -0
- data/sig/conversant/v3/services/portal.rbs +22 -0
- data/sig/conversant/v3/services/vms.rbs +64 -0
- data/sig/conversant/v3.rbs +85 -0
- data/sig/conversant.rbs +37 -0
- metadata +267 -0
data/README.md
ADDED
|
@@ -0,0 +1,860 @@
|
|
|
1
|
+
# Conversant Ruby Gem
|
|
2
|
+
|
|
3
|
+
A Ruby client library for interacting with Conversant/SwiftFederation CDN services, providing clean interfaces for Portal, CDN, and LMS APIs with V3 authentication support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Zero Configuration** - Automatically uses existing CONVERSANT environment variables
|
|
8
|
+
- **V3 SSO Authentication** - Handles complex authentication flow with session management
|
|
9
|
+
- **Service-Oriented Architecture** - Separate classes for Portal, CDN, LMS, VMS, and OSS services
|
|
10
|
+
- **Clean Service Structure** - Each service organized with focused sub-services (analytics, domain, dashboard, etc.)
|
|
11
|
+
- **79 API Methods** - Complete coverage across 4 main services (CDN: 46, LMS: 19, VMS: 9, Portal: 5)
|
|
12
|
+
- **Comprehensive CDN Management** - Full suite of CDN operations including domains, certificates, analytics
|
|
13
|
+
- **Inheritance-Friendly** - Easy to extend with your custom business logic
|
|
14
|
+
- **Redis Session Caching** - Automatic session management with configurable TTL
|
|
15
|
+
- **Thread-Safe** - Proper cookie jar management for concurrent requests
|
|
16
|
+
- **Drop-in Replacement** - Works with existing `Utils::Conversant::V3::Private` classes
|
|
17
|
+
- **YARD Documentation** - Complete API documentation with examples
|
|
18
|
+
- **RBS Type Signatures** - Full type coverage for better IDE support
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
Add this line to your application's Gemfile:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
gem 'conversant'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
And then execute:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
$ bundle install
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or install it yourself as:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
$ gem install conversant
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
### Zero Configuration (If ENV Variables Already Set)
|
|
43
|
+
|
|
44
|
+
If you're already using the CONVERSANT system, the gem works immediately:
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
require 'conversant'
|
|
48
|
+
|
|
49
|
+
# No configuration needed! The gem automatically uses:
|
|
50
|
+
# - ENV['PRIVATE_CDN_ENDPOINT']
|
|
51
|
+
# - ENV['PRIVATE_LMS_ENDPOINT']
|
|
52
|
+
# - ENV['SWIFTSERVE_IDENTIFIER_ID']
|
|
53
|
+
# - ENV['SWIFTSERVE_IDENTIFIER_HASH']
|
|
54
|
+
# - All other CONVERSANT ENV variables
|
|
55
|
+
|
|
56
|
+
# Just start using it:
|
|
57
|
+
portal = Conversant::V3.portal(customer_id)
|
|
58
|
+
cdn = Conversant::V3.cdn(customer_id)
|
|
59
|
+
lms = Conversant::V3.lms(customer_id)
|
|
60
|
+
vms = Conversant::V3.vms(customer_id)
|
|
61
|
+
oss = Conversant::V3.oss(customer_id)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
### Environment Variables
|
|
67
|
+
|
|
68
|
+
The gem automatically uses these CONVERSANT environment variables:
|
|
69
|
+
|
|
70
|
+
| Variable | Description | Default | Required |
|
|
71
|
+
|------------------------------|---------------------------------|----------------------------------|----------|
|
|
72
|
+
| `PORTAL_ROOT_HOSTNAME` | Portal hostname | `console.swiftfederation.com` | No |
|
|
73
|
+
| `PORTAL_SSO_HOSTNAME` | SSO hostname | `sso.swiftfederation.com` | No |
|
|
74
|
+
| `PRIVATE_PORTAL_ENDPOINT` | Portal API endpoint | `https://{PORTAL_ROOT_HOSTNAME}` | No |
|
|
75
|
+
| `PRIVATE_SSO_ENDPOINT` | SSO endpoint | `https://{PORTAL_SSO_HOSTNAME}` | No |
|
|
76
|
+
| `PRIVATE_CDN_ENDPOINT` | CDN API endpoint | - | Yes |
|
|
77
|
+
| `PRIVATE_LMS_ENDPOINT` | LMS API endpoint | - | Yes |
|
|
78
|
+
| `PRIVATE_VMS_ENDPOINT` | VMS API endpoint | - | No |
|
|
79
|
+
| `PRIVATE_OSS_ENDPOINT` | OSS API endpoint | - | No |
|
|
80
|
+
| `CONVERSANT_CDN_API_URL` | CDN API URL for http_code | - | No |
|
|
81
|
+
| `SWIFTSERVE_IDENTIFIER_ID` | Authentication ID | - | Yes |
|
|
82
|
+
| `SWIFTSERVE_IDENTIFIER_HASH` | Authentication hash | - | Yes |
|
|
83
|
+
| `DEFAULT_UA` | User agent string | Mozilla/5.0... | No |
|
|
84
|
+
| `DEFAULT_CONTENT_TYPE` | Content type header | `application/json` | No |
|
|
85
|
+
| `CONVERSANT_DEBUG` | Enable debug logging | `false` | No |
|
|
86
|
+
| `CONVERSANT_CACHE_TTL` | Session cache TTL (seconds) | `1200` | No |
|
|
87
|
+
| `REDIS_URL` | Redis connection URL | - | No* |
|
|
88
|
+
| `RAILS_ENV` | Rails environment (affects SSL) | - | No |
|
|
89
|
+
|
|
90
|
+
*Redis is auto-detected from `$redis` global or `ENV['REDIS_URL']`.
|
|
91
|
+
|
|
92
|
+
### Auto-Configuration (With Validation)
|
|
93
|
+
|
|
94
|
+
To ensure all required variables are present:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
# In config/initializers/conversant.rb (Rails)
|
|
98
|
+
require 'conversant'
|
|
99
|
+
|
|
100
|
+
# Validate that all required ENV variables are set
|
|
101
|
+
Conversant.auto_configure!
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Manual Configuration
|
|
105
|
+
|
|
106
|
+
Override specific settings if needed:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
Conversant.configure do |config|
|
|
110
|
+
# Override only what you need
|
|
111
|
+
config.cache_ttl = 3600 # 1 hour instead of default 20 minutes
|
|
112
|
+
config.logger = Rails.logger
|
|
113
|
+
config.debug_mode = true
|
|
114
|
+
end
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Usage
|
|
118
|
+
|
|
119
|
+
### Basic Usage
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
require 'conversant'
|
|
123
|
+
|
|
124
|
+
# Initialize services
|
|
125
|
+
customer_id = 12345
|
|
126
|
+
cdn = Conversant::V3.cdn(customer_id)
|
|
127
|
+
lms = Conversant::V3.lms(customer_id)
|
|
128
|
+
vms = Conversant::V3.vms(customer_id)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### CDN Domain Management
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
# List all domains
|
|
135
|
+
domains = cdn.domain.all
|
|
136
|
+
domains.each do |domain|
|
|
137
|
+
puts "Domain: #{domain.name} (ID: #{domain.id})"
|
|
138
|
+
puts " Type: #{domain.businessScenarioType == 1 ? 'CDN' : 'Storage'}"
|
|
139
|
+
puts " Status: #{domain.status == 1 ? 'Active' : 'Inactive'}"
|
|
140
|
+
puts " Usage: #{domain.current_disk_usage / 1_073_741_824.0} GB"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Find specific domain
|
|
144
|
+
domain = cdn.domain.find_by(name: 'cdn.example.com')
|
|
145
|
+
if domain
|
|
146
|
+
puts "Found domain: #{domain.name} with ID: #{domain.id}"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Create new CDN domain
|
|
150
|
+
domain_id = cdn.domain.create({
|
|
151
|
+
businessScenarioType: 1,
|
|
152
|
+
name: "cdn.example.com",
|
|
153
|
+
origins: [{ url_prefix: "203.0.113.10" }],
|
|
154
|
+
redirect_http_to_https: true,
|
|
155
|
+
http2_enabled: true
|
|
156
|
+
})
|
|
157
|
+
puts "Created domain with ID: #{domain_id}" if domain_id
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### CDN Analytics & Reporting
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
# Get bandwidth usage for the current month
|
|
164
|
+
require 'date'
|
|
165
|
+
start_time = Date.today.beginning_of_month.strftime("%Y-%m-%dT00:00:00Z")
|
|
166
|
+
end_time = Date.today.end_of_month.strftime("%Y-%m-%dT23:59:59Z")
|
|
167
|
+
|
|
168
|
+
bandwidth = cdn.analytics.bandwidths({
|
|
169
|
+
startTime: start_time,
|
|
170
|
+
endTime: end_time,
|
|
171
|
+
pageSize: 1000
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
total_bandwidth = bandwidth.sum { |b| b['bandwidth'] || 0 }
|
|
175
|
+
puts "Total bandwidth this month: #{total_bandwidth / 1_099_511_627_776.0} TB"
|
|
176
|
+
|
|
177
|
+
# Get 95th percentile for billing
|
|
178
|
+
bandwidth_95th = cdn.business.bandwidth95th(year: 2025)
|
|
179
|
+
annual_95th = bandwidth_95th.map { |m| m['bandwidth_95th'] }.max
|
|
180
|
+
puts "Annual 95th percentile: #{annual_95th / 1_000_000.0} Mbps"
|
|
181
|
+
|
|
182
|
+
# Top URLs analysis
|
|
183
|
+
top_urls = cdn.analytics.top_urls({
|
|
184
|
+
startTime: start_time,
|
|
185
|
+
endTime: end_time,
|
|
186
|
+
limit: 10
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
puts "Top 10 URLs:"
|
|
190
|
+
top_urls.each_with_index do |url, i|
|
|
191
|
+
puts " #{i+1}. #{url['url']} - #{url['hits']} hits"
|
|
192
|
+
end
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Certificate Management
|
|
196
|
+
|
|
197
|
+
```ruby
|
|
198
|
+
# List all certificates
|
|
199
|
+
certificates = cdn.certificate.all
|
|
200
|
+
certificates.each do |cert|
|
|
201
|
+
puts "Certificate: #{cert['name']}"
|
|
202
|
+
puts " Status: #{cert['status']}"
|
|
203
|
+
puts " Expires: #{cert['expiry_date']}"
|
|
204
|
+
puts " Domains: #{cert['domains']&.join(', ')}"
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Deploy Let's Encrypt certificate
|
|
208
|
+
success = cdn.certificate.deploy_auto_certificate(12345, "example.com", {
|
|
209
|
+
enable: true,
|
|
210
|
+
domains: ["example.com", "www.example.com"],
|
|
211
|
+
validation_method: "http",
|
|
212
|
+
auto_renewal: true
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
if success
|
|
216
|
+
puts "Auto-certificate deployed successfully"
|
|
217
|
+
else
|
|
218
|
+
puts "Failed to deploy auto-certificate"
|
|
219
|
+
end
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Monitoring & Alerts
|
|
223
|
+
|
|
224
|
+
```ruby
|
|
225
|
+
# Monitor domain health
|
|
226
|
+
domain_list = cdn.monitoring.delivery_domain_list
|
|
227
|
+
domain_list.each do |domain|
|
|
228
|
+
if domain['status'] != 'healthy'
|
|
229
|
+
puts "Alert: Domain #{domain['name']} status is #{domain['status']}"
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Get detailed monitoring for specific domain
|
|
234
|
+
domain_detail = cdn.monitoring.delivery_domain_detail(domain_id)
|
|
235
|
+
if domain_detail
|
|
236
|
+
puts "Domain: #{domain_detail['name']}"
|
|
237
|
+
puts " Response Time: #{domain_detail['response_time']}ms"
|
|
238
|
+
puts " Cache Hit Rate: #{domain_detail['cache_hit_rate']}%"
|
|
239
|
+
puts " Error Rate: #{domain_detail['error_rate']}%"
|
|
240
|
+
end
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Audit Logging
|
|
244
|
+
|
|
245
|
+
```ruby
|
|
246
|
+
# Get audit logs for the last 24 hours
|
|
247
|
+
yesterday = (Time.now - 86400).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
248
|
+
now = Time.now.strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
249
|
+
|
|
250
|
+
audit_logs = cdn.audit.delivery_domain_operations({
|
|
251
|
+
startTime: yesterday,
|
|
252
|
+
endTime: now,
|
|
253
|
+
pageSize: 100
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
audit_logs.each do |log|
|
|
257
|
+
puts "[#{log['timestamp']}] #{log['action']} by #{log['user']}"
|
|
258
|
+
puts " Domain: #{log['domain_name']}"
|
|
259
|
+
puts " Details: #{log['details']}"
|
|
260
|
+
end
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Inheritance Pattern (Extend with Your Logic)
|
|
264
|
+
|
|
265
|
+
Create your own classes that inherit from the gem's services:
|
|
266
|
+
|
|
267
|
+
```ruby
|
|
268
|
+
module Utils
|
|
269
|
+
module Conversant
|
|
270
|
+
module V3
|
|
271
|
+
class CDN < ::Conversant::V3::Services::CDN
|
|
272
|
+
# Add your custom methods
|
|
273
|
+
def daily_summary(date = Date.today)
|
|
274
|
+
start_time = date.strftime("%Y-%m-%dT00:00:00Z")
|
|
275
|
+
end_time = date.strftime("%Y-%m-%dT23:59:59Z")
|
|
276
|
+
|
|
277
|
+
{
|
|
278
|
+
bandwidth: analytics.bandwidths({startTime: start_time, endTime: end_time}),
|
|
279
|
+
volumes: analytics.volumes({startTime: start_time, endTime: end_time}),
|
|
280
|
+
spots: monitoring.spots({startTime: start_time, endTime: end_time})
|
|
281
|
+
}
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Use your extended class
|
|
289
|
+
cdn = Utils::Conversant::V3::CDN.new(customer_id)
|
|
290
|
+
summary = cdn.daily_summary # Your custom method
|
|
291
|
+
bandwidth = cdn.analytics.bandwidths(payload) # Parent's method
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Integration with Existing Code
|
|
295
|
+
|
|
296
|
+
### Option 1: Drop-in Replacement for ConversantHttpClientV3
|
|
297
|
+
|
|
298
|
+
Replace your existing authentication module:
|
|
299
|
+
|
|
300
|
+
```ruby
|
|
301
|
+
# app/models/concerns/conversant_http_client_v3.rb
|
|
302
|
+
require 'conversant'
|
|
303
|
+
|
|
304
|
+
Conversant.auto_configure! unless Conversant.configured?
|
|
305
|
+
|
|
306
|
+
module ConversantHttpClientV3
|
|
307
|
+
def self.included(base)
|
|
308
|
+
base.class_eval do
|
|
309
|
+
include ::Conversant::V3::Mixins::Authentication
|
|
310
|
+
use_conversant_auth!
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def authenticate
|
|
315
|
+
conversant_authenticate
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def signature
|
|
319
|
+
sessions = authenticate
|
|
320
|
+
sessions && sessions[:session]
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Option 2: Update Only Authorization Headers
|
|
326
|
+
|
|
327
|
+
Keep your existing classes, just update the auth:
|
|
328
|
+
|
|
329
|
+
```ruby
|
|
330
|
+
class Utils::Conversant::V3::Private::Operation
|
|
331
|
+
def authorized_headers
|
|
332
|
+
# Only change this method - use gem's auth
|
|
333
|
+
::Conversant::V3.portal(@customer_id).send(:authorized_headers)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# All your existing methods remain unchanged
|
|
337
|
+
def appliances(gte = nil, lte = nil)
|
|
338
|
+
# Your existing implementation
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Option 3: Using the Mixin
|
|
344
|
+
|
|
345
|
+
For classes that can't change inheritance:
|
|
346
|
+
|
|
347
|
+
```ruby
|
|
348
|
+
class Utils::Conversant::V3::Private::Operation
|
|
349
|
+
include Conversant::V3::Mixins::Authentication
|
|
350
|
+
use_conversant_auth!
|
|
351
|
+
|
|
352
|
+
def initialize(customer_id, type = 2)
|
|
353
|
+
@customer_id = customer_id
|
|
354
|
+
@type = type
|
|
355
|
+
initialize_conversant_auth(customer_id, type)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def your_existing_method
|
|
359
|
+
headers = portal_authorized_headers # Use gem's auth
|
|
360
|
+
# Your existing logic...
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## API Reference
|
|
366
|
+
|
|
367
|
+
### Portal Service
|
|
368
|
+
|
|
369
|
+
```ruby
|
|
370
|
+
portal = Conversant::V3.portal(customer_id)
|
|
371
|
+
|
|
372
|
+
# Get appliances
|
|
373
|
+
appliances = portal.appliances(start_date, end_date)
|
|
374
|
+
# Returns: Array of appliance hashes with ip, hostname, deleted, pop, volume, federation
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### CDN Service
|
|
378
|
+
|
|
379
|
+
The CDN service provides comprehensive domain, certificate, analytics, and monitoring capabilities.
|
|
380
|
+
|
|
381
|
+
```ruby
|
|
382
|
+
cdn = Conversant::V3.cdn(customer_id)
|
|
383
|
+
|
|
384
|
+
# Domain Management
|
|
385
|
+
domains = cdn.domain.all # Get all domains
|
|
386
|
+
domain = cdn.domain.find(domain_id) # Find by ID
|
|
387
|
+
domain = cdn.domain.find_by(name: 'example.com') # Find by name
|
|
388
|
+
total_usage = cdn.domain.total_current_disk_usage # Storage usage in bytes
|
|
389
|
+
|
|
390
|
+
# Create new domain
|
|
391
|
+
domain_id = cdn.domain.create({
|
|
392
|
+
businessScenarioType: 1, # 1=CDN, 2=Storage
|
|
393
|
+
name: "cdn.example.com",
|
|
394
|
+
origins: [{ url_prefix: "203.0.113.10" }],
|
|
395
|
+
redirect_http_to_https: true,
|
|
396
|
+
http2_enabled: true,
|
|
397
|
+
streaming_service: false,
|
|
398
|
+
disabled: false
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
# Certificate Management
|
|
402
|
+
certificates = cdn.certificate.all # List all certificates
|
|
403
|
+
cert_info = cdn.certificate.auto_certificate_info(cert_id, domain_name)
|
|
404
|
+
|
|
405
|
+
# Deploy Let's Encrypt certificate
|
|
406
|
+
success = cdn.certificate.deploy_auto_certificate(cert_id, domain_name, {
|
|
407
|
+
enable: true,
|
|
408
|
+
domains: ["example.com", "www.example.com"],
|
|
409
|
+
validation_method: "http",
|
|
410
|
+
auto_renewal: true
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
# Analytics
|
|
414
|
+
bandwidth = cdn.analytics.bandwidths({
|
|
415
|
+
startTime: "2025-01-01T00:00:00Z",
|
|
416
|
+
endTime: "2025-01-31T23:59:59Z",
|
|
417
|
+
pageSize: 1000,
|
|
418
|
+
pageNumber: 0
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
hits = cdn.analytics.hits({
|
|
422
|
+
startTime: start_time,
|
|
423
|
+
endTime: end_time,
|
|
424
|
+
groupBy: "hour"
|
|
425
|
+
})
|
|
426
|
+
|
|
427
|
+
status_codes = cdn.analytics.status_codes({
|
|
428
|
+
startTime: start_time,
|
|
429
|
+
endTime: end_time,
|
|
430
|
+
statusCode: "200,404,500"
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
top_urls = cdn.analytics.top_urls({
|
|
434
|
+
startTime: start_time,
|
|
435
|
+
endTime: end_time,
|
|
436
|
+
limit: 100
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
# Business Metrics (Billing)
|
|
440
|
+
usage = cdn.business.bandwidth({
|
|
441
|
+
startTime: "2025-01-01T00:00:00Z",
|
|
442
|
+
endTime: "2025-01-31T23:59:59Z",
|
|
443
|
+
pageSize: 1000
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
# Get 95th percentile bandwidth
|
|
447
|
+
bandwidth_95th = cdn.business.bandwidth95th(year: 2025)
|
|
448
|
+
|
|
449
|
+
# Monitoring
|
|
450
|
+
domain_list = cdn.monitoring.delivery_domain_list
|
|
451
|
+
domain_detail = cdn.monitoring.delivery_domain_detail(domain_id)
|
|
452
|
+
customer_details = cdn.monitoring.customer_details
|
|
453
|
+
|
|
454
|
+
# Audit Logs
|
|
455
|
+
audit_logs = cdn.audit.delivery_domain_operations({
|
|
456
|
+
startTime: start_time,
|
|
457
|
+
endTime: end_time,
|
|
458
|
+
pageSize: 50
|
|
459
|
+
})
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### LMS Service (Live Media Streaming)
|
|
463
|
+
|
|
464
|
+
```ruby
|
|
465
|
+
lms = Conversant::V3.lms(customer_id)
|
|
466
|
+
|
|
467
|
+
# Job Management - Query streaming jobs
|
|
468
|
+
jobs = lms.job.where(
|
|
469
|
+
page_number: 1,
|
|
470
|
+
page_size: 50,
|
|
471
|
+
status: 'active'
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
jobs.each do |job|
|
|
475
|
+
puts "Job #{job['id']}: #{job['name']}"
|
|
476
|
+
puts " Status: #{job['status']}"
|
|
477
|
+
puts " Created: #{job['created_at']}"
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
# Domain Management
|
|
481
|
+
domains = lms.domain.all(page_number: 1, page_size: 50)
|
|
482
|
+
domains.each do |domain|
|
|
483
|
+
puts "Domain: #{domain['name']} (ID: #{domain['id']})"
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
# Get specific domain details
|
|
487
|
+
domain = lms.domain.show(domain_id)
|
|
488
|
+
|
|
489
|
+
# Create streaming domain
|
|
490
|
+
domain_id = lms.domain.create({
|
|
491
|
+
name: "stream.example.com",
|
|
492
|
+
type: "live",
|
|
493
|
+
protocol: "rtmp"
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
# Update streaming domain
|
|
497
|
+
lms.domain.update({
|
|
498
|
+
id: domain_id,
|
|
499
|
+
name: "updated-stream.example.com"
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
# Delete streaming domain
|
|
503
|
+
lms.domain.delete(domain_id)
|
|
504
|
+
|
|
505
|
+
# Dashboard Metrics
|
|
506
|
+
live_duration = lms.dashboard.live
|
|
507
|
+
vod_duration = lms.dashboard.vod
|
|
508
|
+
dvr_duration = lms.dashboard.dvr
|
|
509
|
+
recent_jobs = lms.dashboard.recent_jobs
|
|
510
|
+
|
|
511
|
+
# Transcoding Presets
|
|
512
|
+
presets = lms.preset.all
|
|
513
|
+
presets.each do |preset|
|
|
514
|
+
puts "Preset: #{preset['name']} - #{preset['resolution']}"
|
|
515
|
+
end
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### VMS Service (Video Management System)
|
|
519
|
+
|
|
520
|
+
```ruby
|
|
521
|
+
vms = Conversant::V3.vms(customer_id)
|
|
522
|
+
|
|
523
|
+
# Transcoding Jobs and Presets
|
|
524
|
+
jobs = vms.transcoding.jobs(page_size: 50, job_status: 2)
|
|
525
|
+
jobs.each do |job|
|
|
526
|
+
puts "Job #{job['id']}: #{job['file_name']} - Status: #{job['status']}"
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
# Get available transcoding presets
|
|
530
|
+
presets = vms.transcoding.presets(page_size: 50)
|
|
531
|
+
presets.each do |preset|
|
|
532
|
+
puts "Preset: #{preset['name']} - #{preset['description']}"
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
# Analytics - Transcoding duration and volume
|
|
536
|
+
duration = vms.analytics.transcoding({
|
|
537
|
+
startTime: "2025-01-01",
|
|
538
|
+
endTime: "2025-01-31"
|
|
539
|
+
})
|
|
540
|
+
|
|
541
|
+
volume = vms.analytics.volume({
|
|
542
|
+
startTime: "2025-01-01",
|
|
543
|
+
endTime: "2025-01-31"
|
|
544
|
+
})
|
|
545
|
+
|
|
546
|
+
# Business Metrics - Detailed transcoding breakdown
|
|
547
|
+
transcoding_breakdown = vms.business.transcoding({
|
|
548
|
+
startTime: Time.now.beginning_of_month
|
|
549
|
+
})
|
|
550
|
+
puts "SD: #{transcoding_breakdown[:vms_transcoding_sd]} minutes"
|
|
551
|
+
puts "HD: #{transcoding_breakdown[:vms_transcoding_hd]} minutes"
|
|
552
|
+
puts "UHD: #{transcoding_breakdown[:vms_transcoding_uhd]} minutes"
|
|
553
|
+
puts "Total: #{transcoding_breakdown[:vms_transcoding]} minutes"
|
|
554
|
+
|
|
555
|
+
# Duration and volume for billing
|
|
556
|
+
duration_metrics = vms.business.duration({
|
|
557
|
+
startTime: Time.now.beginning_of_month
|
|
558
|
+
})
|
|
559
|
+
|
|
560
|
+
volume_metrics = vms.business.volume({
|
|
561
|
+
startTime: Time.now.beginning_of_month
|
|
562
|
+
})
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
### OSS Service (Object Storage Service)
|
|
566
|
+
|
|
567
|
+
```ruby
|
|
568
|
+
oss = Conversant::V3.oss(customer_id)
|
|
569
|
+
|
|
570
|
+
# Bucket operations
|
|
571
|
+
buckets = oss.list_buckets
|
|
572
|
+
bucket = oss.get_bucket(bucket_name)
|
|
573
|
+
oss.create_bucket(bucket_name, region: 'us-east-1')
|
|
574
|
+
|
|
575
|
+
# Object operations
|
|
576
|
+
objects = oss.list_objects(bucket_name, prefix: 'videos/')
|
|
577
|
+
object = oss.get_object(bucket_name, object_key)
|
|
578
|
+
oss.put_object(bucket_name, object_key, file_content)
|
|
579
|
+
oss.delete_object(bucket_name, object_key)
|
|
580
|
+
|
|
581
|
+
# Presigned URLs
|
|
582
|
+
url = oss.presigned_url(bucket_name, object_key, expires_in: 3600)
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
## Error Handling
|
|
586
|
+
|
|
587
|
+
```ruby
|
|
588
|
+
begin
|
|
589
|
+
portal = Conversant::V3.portal(customer_id)
|
|
590
|
+
appliances = portal.appliances
|
|
591
|
+
rescue Conversant::AuthenticationError => e
|
|
592
|
+
# Handle authentication failures
|
|
593
|
+
puts "Authentication failed: #{e.message}"
|
|
594
|
+
rescue Conversant::ApiError => e
|
|
595
|
+
# Handle API errors
|
|
596
|
+
puts "API error: #{e.message}"
|
|
597
|
+
rescue Conversant::Error => e
|
|
598
|
+
# Handle general errors
|
|
599
|
+
puts "Error: #{e.message}"
|
|
600
|
+
end
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
## Migration Guide
|
|
604
|
+
|
|
605
|
+
### From Existing Utils::Conversant::V3::Private
|
|
606
|
+
|
|
607
|
+
#### Phase 1: Authentication Only
|
|
608
|
+
1. Add `gem 'conversant'` to Gemfile
|
|
609
|
+
2. Replace `ConversantHttpClientV3` module with gem's wrapper
|
|
610
|
+
3. Keep all business logic unchanged
|
|
611
|
+
|
|
612
|
+
#### Phase 2: Gradual Migration (Optional)
|
|
613
|
+
- Migrate services one by one
|
|
614
|
+
- Start with least critical service
|
|
615
|
+
- Keep custom business logic where needed
|
|
616
|
+
|
|
617
|
+
#### Phase 3: Full Integration (Optional)
|
|
618
|
+
- Use inheritance pattern for all services
|
|
619
|
+
- Add custom methods as needed
|
|
620
|
+
|
|
621
|
+
### Rollback Plan
|
|
622
|
+
|
|
623
|
+
If you need to rollback:
|
|
624
|
+
1. Restore original `ConversantHttpClientV3` module
|
|
625
|
+
2. Remove gem from Gemfile
|
|
626
|
+
3. No changes needed to business logic
|
|
627
|
+
|
|
628
|
+
## Authentication Architecture
|
|
629
|
+
|
|
630
|
+
The gem implements a multi-tier authentication system with automatic session management using a consistent Authorization module pattern across all services.
|
|
631
|
+
|
|
632
|
+
### Authentication Flow
|
|
633
|
+
|
|
634
|
+
1. **Root Authentication**:
|
|
635
|
+
- Logs into SSO with master credentials (SWIFTSERVE_IDENTIFIER_ID/HASH)
|
|
636
|
+
- Obtains SESSION and SSO_GW_SESSION2 cookies
|
|
637
|
+
- Cached in Redis with TTL management
|
|
638
|
+
|
|
639
|
+
2. **Service-Specific Sessions**:
|
|
640
|
+
All services follow the same authentication pattern via the Authorization module:
|
|
641
|
+
- **Portal**: Exchanges root sessions for customer-specific SESSION cookie
|
|
642
|
+
- **CDN**: Exchanges root sessions for customer-specific SESSION cookie
|
|
643
|
+
- **LMS**: Exchanges root sessions for Java-based JSESSIONID cookie
|
|
644
|
+
- **VMS**: Exchanges root sessions for Java-based JSESSIONID cookie
|
|
645
|
+
- **OSS**: Uses root sessions with S3-compatible authentication
|
|
646
|
+
|
|
647
|
+
3. **Authorization Module Pattern**:
|
|
648
|
+
Each service implements the Authorization module which provides:
|
|
649
|
+
- Automatic session fetching and caching
|
|
650
|
+
- Session validation before API calls
|
|
651
|
+
- Consistent error handling
|
|
652
|
+
- Redis-based session storage
|
|
653
|
+
|
|
654
|
+
### Session Caching
|
|
655
|
+
|
|
656
|
+
All sessions are cached in Redis with automatic refresh using a standardized key format:
|
|
657
|
+
|
|
658
|
+
```ruby
|
|
659
|
+
# Sessions are cached with these keys:
|
|
660
|
+
"CONVERSANT.V3.PORTAL.SESSION.{customer_id}" # Portal SESSION cookie
|
|
661
|
+
"CONVERSANT.V3.PORTAL.SSO_GW_SESSION2.{customer_id}" # SSO session (shared)
|
|
662
|
+
"CONVERSANT.V3.CDN.SESSION.{customer_id}" # CDN SESSION cookie
|
|
663
|
+
"CONVERSANT.V3.LMS.JSESSIONID.{customer_id}" # LMS JSESSIONID cookie
|
|
664
|
+
"CONVERSANT.V3.VMS.JSESSIONID.{customer_id}" # VMS JSESSIONID cookie
|
|
665
|
+
|
|
666
|
+
# Default TTL: 20 minutes (configurable)
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
### Session Validation
|
|
670
|
+
|
|
671
|
+
All services automatically validate session cookies before making API calls:
|
|
672
|
+
|
|
673
|
+
```ruby
|
|
674
|
+
# Each service checks for its required cookie before making requests:
|
|
675
|
+
# - Portal, CDN: Validates SESSION cookie presence
|
|
676
|
+
# - LMS, VMS: Validates JSESSIONID cookie presence
|
|
677
|
+
# - If missing, automatically fetches new session
|
|
678
|
+
# - If fetch fails, raises AuthenticationError
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### Using Custom Authentication
|
|
682
|
+
|
|
683
|
+
If you need access to raw authentication headers:
|
|
684
|
+
|
|
685
|
+
```ruby
|
|
686
|
+
# Get authorized headers for direct API calls
|
|
687
|
+
cdn = Conversant::V3.cdn(customer_id)
|
|
688
|
+
headers = cdn.send(:authorized_headers)
|
|
689
|
+
|
|
690
|
+
# Make custom API call
|
|
691
|
+
response = RestClient.get("#{cdn_endpoint}/custom-endpoint", headers)
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
## Development
|
|
695
|
+
|
|
696
|
+
See [DEVELOPER_GUIDE.md](DEVELOPER_GUIDE.md) for detailed information on:
|
|
697
|
+
- Building and testing the gem
|
|
698
|
+
- Publishing to RubyGems
|
|
699
|
+
- Version management
|
|
700
|
+
- Documentation generation
|
|
701
|
+
- GitLab CI/CD setup
|
|
702
|
+
- Troubleshooting
|
|
703
|
+
|
|
704
|
+
Quick start:
|
|
705
|
+
```bash
|
|
706
|
+
bundle install # Install dependencies
|
|
707
|
+
bundle exec rspec # Run tests
|
|
708
|
+
bundle exec yard doc # Generate documentation
|
|
709
|
+
./publish.sh # Publish to RubyGems
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
## Debugging
|
|
713
|
+
|
|
714
|
+
Enable debug mode for verbose logging:
|
|
715
|
+
|
|
716
|
+
```ruby
|
|
717
|
+
Conversant.configure do |config|
|
|
718
|
+
config.debug_mode = true
|
|
719
|
+
config.logger = Logger.new($stdout)
|
|
720
|
+
end
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
## Examples
|
|
724
|
+
|
|
725
|
+
See the `examples/` directory for:
|
|
726
|
+
- `inheritance_integration.rb` - Complete inheritance patterns
|
|
727
|
+
- `rails_initializer.rb` - Rails setup example
|
|
728
|
+
|
|
729
|
+
## Troubleshooting
|
|
730
|
+
|
|
731
|
+
### Common Issues
|
|
732
|
+
|
|
733
|
+
#### Authentication Errors
|
|
734
|
+
```ruby
|
|
735
|
+
# Error: Conversant::AuthenticationError
|
|
736
|
+
# Solution: Check environment variables
|
|
737
|
+
ENV['SWIFTSERVE_IDENTIFIER_ID'] # Must be set
|
|
738
|
+
ENV['SWIFTSERVE_IDENTIFIER_HASH'] # Must be set
|
|
739
|
+
ENV['PRIVATE_CDN_ENDPOINT'] # Must be set
|
|
740
|
+
ENV['PRIVATE_LMS_ENDPOINT'] # Must be set
|
|
741
|
+
|
|
742
|
+
# Clear cache if credentials changed
|
|
743
|
+
redis.del("CONVERSANT.V3.PORTAL.SESSION.#{customer_id}")
|
|
744
|
+
redis.del("CONVERSANT.V3.PORTAL.SSO_GW_SESSION2.#{customer_id}")
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
#### Redis Connection Issues
|
|
748
|
+
```ruby
|
|
749
|
+
# Error: Redis::CannotConnectError
|
|
750
|
+
# Solution: Configure Redis properly
|
|
751
|
+
Conversant.configure do |config|
|
|
752
|
+
config.redis = Redis.new(url: ENV['REDIS_URL'])
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
# Or use global $redis
|
|
756
|
+
$redis = Redis.new(url: ENV['REDIS_URL'])
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
#### Domain Creation Failures
|
|
760
|
+
```ruby
|
|
761
|
+
# Common issues:
|
|
762
|
+
# 1. Domain name already exists
|
|
763
|
+
# 2. Invalid origin configuration
|
|
764
|
+
# 3. Missing FTP password for storage domains (businessScenarioType: 2)
|
|
765
|
+
|
|
766
|
+
# Check response for details:
|
|
767
|
+
domain_id = cdn.domain.create(payload)
|
|
768
|
+
if domain_id.nil?
|
|
769
|
+
# Check logs for error details
|
|
770
|
+
# Enable debug mode for more info
|
|
771
|
+
Conversant.configure { |c| c.debug_mode = true }
|
|
772
|
+
end
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
#### Certificate Deployment Issues
|
|
776
|
+
```ruby
|
|
777
|
+
# Let's Encrypt validation failures:
|
|
778
|
+
# 1. Domain must be publicly accessible
|
|
779
|
+
# 2. HTTP validation requires port 80 open
|
|
780
|
+
# 3. DNS validation requires DNS control
|
|
781
|
+
|
|
782
|
+
# Check certificate status:
|
|
783
|
+
cert_info = cdn.certificate.auto_certificate_info(cert_id, domain)
|
|
784
|
+
puts cert_info['validation_status'] # Should be 'valid'
|
|
785
|
+
puts cert_info['error_message'] # If failed
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
### Debug Mode
|
|
789
|
+
|
|
790
|
+
Enable detailed logging for troubleshooting:
|
|
791
|
+
|
|
792
|
+
```ruby
|
|
793
|
+
Conversant.configure do |config|
|
|
794
|
+
config.debug_mode = true
|
|
795
|
+
config.logger = Logger.new($stdout)
|
|
796
|
+
config.log_level = :debug
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
# This will log:
|
|
800
|
+
# - All API requests and responses
|
|
801
|
+
# - Authentication attempts
|
|
802
|
+
# - Session cache hits/misses
|
|
803
|
+
# - Error details with stack traces
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
### Performance Tips
|
|
807
|
+
|
|
808
|
+
1. **Use Redis connection pooling**:
|
|
809
|
+
```ruby
|
|
810
|
+
require 'connection_pool'
|
|
811
|
+
|
|
812
|
+
redis_pool = ConnectionPool.new(size: 5) { Redis.new }
|
|
813
|
+
Conversant.configure do |config|
|
|
814
|
+
config.redis = redis_pool
|
|
815
|
+
end
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
2. **Batch API calls when possible**:
|
|
819
|
+
```ruby
|
|
820
|
+
# Instead of multiple calls:
|
|
821
|
+
domains.each { |d| cdn.domain.find(d.id) }
|
|
822
|
+
|
|
823
|
+
# Use single call with filtering:
|
|
824
|
+
all_domains = cdn.domain.all
|
|
825
|
+
my_domains = all_domains.select { |d| domain_ids.include?(d.id) }
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
3. **Increase cache TTL for stable data**:
|
|
829
|
+
```ruby
|
|
830
|
+
Conversant.configure do |config|
|
|
831
|
+
config.cache_ttl = 3600 # 1 hour for stable environments
|
|
832
|
+
end
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
## Version Notes
|
|
836
|
+
|
|
837
|
+
### Latest Release: 1.0.10 (2025-09-30)
|
|
838
|
+
|
|
839
|
+
**Documentation Update**: Enhanced documentation for v1.0.9 critical fixes
|
|
840
|
+
|
|
841
|
+
### Version 1.0.9 - Critical Bug Fix
|
|
842
|
+
|
|
843
|
+
**Fixed authentication issues with LMS and VMS services:**
|
|
844
|
+
|
|
845
|
+
- ✅ Fixed JSESSIONID authentication for LMS service (resolves "Missing JSESSIONID" error)
|
|
846
|
+
- ✅ Fixed JSESSIONID authentication for VMS service
|
|
847
|
+
- ✅ Improved RestClient cookie handling (switched from manual `'Cookie'` header to `:cookies` option)
|
|
848
|
+
- ✅ Fixed method visibility issues in Authorization module
|
|
849
|
+
|
|
850
|
+
**Upgrade recommended** if you're using LMS or VMS services. CDN and Portal services are unaffected.
|
|
851
|
+
|
|
852
|
+
See [CHANGELOG.md](CHANGELOG.md) for complete version history.
|
|
853
|
+
|
|
854
|
+
## Contributing
|
|
855
|
+
|
|
856
|
+
Bug reports and pull requests are welcome on GitLab at https://gitlab.vnetwork.dev/gems/conversant.
|
|
857
|
+
|
|
858
|
+
## License
|
|
859
|
+
|
|
860
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|