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,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conversant
4
+ module V3
5
+ module Services
6
+ # Shared authorization methods for service classes
7
+ #
8
+ # This module provides common authentication and authorization functionality
9
+ # for Conversant V3 service classes. It handles session management, cookie
10
+ # generation, and HTTP header construction with automatic caching via Redis.
11
+ #
12
+ # @example Including in a service class
13
+ # class MyService < Conversant::V3::BaseService
14
+ # include Conversant::V3::Services::Authorization
15
+ #
16
+ # def fetch_new_session
17
+ # # Custom session fetching logic
18
+ # end
19
+ #
20
+ # def service_endpoint
21
+ # 'https://api.example.com'
22
+ # end
23
+ # end
24
+ module Authorization
25
+ protected
26
+
27
+ # Builds authorized HTTP headers with session cookies
28
+ #
29
+ # Retrieves session cookies from cache or authenticates if needed,
30
+ # then constructs headers with appropriate cookie values.
31
+ #
32
+ # @return [Hash] HTTP headers including Cookie header if sessions exist
33
+ def authorized_headers
34
+ session_cookie = fetch_session_cookie
35
+ sso_gw_session2 = fetch_sso_session
36
+
37
+ log_missing_session if session_cookie.nil? && respond_to?(:requires_session?) && requires_session?
38
+
39
+ build_headers_with_cookies(session_cookie, sso_gw_session2)
40
+ end
41
+
42
+ # Authorizes and retrieves a session cookie
43
+ #
44
+ # Checks Redis cache first, then fetches a new session if not cached.
45
+ #
46
+ # @return [String, nil] The session cookie value, or nil if authorization fails
47
+ def authorize
48
+ cached = cached_session
49
+ return cached if cached
50
+
51
+ fetch_new_session
52
+ rescue StandardError => e
53
+ logger.debug "#{identifier}.METHOD:authorize.EXCEPTION:#{e.message}"
54
+ nil
55
+ end
56
+
57
+ private
58
+
59
+ # Fetches session cookie from cache or authorizes to get a new one
60
+ #
61
+ # @return [String, nil] The session cookie value
62
+ def fetch_session_cookie
63
+ cached = cached_session
64
+ cached || authorize
65
+ end
66
+
67
+ # Fetches SSO session from authentication or cache
68
+ #
69
+ # @return [String, nil] The SSO_GW_SESSION2 cookie value
70
+ def fetch_sso_session
71
+ sessions = authenticate
72
+ sessions&.[](:sso_gw_session2)
73
+ end
74
+
75
+ # Retrieves cached session from Redis
76
+ #
77
+ # @return [String, nil] The cached session value
78
+ def cached_session
79
+ redis.get(session_cache_key)
80
+ end
81
+
82
+ # Generates Redis cache key for session storage
83
+ #
84
+ # @return [String] Redis key in format "CONVERSANT.V3.SERVICE.SESSION_TYPE.CUSTOMER_ID"
85
+ def session_cache_key
86
+ service_name = self.class.name.split('::').last.upcase
87
+ session_type = respond_to?(:session_cookie_name, true) ? session_cookie_name : 'SESSION'
88
+ "CONVERSANT.V3.#{service_name}.#{session_type}.#{customer_id}"
89
+ end
90
+
91
+ # Generates Redis cache key for SSO session storage
92
+ #
93
+ # @return [String] Redis key in format "CONVERSANT.V3.PORTAL.SSO_GW_SESSION2.CUSTOMER_ID"
94
+ def sso_cache_key
95
+ "CONVERSANT.V3.PORTAL.SSO_GW_SESSION2.#{customer_id}"
96
+ end
97
+
98
+ # Builds HTTP headers hash with cookie values
99
+ #
100
+ # @param session_cookie [String, nil] The session cookie value
101
+ # @param sso_gw_session2 [String, nil] The SSO session cookie value
102
+ # @return [Hash] Complete HTTP headers including Cookie header if applicable
103
+ def build_headers_with_cookies(session_cookie, sso_gw_session2)
104
+ headers = base_headers
105
+ cookie_header = format_cookies(session_cookie, sso_gw_session2)
106
+ headers['Cookie'] = cookie_header if cookie_header
107
+ headers
108
+ end
109
+
110
+ # Builds base HTTP headers for API requests
111
+ #
112
+ # @return [Hash] Base headers with Authority, Content-Type, Referer, and User-Agent
113
+ def base_headers
114
+ {
115
+ 'Authority' => URI.parse(service_endpoint).hostname,
116
+ 'Content-Type' => configuration.default_content_type,
117
+ 'Referer' => portal_endpoint,
118
+ 'User-Agent' => configuration.default_ua
119
+ }
120
+ end
121
+
122
+ # Formats cookie hash into HTTP Cookie header string
123
+ #
124
+ # @param session_cookie [String, nil] The session cookie value
125
+ # @param sso_gw_session2 [String, nil] The SSO session cookie value
126
+ # @return [String, nil] Formatted cookie string "key1=value1; key2=value2" or nil if no cookies
127
+ def format_cookies(session_cookie, sso_gw_session2)
128
+ cookies = build_cookie_hash(session_cookie, sso_gw_session2)
129
+ return nil if cookies.empty?
130
+
131
+ cookies.map { |k, v| "#{k}=#{v}" }.join('; ')
132
+ end
133
+
134
+ # Builds hash of cookie key-value pairs
135
+ #
136
+ # @param session_cookie [String, nil] The session cookie value
137
+ # @param sso_gw_session2 [String, nil] The SSO session cookie value
138
+ # @return [Hash] Cookie hash with appropriate keys based on service configuration
139
+ def build_cookie_hash(session_cookie, sso_gw_session2)
140
+ cookies = {}
141
+
142
+ if respond_to?(:session_cookie_name, true)
143
+ cookies[session_cookie_name] = session_cookie if session_cookie
144
+ elsif session_cookie
145
+ cookies['SESSION'] = session_cookie
146
+ end
147
+
148
+ cookies['SSO_GW_SESSION2'] = sso_gw_session2 if sso_gw_session2
149
+ cookies
150
+ end
151
+
152
+ # Logs warning when required session is missing
153
+ #
154
+ # @return [void]
155
+ def log_missing_session
156
+ session_type = respond_to?(:session_cookie_name, true) ? session_cookie_name : 'SESSION'
157
+ logger.warn "#{identifier}.METHOD:authorized_headers.NO_#{session_type}"
158
+ end
159
+
160
+ # Returns the service-specific API endpoint
161
+ #
162
+ # @abstract Subclasses must implement this method
163
+ # @return [String] The service endpoint URL
164
+ # @raise [NotImplementedError] if not implemented by subclass
165
+ def service_endpoint
166
+ raise NotImplementedError, "#{self.class} must implement #service_endpoint"
167
+ end
168
+
169
+ # Fetches a new session from the service
170
+ #
171
+ # @abstract Subclasses must implement this method with service-specific logic
172
+ # @return [String] The new session cookie value
173
+ # @raise [NotImplementedError] if not implemented by subclass
174
+ def fetch_new_session
175
+ raise NotImplementedError, "#{self.class} must implement #fetch_new_session"
176
+ end
177
+
178
+ # Returns the session cookie name for this service
179
+ #
180
+ # @return [String] The cookie name (defaults to 'SESSION')
181
+ def session_cookie_name
182
+ 'SESSION'
183
+ end
184
+
185
+ # Indicates whether this service requires a session cookie
186
+ #
187
+ # @return [Boolean] true if session is required, false otherwise
188
+ def requires_session?
189
+ false
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,483 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conversant
4
+ module V3
5
+ module Services
6
+ class CDN
7
+ # Analytics service for CDN metrics and reporting
8
+ #
9
+ # Provides comprehensive analytics data for CDN performance including:
10
+ # - Bandwidth and volume metrics
11
+ # - Viewer statistics
12
+ # - Request rates and hit rates
13
+ # - HTTP status code distribution
14
+ # - Referrer and popular content analytics
15
+ # - Origin request metrics
16
+ # - ISP and user agent analytics
17
+ #
18
+ # @example Basic usage
19
+ # cdn = Conversant::V3.cdn(12345)
20
+ #
21
+ # # Get bandwidth data
22
+ # bandwidth = cdn.analytics.bandwidths({
23
+ # domain: "All",
24
+ # startTime: "2025-01-01T00:00:00Z",
25
+ # endTime: "2025-01-01T23:59:59Z",
26
+ # interval: "hour"
27
+ # })
28
+ #
29
+ # # Get viewer statistics
30
+ # viewers = cdn.analytics.viewers({
31
+ # domain: "streaming.example.com",
32
+ # startTime: "2025-01-01T00:00:00Z",
33
+ # endTime: "2025-01-01T23:59:59Z",
34
+ # interval: "minute"
35
+ # })
36
+ #
37
+ # @since 1.0.0
38
+ class Analytics
39
+ # @return [CDN] the parent CDN service instance
40
+ attr_reader :parent
41
+
42
+ # Initialize analytics service
43
+ #
44
+ # @param parent [CDN] the parent CDN service instance
45
+ def initialize(parent)
46
+ @parent = parent
47
+ end
48
+
49
+ # Get bandwidth analytics data
50
+ #
51
+ # @param payload [Hash] analytics parameters
52
+ # @option payload [String] :domain domain name or "All" for all domains
53
+ # @option payload [String] :startTime start time in ISO 8601 format ("2025-01-01T00:00:00Z")
54
+ # @option payload [String] :endTime end time in ISO 8601 format
55
+ # @option payload [String] :interval time interval ("minute", "hour", "day")
56
+ # @option payload [Boolean] :fillFixedTime whether to fill missing time slots
57
+ # @option payload [String] :daHostName specific hostname filter
58
+ #
59
+ # @return [Hash, nil] bandwidth data with averageThroughput and data array, or nil on error
60
+ #
61
+ # @example Get hourly bandwidth for all domains
62
+ # bandwidth = cdn.analytics.bandwidths({
63
+ # domain: "All",
64
+ # startTime: "2025-01-01T00:00:00Z",
65
+ # endTime: "2025-01-01T23:59:59Z",
66
+ # interval: "hour",
67
+ # fillFixedTime: true
68
+ # })
69
+ # puts bandwidth[:averageThroughput]
70
+ # bandwidth[:data].each { |point| puts "#{point['time']}: #{point['value']}" }
71
+ #
72
+ # @since 1.0.0
73
+ def bandwidths(payload)
74
+ response = JSON.parse(@parent.send(:call, 'POST', '/api/report/bandwidth', payload))
75
+ if response&.[]('bandwidth')
76
+ {
77
+ averageThroughput: response&.[]('averageThroughput'),
78
+ data: response&.[]('bandwidth'),
79
+ }
80
+ end
81
+ rescue StandardError => e
82
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
83
+ nil
84
+ end
85
+
86
+ # Get volume analytics data
87
+ #
88
+ # @param payload [Hash] analytics parameters
89
+ # @option payload [String] :domain domain name or "All" for all domains
90
+ # @option payload [String] :startTime start time in ISO 8601 format
91
+ # @option payload [String] :endTime end time in ISO 8601 format
92
+ # @option payload [String] :interval time interval ("minute", "hour", "day")
93
+ # @option payload [Boolean] :fillFixedTime whether to fill missing time slots
94
+ #
95
+ # @return [Hash, nil] volume data with domain and volumes array, or nil on error
96
+ #
97
+ # @example Get daily volume data
98
+ # volume = cdn.analytics.volumes({
99
+ # domain: "example.com",
100
+ # startTime: "2025-01-01T00:00:00Z",
101
+ # endTime: "2025-01-31T00:00:00Z",
102
+ # interval: "day"
103
+ # })
104
+ # puts "Domain: #{volume[:domain]}"
105
+ # volume[:data].each { |point| puts "#{point['time']}: #{point['bytes']} bytes" }
106
+ #
107
+ # @since 1.0.0
108
+ def volumes(payload)
109
+ response = JSON.parse(@parent.send(:call, 'POST', '/api/report/volume', payload))
110
+ { data: response&.[]('volumes') } if response&.[]('volumes')
111
+ rescue StandardError => e
112
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
113
+ nil
114
+ end
115
+
116
+ # Get viewer analytics data
117
+ #
118
+ # @param payload [Hash] analytics parameters
119
+ # @option payload [String] :domain domain name or "All" for all domains
120
+ # @option payload [String] :startTime start time in ISO 8601 format
121
+ # @option payload [String] :endTime end time in ISO 8601 format
122
+ # @option payload [String] :interval time interval ("minute", "hour", "day")
123
+ #
124
+ # @return [Array, nil] array of viewer data points, or nil on error
125
+ #
126
+ # @example Get viewer statistics
127
+ # viewers = cdn.analytics.viewers({
128
+ # domain: "streaming.example.com",
129
+ # startTime: "2025-01-01T00:00:00Z",
130
+ # endTime: "2025-01-01T23:59:59Z",
131
+ # interval: "minute"
132
+ # })
133
+ # viewers.each { |point| puts "#{point['time']}: #{point['viewers']} viewers" }
134
+ #
135
+ # @since 1.0.0
136
+ def viewers(payload)
137
+ JSON.parse(@parent.send(:call, 'POST', '/api/report/viewers', payload))
138
+ rescue StandardError => e
139
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
140
+ nil
141
+ end
142
+
143
+ # Get requests per second (RPS) analytics
144
+ #
145
+ # @param payload [Hash] analytics parameters (passed as query string)
146
+ # @option payload [String] :domain domain name or "All" for all domains
147
+ # @option payload [String] :startTime start time in ISO 8601 format
148
+ # @option payload [String] :endTime end time in ISO 8601 format
149
+ # @option payload [String] :interval time interval
150
+ #
151
+ # @return [Hash, nil] RPS data with statistics and time series, or nil on error
152
+ #
153
+ # @example Get RPS data
154
+ # rps = cdn.analytics.request_per_second({
155
+ # domain: "api.example.com",
156
+ # startTime: "2025-01-01T12:00:00Z",
157
+ # endTime: "2025-01-01T13:00:00Z"
158
+ # })
159
+ # puts "Max RPS: #{rps[:maxQuantity]}"
160
+ # puts "Avg RPS: #{rps[:avgQuantity]}"
161
+ # puts "HTTP Total: #{rps[:httpTotalQuantity]}"
162
+ # puts "HTTPS Total: #{rps[:httpsTotalQuantity]}"
163
+ #
164
+ # @since 1.0.0
165
+ def request_per_second(payload)
166
+ response = JSON.parse(@parent.send(:call, 'GET', "/api/rps_chart_data?#{payload.to_query}"))
167
+ if response&.[]('datas')
168
+ {
169
+ maxQuantity: response['maxQuantity'],
170
+ avgQuantity: response['avgQuantity'],
171
+ httpTotalQuantity: response['httpTotalQuantity'],
172
+ httpsTotalQuantity: response['httpsTotalQuantity'],
173
+ data: response['datas'],
174
+ }
175
+ end
176
+ rescue StandardError => e
177
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
178
+ nil
179
+ end
180
+
181
+ # Get HTTP status code analytics
182
+ #
183
+ # @param payload [Hash] analytics parameters
184
+ # @option payload [Array<String>] :domains array of domain names
185
+ # @option payload [String] :startTime start time in ISO 8601 format
186
+ # @option payload [String] :endTime end time in ISO 8601 format
187
+ # @option payload [String] :interval time interval
188
+ #
189
+ # @return [Hash, nil] HTTP status code distribution data, or nil on error
190
+ #
191
+ # @example Get HTTP status codes
192
+ # codes = cdn.analytics.http_codes({
193
+ # domains: ["example.com"],
194
+ # startTime: "2025-01-01T00:00:00Z",
195
+ # endTime: "2025-01-01T23:59:59Z",
196
+ # interval: "hour"
197
+ # })
198
+ # puts "2xx responses: #{codes['2xx']}"
199
+ # puts "4xx responses: #{codes['4xx']}"
200
+ #
201
+ # @since 1.0.1
202
+ def http_codes(payload)
203
+ JSON.parse(@parent.send(:call, 'POST', "/api/report/customers/#{@parent.customer_id}/http_codes", payload))
204
+ rescue StandardError => e
205
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
206
+ nil
207
+ end
208
+
209
+ # Get referrer analytics by domain
210
+ #
211
+ # @param payload [Hash] analytics parameters
212
+ # @option payload [String] :customerId customer ID
213
+ # @option payload [String] :serviceName domain/service name
214
+ # @option payload [String] :startTime start time in ISO 8601 format
215
+ # @option payload [String] :endTime end time in ISO 8601 format
216
+ # @option payload [Integer] :pageNumber page number (0-based)
217
+ # @option payload [Integer] :pageSize number of results per page
218
+ #
219
+ # @return [Array] array of referrer data, or empty array on error
220
+ #
221
+ # @example Get top referrers
222
+ # referrers = cdn.analytics.referrer_by_domain({
223
+ # customerId: "12345",
224
+ # serviceName: "example.com",
225
+ # startTime: "2025-01-01T00:00:00Z",
226
+ # endTime: "2025-01-07T00:00:00Z",
227
+ # pageNumber: 0,
228
+ # pageSize: 20
229
+ # })
230
+ # referrers.each { |ref| puts "#{ref['referrer']}: #{ref['requests']} requests" }
231
+ #
232
+ # @since 1.0.1
233
+ def referrer_by_domain(payload)
234
+ response = JSON.parse(@parent.send(:call, 'POST', '/api/report/getReferrerByDomain', payload))
235
+ response&.[]('list') || []
236
+ rescue StandardError => e
237
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
238
+ []
239
+ end
240
+
241
+ # Get popular content URLs by domain
242
+ #
243
+ # @param payload [Hash] analytics parameters
244
+ # @option payload [String] :customerId customer ID
245
+ # @option payload [String] :serviceName domain/service name
246
+ # @option payload [String] :startTime start time in ISO 8601 format
247
+ # @option payload [String] :endTime end time in ISO 8601 format
248
+ # @option payload [Integer] :pageNumber page number (0-based)
249
+ # @option payload [Integer] :pageSize number of results per page
250
+ #
251
+ # @return [Array] array of popular URL data, or empty array on error
252
+ #
253
+ # @example Get most requested URLs
254
+ # urls = cdn.analytics.popular_content_url_by_domain({
255
+ # customerId: "12345",
256
+ # serviceName: "cdn.example.com",
257
+ # startTime: "2025-01-01T00:00:00Z",
258
+ # endTime: "2025-01-07T00:00:00Z",
259
+ # pageNumber: 0,
260
+ # pageSize: 50
261
+ # })
262
+ # urls.each { |url| puts "#{url['url']}: #{url['requests']} requests" }
263
+ #
264
+ # @since 1.0.1
265
+ def popular_content_url_by_domain(payload)
266
+ response = JSON.parse(@parent.send(:call, 'POST', '/api/report/getUrlByDomain', payload))
267
+ response&.[]('list') || []
268
+ rescue StandardError => e
269
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
270
+ []
271
+ end
272
+
273
+ # Get origin request analytics
274
+ #
275
+ # @param payload [Hash] analytics parameters (passed as query string)
276
+ # @option payload [String] :domain domain name
277
+ # @option payload [String] :startTime start time in ISO 8601 format
278
+ # @option payload [String] :endTime end time in ISO 8601 format
279
+ # @option payload [String] :interval time interval
280
+ #
281
+ # @return [Hash, nil] origin request data with time series, or nil on error
282
+ #
283
+ # @example Get origin request data
284
+ # origin = cdn.analytics.origin_request({
285
+ # domain: "example.com",
286
+ # startTime: "2025-01-01T00:00:00Z",
287
+ # endTime: "2025-01-01T23:59:59Z",
288
+ # interval: "hour"
289
+ # })
290
+ # origin[:data].each { |point| puts "#{point['time']}: #{point['requests']} origin requests" }
291
+ #
292
+ # @since 1.0.0
293
+ def origin_request(payload)
294
+ response = JSON.parse(@parent.send(:call, 'GET', "/api/origin_request_chart_data?#{payload.to_query}"))
295
+ { data: response['requestSpots'] } if response&.[]('requestSpots')
296
+ rescue StandardError => e
297
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
298
+ nil
299
+ end
300
+
301
+ # Get request hit rate analytics
302
+ #
303
+ # @param params [Hash] analytics parameters (passed as query string)
304
+ # @option params [String] :domain domain name or "All"
305
+ # @option params [String] :startTime start time in ISO 8601 format
306
+ # @option params [String] :endTime end time in ISO 8601 format
307
+ # @option params [String] :interval time interval
308
+ # @option params [Boolean] :fillFixedTime fill missing time slots
309
+ #
310
+ # @return [Hash, nil] hit rate data, or nil on error
311
+ #
312
+ # @since 1.0.8
313
+ def request_hit_rate(params)
314
+ query_string = URI.encode_www_form(params)
315
+ response = JSON.parse(@parent.send(:call, 'GET', "/api/hit_rate_chart_data?#{query_string}", nil))
316
+ response
317
+ rescue StandardError => e
318
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
319
+ nil
320
+ end
321
+
322
+ # Get byte hit rate analytics
323
+ #
324
+ # @param params [Hash] analytics parameters (passed as query string)
325
+ # @return [Hash, nil] byte hit rate data, or nil on error
326
+ # @since 1.0.8
327
+ def byte_hit_rate(params)
328
+ query_string = URI.encode_www_form(params)
329
+ response = JSON.parse(@parent.send(:call, 'GET', "/api/byte_rate_chart_data?#{query_string}", nil))
330
+ response
331
+ rescue StandardError => e
332
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
333
+ nil
334
+ end
335
+
336
+ # Get origin bandwidth analytics
337
+ #
338
+ # @param payload [Hash] analytics parameters
339
+ # @return [Array] origin bandwidth data, or empty array on error
340
+ # @since 1.0.8
341
+ def origin_bandwidth(payload)
342
+ response = JSON.parse(@parent.send(:call, 'POST', '/api/report/origin_bandwidth', payload))
343
+ response || []
344
+ rescue StandardError => e
345
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
346
+ []
347
+ end
348
+
349
+ # Get number of concurrent viewers by domain
350
+ #
351
+ # @param payload [Hash] analytics parameters
352
+ # @return [Array] concurrent viewer data, or empty array on error
353
+ # @since 1.0.8
354
+ def number_of_concurrent_viewers(payload)
355
+ response = JSON.parse(@parent.send(:call, 'POST', '/api/report/domain/viewers', payload))
356
+ response || []
357
+ rescue StandardError => e
358
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
359
+ []
360
+ end
361
+
362
+ # Get origin HTTP status codes
363
+ #
364
+ # @param payload [Hash] analytics parameters
365
+ # @return [Array] origin HTTP code data, or empty array on error
366
+ # @since 1.0.8
367
+ def origin_http_codes(payload)
368
+ customer_id = @parent.send(:customer_id)
369
+ response = JSON.parse(@parent.send(:call, 'POST', "/api/report/customers/#{customer_id}/origin_http_codes", payload))
370
+ response || []
371
+ rescue StandardError => e
372
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
373
+ []
374
+ end
375
+
376
+ # Get total referrer count by domain
377
+ #
378
+ # @param payload [Hash] analytics parameters
379
+ # @return [Hash, nil] total referrer data, or nil on error
380
+ # @since 1.0.8
381
+ def referrer_total_by_domain(payload)
382
+ response = JSON.parse(@parent.send(:call, 'POST', '/api/report/getReferrerTotalByDomain', payload))
383
+ response
384
+ rescue StandardError => e
385
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
386
+ nil
387
+ end
388
+
389
+ # Get referrer CSV export by domain
390
+ #
391
+ # @param payload [Hash] analytics parameters with csvRequest: 1
392
+ # @return [String, nil] CSV data, or nil on error
393
+ # @since 1.0.8
394
+ def referrer_csv_by_domain(payload)
395
+ response = @parent.send(:call, 'POST', '/api/report/getReferrerCsvByDomain', payload)
396
+ response
397
+ rescue StandardError => e
398
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
399
+ nil
400
+ end
401
+
402
+ # Get total popular content URL count by domain
403
+ #
404
+ # @param payload [Hash] analytics parameters
405
+ # @return [Hash, nil] total URL data, or nil on error
406
+ # @since 1.0.8
407
+ def popular_content_url_total_by_domain(payload)
408
+ response = JSON.parse(@parent.send(:call, 'POST', '/api/report/getUrlTotalByDomain', payload))
409
+ response
410
+ rescue StandardError => e
411
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
412
+ nil
413
+ end
414
+
415
+ # Get popular content URL CSV export by domain
416
+ #
417
+ # @param payload [Hash] analytics parameters with csvRequest: 1
418
+ # @return [String, nil] CSV data, or nil on error
419
+ # @since 1.0.8
420
+ def popular_content_url_csv_by_domain(payload)
421
+ response = @parent.send(:call, 'POST', '/api/report/getUrlCsvByDomain', payload)
422
+ response
423
+ rescue StandardError => e
424
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
425
+ nil
426
+ end
427
+
428
+ # Get request and visit number statistics
429
+ #
430
+ # @param payload [Hash] analytics parameters
431
+ # @return [Hash, nil] request/visit statistics, or nil on error
432
+ # @since 1.0.8
433
+ def request_visit_number(payload)
434
+ response = JSON.parse(@parent.send(:call, 'POST', '/api/report/request_visit_number', payload))
435
+ response
436
+ rescue StandardError => e
437
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
438
+ nil
439
+ end
440
+
441
+ # Get ISP analytics
442
+ #
443
+ # @param payload [Hash] analytics parameters
444
+ # @return [Array] ISP data, or empty array on error
445
+ # @since 1.0.8
446
+ def isp(payload)
447
+ response = JSON.parse(@parent.send(:call, 'POST', '/api/report/isp', payload))
448
+ response || []
449
+ rescue StandardError => e
450
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
451
+ []
452
+ end
453
+
454
+ # Get user agent analytics by domain
455
+ #
456
+ # @param payload [Hash] analytics parameters
457
+ # @return [Array] user agent data, or empty array on error
458
+ # @since 1.0.8
459
+ def user_agent(payload)
460
+ response = JSON.parse(@parent.send(:call, 'POST', '/api/report/domain/userAgent', payload))
461
+ response&.[]('list') || []
462
+ rescue StandardError => e
463
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
464
+ []
465
+ end
466
+
467
+ # Get user agent CSV export by domain
468
+ #
469
+ # @param payload [Hash] analytics parameters
470
+ # @return [String, nil] CSV data, or nil on error
471
+ # @since 1.0.8
472
+ def user_agent_csv(payload)
473
+ response = @parent.send(:call, 'POST', '/api/report/domain/userAgentCsv', payload)
474
+ response
475
+ rescue StandardError => e
476
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
477
+ nil
478
+ end
479
+ end
480
+ end
481
+ end
482
+ end
483
+ end