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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.env.example +39 -0
  3. data/.gitignore +52 -0
  4. data/.gitlab-ci.yml +108 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +16 -0
  7. data/.yardopts +7 -0
  8. data/CHANGELOG.md +487 -0
  9. data/Gemfile +12 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +860 -0
  12. data/RELEASE.md +726 -0
  13. data/Rakefile +21 -0
  14. data/conversant.gemspec +49 -0
  15. data/examples/inheritance_integration.rb +348 -0
  16. data/examples/rails_initializer.rb +69 -0
  17. data/lib/conversant/configuration.rb +132 -0
  18. data/lib/conversant/v3/base.rb +47 -0
  19. data/lib/conversant/v3/http_client.rb +456 -0
  20. data/lib/conversant/v3/mixins/authentication.rb +221 -0
  21. data/lib/conversant/v3/services/authorization.rb +194 -0
  22. data/lib/conversant/v3/services/cdn/analytics.rb +483 -0
  23. data/lib/conversant/v3/services/cdn/audit.rb +71 -0
  24. data/lib/conversant/v3/services/cdn/business.rb +122 -0
  25. data/lib/conversant/v3/services/cdn/certificate.rb +180 -0
  26. data/lib/conversant/v3/services/cdn/dashboard.rb +109 -0
  27. data/lib/conversant/v3/services/cdn/domain.rb +223 -0
  28. data/lib/conversant/v3/services/cdn/monitoring.rb +65 -0
  29. data/lib/conversant/v3/services/cdn/partner/analytics.rb +233 -0
  30. data/lib/conversant/v3/services/cdn/partner.rb +60 -0
  31. data/lib/conversant/v3/services/cdn.rb +221 -0
  32. data/lib/conversant/v3/services/lms/dashboard.rb +99 -0
  33. data/lib/conversant/v3/services/lms/domain.rb +108 -0
  34. data/lib/conversant/v3/services/lms/job.rb +211 -0
  35. data/lib/conversant/v3/services/lms/partner/analytics.rb +266 -0
  36. data/lib/conversant/v3/services/lms/partner/business.rb +151 -0
  37. data/lib/conversant/v3/services/lms/partner/report.rb +170 -0
  38. data/lib/conversant/v3/services/lms/partner.rb +58 -0
  39. data/lib/conversant/v3/services/lms/preset.rb +57 -0
  40. data/lib/conversant/v3/services/lms.rb +173 -0
  41. data/lib/conversant/v3/services/oss/partner/analytics.rb +105 -0
  42. data/lib/conversant/v3/services/oss/partner.rb +48 -0
  43. data/lib/conversant/v3/services/oss.rb +128 -0
  44. data/lib/conversant/v3/services/portal/dashboard.rb +114 -0
  45. data/lib/conversant/v3/services/portal.rb +219 -0
  46. data/lib/conversant/v3/services/vms/analytics.rb +114 -0
  47. data/lib/conversant/v3/services/vms/business.rb +190 -0
  48. data/lib/conversant/v3/services/vms/partner/analytics.rb +133 -0
  49. data/lib/conversant/v3/services/vms/partner/business.rb +90 -0
  50. data/lib/conversant/v3/services/vms/partner.rb +57 -0
  51. data/lib/conversant/v3/services/vms/transcoding.rb +184 -0
  52. data/lib/conversant/v3/services/vms.rb +166 -0
  53. data/lib/conversant/v3.rb +36 -0
  54. data/lib/conversant/version.rb +5 -0
  55. data/lib/conversant.rb +108 -0
  56. data/publish.sh +107 -0
  57. data/sig/conversant/v3/services/authorization.rbs +34 -0
  58. data/sig/conversant/v3/services/cdn.rbs +123 -0
  59. data/sig/conversant/v3/services/lms.rbs +80 -0
  60. data/sig/conversant/v3/services/portal.rbs +22 -0
  61. data/sig/conversant/v3/services/vms.rbs +64 -0
  62. data/sig/conversant/v3.rbs +85 -0
  63. data/sig/conversant.rbs +37 -0
  64. metadata +267 -0
@@ -0,0 +1,219 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'authorization'
4
+
5
+ module Conversant
6
+ module V3
7
+ module Services
8
+ # Portal service for Conversant/SwiftFederation administration
9
+ #
10
+ # Provides access to portal functionality including appliance management
11
+ # and infrastructure monitoring.
12
+ #
13
+ # @example Basic usage
14
+ # portal = Conversant::V3.portal(12345)
15
+ #
16
+ # # Get appliances for current month
17
+ # appliances = portal.appliances
18
+ #
19
+ # # Get appliances for specific date range
20
+ # appliances = portal.appliances('2025-01-01T00:00:00Z', '2025-01-31T23:59:59Z')
21
+ #
22
+ # @since 1.0.0
23
+ class Portal < ::Conversant::V3::Base
24
+ include Authorization
25
+
26
+ # Redis key prefix for caching appliance data
27
+ MASTER_APPLIANCE_REDIS_KEY = 'CONVERSANT.V3.PORTAL.APPLIANCE.ITEMS'
28
+
29
+ # Retrieves appliance data for a specified date range
30
+ #
31
+ # Returns information about appliances (CDN nodes) including their IP addresses,
32
+ # hostnames, locations (POP), traffic volume, and federation volume. Results are
33
+ # cached in Redis for 10 minutes to improve performance.
34
+ #
35
+ # @param gte [String, nil] Start date/time in ISO 8601 format (defaults to beginning of current month)
36
+ # @param lte [String, nil] End date/time in ISO 8601 format (defaults to end of current month)
37
+ #
38
+ # @return [Array<Hash>] Array of appliance hashes with keys:
39
+ # - :ip [String] IP address of the appliance
40
+ # - :hostname [String] Hostname of the appliance
41
+ # - :deleted [Boolean] Whether the appliance has been deleted
42
+ # - :pop [String] Point of Presence (datacenter location)
43
+ # - :volume [Integer] Traffic volume in bytes
44
+ # - :federation [Integer] Federation traffic volume in bytes
45
+ #
46
+ # @example Get appliances for current month
47
+ # appliances = portal.appliances
48
+ # appliances.each do |appliance|
49
+ # puts "#{appliance[:hostname]} (#{appliance[:ip]}) - #{appliance[:pop]}"
50
+ # puts " Volume: #{appliance[:volume]} bytes"
51
+ # puts " Federation: #{appliance[:federation]} bytes"
52
+ # end
53
+ #
54
+ # @example Get appliances for specific date range
55
+ # start_date = '2025-01-01T00:00:00Z'
56
+ # end_date = '2025-01-31T23:59:59Z'
57
+ # appliances = portal.appliances(start_date, end_date)
58
+ #
59
+ # @since 1.0.0
60
+ def appliances(gte = nil, lte = nil)
61
+ today = Date.today.to_datetime
62
+
63
+ gte ||= today.beginning_of_month.strftime('%Y-%m-%dT00:00:00Z')
64
+ lte ||= today.end_of_month.strftime('%Y-%m-%dT23:59:59Z')
65
+
66
+ key = "#{MASTER_APPLIANCE_REDIS_KEY}.#{gte.to_datetime&.strftime('%Y%m')}"
67
+
68
+ items = redis.get(key)
69
+
70
+ if items.nil?
71
+ logger.debug "#{identifier}.METHOD:appliances.FETCHING_DATA"
72
+
73
+ payload = {
74
+ startTime: gte,
75
+ endTime: lte
76
+ }
77
+
78
+ response = JSON.parse(call('POST', '/load_appliance_data', payload))
79
+
80
+ items = response&.[]('applianceList')&.map do |item|
81
+ {
82
+ ip: item['ip'],
83
+ hostname: item['hostname'],
84
+ deleted: item['deleted'],
85
+ pop: item['pop'],
86
+ volume: item['volume'],
87
+ federation: item['federationVolume'],
88
+ }
89
+ end.to_json
90
+
91
+ redis.set(key, items, ex: 600)
92
+ end
93
+
94
+ JSON.parse(items)
95
+ rescue StandardError => e
96
+ logger.error "#{identifier}.METHOD:appliances.ERROR:#{e.message}"
97
+ []
98
+ end
99
+
100
+ # Get dashboard service instance
101
+ #
102
+ # @return [Dashboard] dashboard service for customer metadata and reporting
103
+ # @since 1.0.8
104
+ def dashboard
105
+ @dashboard ||= Dashboard.new(self)
106
+ end
107
+
108
+ protected
109
+
110
+ # Makes an authenticated API call to the Portal service
111
+ #
112
+ # Validates SESSION cookie presence before making the request and handles
113
+ # HTTP errors appropriately.
114
+ #
115
+ # @param method [String] HTTP method ('GET', 'POST', etc.)
116
+ # @param uri [String] API endpoint URI (relative to portal_endpoint)
117
+ # @param payload [Hash, nil] Request payload for POST/PUT requests
118
+ #
119
+ # @return [String] Response body
120
+ #
121
+ # @raise [AuthenticationError] if SESSION cookie is missing
122
+ # @raise [ApiError] if HTTP response code is 400 or higher
123
+ def call(method, uri, payload = nil)
124
+ url = "#{portal_endpoint}#{uri}"
125
+
126
+ headers = authorized_headers
127
+
128
+ # Check for SESSION in Cookie header
129
+ if headers['Cookie'].nil? || !headers['Cookie'].include?('SESSION=')
130
+ logger.error "#{identifier}.METHOD:call.NO_SESSION"
131
+ raise AuthenticationError, 'Missing SESSION for Portal'
132
+ end
133
+
134
+ code, response = request(method, url, payload, headers)
135
+
136
+ if code >= 400
137
+ logger.error "#{identifier}.METHOD:call.HTTP_ERROR:#{code}"
138
+ raise ApiError, "Portal API error: #{code}"
139
+ end
140
+
141
+ response.body
142
+ end
143
+
144
+ private
145
+
146
+ # Fetches a new SESSION cookie for Portal API access
147
+ #
148
+ # Authenticates with root portal sessions (SESSION + SSO_GW_SESSION2) and
149
+ # exchanges them for a customer-specific SESSION cookie. The new session
150
+ # is cached in Redis with the configured TTL.
151
+ #
152
+ # @return [String, nil] The SESSION cookie value, or nil if authentication fails
153
+ def fetch_new_session
154
+ sessions = authenticate
155
+ return nil unless sessions && sessions[:session] && sessions[:sso_gw_session2]
156
+
157
+ logger.debug "#{identifier}.METHOD:authorize.REQUESTING_SESSION"
158
+
159
+ response = RestClient.get(
160
+ "#{portal_endpoint}/?cId=#{customer_id}",
161
+ {
162
+ authority: URI.parse(portal_endpoint).hostname,
163
+ referer: portal_endpoint,
164
+ user_agent: configuration.default_ua,
165
+ cookies: {
166
+ 'SESSION': sessions[:session],
167
+ 'SSO_GW_SESSION2': sessions[:sso_gw_session2]
168
+ },
169
+ timeout: 30,
170
+ open_timeout: 30
171
+ }
172
+ )
173
+
174
+ if response.cookies['SESSION']
175
+ session_cookie = response.cookies['SESSION']
176
+ redis.set(session_cache_key, session_cookie, ex: configuration.cache_ttl)
177
+ logger.debug "#{identifier}.METHOD:authorize.SESSION_OBTAINED"
178
+ session_cookie
179
+ else
180
+ logger.error "#{identifier}.METHOD:authorize.NO_SESSION_IN_RESPONSE"
181
+ nil
182
+ end
183
+ rescue RestClient::Unauthorized, RestClient::Forbidden => e
184
+ logger.error "#{identifier}.METHOD:authorize.AUTH_ERROR:#{e.message}"
185
+ nil
186
+ rescue StandardError => e
187
+ logger.error "#{identifier}.METHOD:authorize.ERROR:#{e.message}"
188
+ nil
189
+ end
190
+
191
+ protected
192
+
193
+ # Returns the Portal service endpoint URL
194
+ #
195
+ # @return [String] The portal endpoint URL
196
+ def service_endpoint
197
+ portal_endpoint
198
+ end
199
+
200
+ # Returns the session cookie name used by Portal service
201
+ #
202
+ # @return [String] The cookie name ('SESSION')
203
+ def session_cookie_name
204
+ 'SESSION'
205
+ end
206
+
207
+ # Indicates whether Portal service requires a session cookie
208
+ #
209
+ # @return [Boolean] true (Portal requires SESSION cookie)
210
+ def requires_session?
211
+ true
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
217
+
218
+ # Load nested service classes after Portal is defined
219
+ require_relative 'portal/dashboard'
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conversant
4
+ module V3
5
+ module Services
6
+ class VMS
7
+ # Analytics service for VMS metrics
8
+ #
9
+ # Provides analytics data for video transcoding operations
10
+ # including duration and volume metrics for reporting and
11
+ # capacity planning.
12
+ #
13
+ # @example Get transcoding analytics
14
+ # vms = Conversant::V3.vms(12345)
15
+ #
16
+ # # Get transcoding duration metrics
17
+ # duration = vms.analytics.transcoding({
18
+ # startTime: "2025-01-01",
19
+ # endTime: "2025-01-31"
20
+ # })
21
+ #
22
+ # # Get volume metrics
23
+ # volume = vms.analytics.volume({
24
+ # startTime: "2025-01-01",
25
+ # endTime: "2025-01-31"
26
+ # })
27
+ #
28
+ # @since 1.0.0
29
+ class Analytics
30
+ # @return [VMS] the parent VMS service instance
31
+ attr_reader :parent
32
+
33
+ # Initialize analytics service
34
+ #
35
+ # @param parent [VMS] the parent VMS service instance
36
+ def initialize(parent)
37
+ @parent = parent
38
+ end
39
+
40
+ # Get transcoding duration analytics
41
+ #
42
+ # Retrieves duration metrics for video transcoding operations
43
+ # over a specified time range. Useful for capacity planning
44
+ # and billing calculations.
45
+ #
46
+ # @param payload [Hash] query parameters including time range
47
+ # @option payload [String] :startTime start date/time
48
+ # @option payload [String] :endTime end date/time
49
+ #
50
+ # @return [Array] transcoding duration metrics, or empty array on error
51
+ #
52
+ # @example Get monthly transcoding duration
53
+ # duration = vms.analytics.transcoding({
54
+ # startTime: "2025-01-01",
55
+ # endTime: "2025-01-31"
56
+ # })
57
+ # puts "Total duration: #{duration.sum} minutes"
58
+ #
59
+ # @since 1.0.0
60
+ def transcoding(payload)
61
+ merged_payload = payload.merge(type: nil)
62
+ response = @parent.send(:call, 'GET', "/reporting/vms/transcoding/duration?#{merged_payload.to_query}")
63
+ return [] if response.nil?
64
+
65
+ JSON.parse(response)
66
+ rescue StandardError => e
67
+ logger.error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
68
+ []
69
+ end
70
+
71
+ # Get volume analytics for transcoding
72
+ #
73
+ # Retrieves volume metrics (in bytes/GB) for transcoded video data
74
+ # over a specified time range. Useful for storage capacity planning
75
+ # and bandwidth analysis.
76
+ #
77
+ # @param payload [Hash] query parameters including time range
78
+ # @option payload [String] :startTime start date/time
79
+ # @option payload [String] :endTime end date/time
80
+ #
81
+ # @return [Array] volume metrics for transcoding operations, or empty array on error
82
+ #
83
+ # @example Get monthly transcoding volume
84
+ # volume = vms.analytics.volume({
85
+ # startTime: "2025-01-01",
86
+ # endTime: "2025-01-31"
87
+ # })
88
+ # puts "Total volume: #{volume.sum} GB"
89
+ #
90
+ # @since 1.0.0
91
+ def volume(payload)
92
+ merged_payload = payload.merge(type: nil)
93
+ response = @parent.send(:call, 'GET', "/reporting/vms/transcoding/volume?#{merged_payload.to_query}")
94
+ return [] if response.nil?
95
+
96
+ JSON.parse(response)
97
+ rescue StandardError => e
98
+ logger.error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
99
+ []
100
+ end
101
+
102
+ private
103
+
104
+ # Get logger instance from parent
105
+ #
106
+ # @return [Logger] logger instance
107
+ def logger
108
+ @parent.send(:logger)
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conversant
4
+ module V3
5
+ module Services
6
+ class VMS
7
+ # Business metrics service for VMS
8
+ #
9
+ # Provides business-level metrics for billing and reporting
10
+ # including duration, volume, and transcoding statistics broken
11
+ # down by resolution (SD, HD, UHD) and operation type.
12
+ #
13
+ # @example Get business metrics
14
+ # vms = Conversant::V3.vms(12345)
15
+ #
16
+ # # Get duration metrics for billing
17
+ # duration = vms.business.duration({
18
+ # startTime: Time.now.beginning_of_month
19
+ # })
20
+ #
21
+ # # Get comprehensive transcoding breakdown
22
+ # breakdown = vms.business.transcoding({
23
+ # startTime: Time.now.beginning_of_month
24
+ # })
25
+ # puts "SD: #{breakdown[:vms_transcoding_sd]}"
26
+ # puts "HD: #{breakdown[:vms_transcoding_hd]}"
27
+ # puts "UHD: #{breakdown[:vms_transcoding_uhd]}"
28
+ #
29
+ # @since 1.0.0
30
+ class Business
31
+ # @return [VMS] the parent VMS service instance
32
+ attr_reader :parent
33
+
34
+ # Initialize business metrics service
35
+ #
36
+ # @param parent [VMS] the parent VMS service instance
37
+ def initialize(parent)
38
+ @parent = parent
39
+ end
40
+
41
+ # Get video duration metrics for billing
42
+ #
43
+ # Retrieves video transcoding duration metrics aggregated by month
44
+ # for billing and usage reporting purposes.
45
+ #
46
+ # @param payload [Hash] query parameters including month/year
47
+ # @option payload [Time, String] :startTime start time (used to extract month)
48
+ #
49
+ # @return [Array] duration metrics for billing purposes, or empty array on error
50
+ #
51
+ # @example Get current month duration
52
+ # duration = vms.business.duration({
53
+ # startTime: Time.now.beginning_of_month
54
+ # })
55
+ #
56
+ # @since 1.0.0
57
+ def duration(payload)
58
+ month = extract_month(payload)
59
+ search_payload = {
60
+ month: month,
61
+ selectType: 'vtd',
62
+ customerType: @parent.type || 2,
63
+ _: (Time.now.to_f * 1000).to_i.to_s
64
+ }
65
+
66
+ response = @parent.send(:call, 'GET', "/v5/reporting/vms/business/usage/search?#{search_payload.to_query}")
67
+ return [] if response.nil?
68
+
69
+ JSON.parse(response)
70
+ rescue StandardError => e
71
+ logger.error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
72
+ []
73
+ end
74
+
75
+ # Get video volume metrics for billing
76
+ #
77
+ # Retrieves video transcoding volume metrics aggregated by month
78
+ # for storage billing and capacity planning.
79
+ #
80
+ # @param payload [Hash] query parameters including month/year
81
+ # @option payload [Time, String] :startTime start time (used to extract month)
82
+ #
83
+ # @return [Array] volume metrics for billing purposes, or empty array on error
84
+ #
85
+ # @example Get current month volume
86
+ # volume = vms.business.volume({
87
+ # startTime: Time.now.beginning_of_month
88
+ # })
89
+ #
90
+ # @since 1.0.0
91
+ def volume(payload)
92
+ month = extract_month(payload)
93
+ search_payload = {
94
+ month: month,
95
+ selectType: 'vtv',
96
+ customerType: @parent.type || 2,
97
+ _: (Time.now.to_f * 1000).to_i.to_s
98
+ }
99
+
100
+ response = @parent.send(:call, 'GET', "/v5/reporting/vms/business/usage/search?#{search_payload.to_query}")
101
+ return [] if response.nil?
102
+
103
+ JSON.parse(response)
104
+ rescue StandardError => e
105
+ logger.error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
106
+ []
107
+ end
108
+
109
+ # Get comprehensive transcoding metrics breakdown
110
+ #
111
+ # Retrieves detailed transcoding metrics broken down by resolution
112
+ # (SD, HD, UHD) and operation type (transcoding vs transmuxing).
113
+ # Useful for detailed billing and capacity analysis.
114
+ #
115
+ # @param payload [Hash] query parameters including month/year
116
+ # @option payload [Time, String] :startTime start time (used to extract month)
117
+ #
118
+ # @return [Hash] transcoding metrics breakdown with keys:
119
+ # - :vms_transcoding [Float] total transcoding duration
120
+ # - :vms_transmuxing [Float] transmuxing duration
121
+ # - :vms_transcoding_sd [Float] SD transcoding duration
122
+ # - :vms_transcoding_hd [Float] HD transcoding duration
123
+ # - :vms_transcoding_uhd [Float] UHD transcoding duration
124
+ #
125
+ # @example Get transcoding breakdown
126
+ # breakdown = vms.business.transcoding({
127
+ # startTime: Time.now.beginning_of_month
128
+ # })
129
+ # puts "Total transcoding: #{breakdown[:vms_transcoding]} minutes"
130
+ # puts "SD: #{breakdown[:vms_transcoding_sd]} minutes"
131
+ # puts "HD: #{breakdown[:vms_transcoding_hd]} minutes"
132
+ # puts "UHD: #{breakdown[:vms_transcoding_uhd]} minutes"
133
+ # puts "Transmuxing: #{breakdown[:vms_transmuxing]} minutes"
134
+ #
135
+ # @since 1.0.0
136
+ def transcoding(payload)
137
+ report_vms = {
138
+ vms_transcoding: 0,
139
+ vms_transmuxing: 0,
140
+ vms_transcoding_sd: 0,
141
+ vms_transcoding_hd: 0,
142
+ vms_transcoding_uhd: 0
143
+ }
144
+
145
+ vms_trans = duration(payload)&.first
146
+ return report_vms unless vms_trans
147
+
148
+ name = vms_trans.keys&.first
149
+ if vms_trans[name].present? && !vms_trans[name].empty?
150
+ vms_trans[name]&.each do |item|
151
+ report_vms[:vms_transmuxing] += item['transmux']&.to_f || 0
152
+ report_vms[:vms_transcoding_sd] += (item['h264SdTranscoding']&.to_f || 0) + (item['h265SdTranscoding']&.to_f || 0)
153
+ report_vms[:vms_transcoding_hd] += (item['h264HdTranscoding']&.to_f || 0) + (item['h265HdTranscoding']&.to_f || 0)
154
+ report_vms[:vms_transcoding_uhd] += (item['h264UhdTranscoding']&.to_f || 0) + (item['h265UhdTranscoding']&.to_f || 0)
155
+ end
156
+ end
157
+
158
+ report_vms[:vms_transcoding] =
159
+ report_vms[:vms_transcoding_sd] +
160
+ report_vms[:vms_transcoding_hd] +
161
+ report_vms[:vms_transcoding_uhd]
162
+
163
+ report_vms
164
+ rescue StandardError => e
165
+ logger.error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
166
+ report_vms
167
+ end
168
+
169
+ private
170
+
171
+ # Extract month in YYYYMM format from payload
172
+ #
173
+ # @param payload [Hash] payload containing startTime
174
+ # @return [String] month in YYYYMM format
175
+ def extract_month(payload)
176
+ start_time = payload[:startTime] || Time.now.beginning_of_month
177
+ start_time.to_datetime.strftime('%Y%m')
178
+ end
179
+
180
+ # Get logger instance from parent
181
+ #
182
+ # @return [Logger] logger instance
183
+ def logger
184
+ @parent.send(:logger)
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conversant
4
+ module V3
5
+ module Services
6
+ class VMS
7
+ class Partner
8
+ # VMS analytics service for partner-level reporting
9
+ #
10
+ # Provides partner-level analytics and reporting for Video Management System services
11
+ # including VOD duration and transcoding metrics across multiple customer accounts.
12
+ #
13
+ # @since 1.0.12
14
+ class Analytics
15
+ # @return [Conversant::V3::Services::VMS] the parent VMS service instance
16
+ attr_reader :parent
17
+
18
+ # Initialize partner VMS analytics service
19
+ #
20
+ # @param parent [Conversant::V3::Services::VMS] the parent VMS service instance
21
+ def initialize(parent)
22
+ @parent = parent
23
+ end
24
+
25
+ # Get business service instance
26
+ #
27
+ # Provides access to business-focused analytics that aggregate transcoding
28
+ # data for VOD billing and capacity planning.
29
+ #
30
+ # @return [Business] business service for VMS aggregated analytics
31
+ # @since 1.0.16
32
+ #
33
+ # @example Access business methods
34
+ # vms = Conversant::V3.vms(12345)
35
+ # business = vms.partner.analytics.business
36
+ # metrics = business.transcoding(startTime: Time.now.beginning_of_month)
37
+ def business
38
+ @business ||= Business.new(self)
39
+ end
40
+
41
+ # Get duration of VOD (Video on Demand) for business usage reporting
42
+ #
43
+ # Retrieves VOD transcoding duration metrics aggregated by month and customer type
44
+ # for partner-level business reporting and billing purposes.
45
+ #
46
+ # @param args [Hash] query parameters
47
+ # @option args [Time, String] :startTime start time for the report (defaults to beginning of current month)
48
+ # @option args [String, Integer] :type customer type filter (defaults to 2)
49
+ #
50
+ # @return [Array] VOD duration data, or empty array on error
51
+ #
52
+ # @example Get VOD duration for current month
53
+ # duration = vms.partner.analytics.duration_of_vod
54
+ # puts "Total VOD duration: #{duration.first['total_duration']} minutes"
55
+ #
56
+ # @since 1.0.8
57
+ def duration_of_vod(**args)
58
+ month, type, timestamp = queries(**args)
59
+
60
+ payload = {
61
+ month: month,
62
+ selectType: 'vtd',
63
+ customerType: type,
64
+ _: timestamp
65
+ }
66
+
67
+ response = @parent.send(:call, 'GET', "/v5/reporting/vms/business/usage/search?#{payload.to_query}")
68
+ return [] if response.nil?
69
+
70
+ JSON.parse(response)&.map(&:with_indifferent_access) || []
71
+ rescue StandardError => e
72
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
73
+ []
74
+ end
75
+
76
+ # Get VMS transcoding duration metrics
77
+ #
78
+ # Retrieves transcoding duration metrics for video processing operations,
79
+ # useful for capacity planning and billing calculations.
80
+ #
81
+ # @param params [Hash] query parameters
82
+ # @option params [String] :year year in YYYY format
83
+ #
84
+ # @return [Array] transcoding duration data, or empty array on error
85
+ #
86
+ # @example Get transcoding duration for a year
87
+ # duration = vms.partner.analytics.duration(year: "2025")
88
+ # puts "Total transcoding: #{duration['total']} hours"
89
+ #
90
+ # @since 1.0.8
91
+ def duration(params = {})
92
+ query_string = params.map { |k, v| "#{k}=#{v}" }.join('&')
93
+ uri = '/reporting/vms/transcoding/duration'
94
+ uri += "?#{query_string}" unless query_string.empty?
95
+ response = @parent.send(:call, 'GET', uri)
96
+ return [] if response.nil?
97
+
98
+ JSON.parse(response)&.map(&:with_indifferent_access) || []
99
+ rescue StandardError => e
100
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
101
+ []
102
+ end
103
+
104
+ private
105
+
106
+ def logger
107
+ @parent.send(:logger)
108
+ end
109
+
110
+ # Helper method to build query parameters
111
+ #
112
+ # Generates month, customer type, and timestamp parameters for VMS business usage queries.
113
+ # Defaults to current month if startTime is not provided.
114
+ #
115
+ # @param args [Hash] Input parameters
116
+ # @option args [Time, String] :startTime Start time for the report (defaults to beginning of current month)
117
+ # @option args [String, Integer] :type Customer type filter (defaults to parent's type or 2)
118
+ #
119
+ # @return [Array<String, Integer, Integer>] Array containing [month (YYYYMM), customer_type, timestamp (ms)]
120
+ def queries(**args)
121
+ month = (args[:startTime] || Time.now&.beginning_of_month)&.to_datetime&.strftime('%Y%m')
122
+ customer_type = args[:type]
123
+ customer_type = @parent.type || 2 unless customer_type.present?
124
+ timestamp = Time.now.getutc.to_i * 1000
125
+
126
+ [month, customer_type, timestamp]
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end