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,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conversant
4
+ module V3
5
+ module Services
6
+ class LMS
7
+ # Preset service for live transcoding preset management
8
+ #
9
+ # Manages transcoding presets which define encoding parameters
10
+ # such as bitrate, resolution, codec settings for live streams.
11
+ #
12
+ # @example Get all transcoding presets
13
+ # lms = Conversant::V3.lms(12345)
14
+ #
15
+ # presets = lms.preset.all
16
+ # presets.each do |preset|
17
+ # puts "#{preset['name']}: #{preset['resolution']} @ #{preset['bitrate']}"
18
+ # end
19
+ #
20
+ # @since 1.0.8
21
+ class Preset
22
+ # @return [LMS] the parent LMS service instance
23
+ attr_reader :parent
24
+
25
+ # Initialize preset service
26
+ #
27
+ # @param parent [LMS] the parent LMS service instance
28
+ def initialize(parent)
29
+ @parent = parent
30
+ end
31
+
32
+ # Get all available transcoding presets
33
+ #
34
+ # Retrieves the list of transcoding presets configured for the customer,
35
+ # including encoding parameters like resolution, bitrate, codec settings.
36
+ #
37
+ # @return [Array<Hash>] array of preset configurations, or empty array on error
38
+ #
39
+ # @example Get all presets
40
+ # presets = lms.preset.all
41
+ # presets.each do |preset|
42
+ # puts "#{preset['name']}: #{preset['resolution']} @ #{preset['bitrate']}"
43
+ # end
44
+ #
45
+ # @since 1.0.8
46
+ def all
47
+ response = JSON.parse(@parent.send(:call, 'GET', '/live/preset/list_page'))
48
+ response&.[]('list') || []
49
+ rescue StandardError => e
50
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
51
+ []
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'authorization'
4
+
5
+ module Conversant
6
+ module V3
7
+ module Services
8
+ # Live Media Streaming (LMS) service for managing streaming operations
9
+ #
10
+ # Provides comprehensive functionality for live streaming management including:
11
+ # - Job management for streaming operations
12
+ # - Domain configuration for streaming endpoints
13
+ # - Dashboard metrics and monitoring
14
+ #
15
+ # @example Basic usage
16
+ # lms = Conversant::V3.lms(12345)
17
+ #
18
+ # # Get streaming jobs
19
+ # jobs = lms.job.where(status: 'active')
20
+ #
21
+ # # Get dashboard metrics
22
+ # live_metrics = lms.dashboard.live
23
+ #
24
+ # @since 1.0.0
25
+ class LMS < ::Conversant::V3::Base
26
+ include Authorization
27
+ # Get job management service instance
28
+ #
29
+ # @return [Job] job service for streaming job management
30
+ # @since 1.0.0
31
+ def job
32
+ @job ||= Job.new(self)
33
+ end
34
+
35
+ # Get domain management service instance
36
+ #
37
+ # @return [Domain] domain service for streaming domain management
38
+ # @since 1.0.0
39
+ def domain
40
+ @domain ||= Domain.new(self)
41
+ end
42
+
43
+ # Get dashboard metrics service instance
44
+ #
45
+ # @return [Dashboard] dashboard service for streaming metrics
46
+ # @since 1.0.0
47
+ def dashboard
48
+ @dashboard ||= Dashboard.new(self)
49
+ end
50
+
51
+ # Get preset service instance
52
+ #
53
+ # @return [Preset] preset service for transcoding configuration
54
+ # @since 1.0.8
55
+ def preset
56
+ @preset ||= Preset.new(self)
57
+ end
58
+
59
+ # Get partner service instance
60
+ #
61
+ # Provides access to partner-level analytics for Live Media Streaming
62
+ # that aggregate data across multiple customer accounts.
63
+ #
64
+ # @return [Partner] partner service for LMS analytics
65
+ # @since 1.0.12
66
+ #
67
+ # @example Access partner analytics
68
+ # lms = Conversant::V3.lms(12345)
69
+ # duration = lms.partner.analytics.duration(payload)
70
+ def partner
71
+ @partner ||= Partner.new(self)
72
+ end
73
+
74
+ # @deprecated Use {#partner} instead
75
+ # Get partner analytics service instance (deprecated)
76
+ #
77
+ # @return [LMS::Partner::Analytics] partner analytics for LMS reporting
78
+ # @since 1.0.8
79
+ # @deprecated Use `lms.partner.analytics` instead of `lms.partner_analytics`
80
+ def partner_analytics
81
+ @partner_analytics ||= Partner::Analytics.new(self)
82
+ end
83
+
84
+ protected
85
+
86
+ def call(method, uri, payload = nil)
87
+ url = "#{configuration.private_lms_endpoint}#{uri}"
88
+
89
+ headers = authorized_headers
90
+
91
+ # Check for JSESSIONID in Cookie header
92
+ if headers['Cookie'].nil? || !headers['Cookie'].include?('JSESSIONID=')
93
+ logger.error "#{identifier}.METHOD:call.NO_JSESSIONID"
94
+ raise AuthenticationError, 'Missing JSESSIONID for LMS'
95
+ end
96
+
97
+ code, response = request(method, url, payload, headers)
98
+
99
+ if code >= 400
100
+ logger.error "#{identifier}.METHOD:call.HTTP_ERROR:#{code}"
101
+ raise ApiError, "LMS API error: #{code}"
102
+ end
103
+
104
+ response.body
105
+ end
106
+
107
+ protected
108
+
109
+ def service_endpoint
110
+ configuration.private_lms_endpoint
111
+ end
112
+
113
+ def session_cookie_name
114
+ 'JSESSIONID'
115
+ end
116
+
117
+ def requires_session?
118
+ true
119
+ end
120
+
121
+ private
122
+
123
+ def fetch_new_session
124
+ sessions = authenticate
125
+ return nil unless sessions && sessions[:session] && sessions[:sso_gw_session2]
126
+
127
+ timestamp = (Time.now.to_f * 1000).to_i
128
+ reporting_url = "#{configuration.private_lms_endpoint}/?customerId=#{customer_id}&isHideMenu=f&t=#{timestamp}"
129
+
130
+ logger.debug "#{identifier}.METHOD:authorize.REQUESTING_JSESSIONID"
131
+
132
+ response = RestClient.get(
133
+ reporting_url,
134
+ {
135
+ authority: URI.parse(configuration.private_lms_endpoint).hostname,
136
+ referer: portal_endpoint,
137
+ user_agent: configuration.default_ua,
138
+ cookies: {
139
+ 'SESSION': sessions[:session],
140
+ 'SSO_GW_SESSION2': sessions[:sso_gw_session2]
141
+ },
142
+ timeout: 30,
143
+ open_timeout: 30
144
+ }
145
+ )
146
+
147
+ if response.cookies['JSESSIONID']
148
+ jsessionid = response.cookies['JSESSIONID']
149
+ redis.set(session_cache_key, jsessionid, ex: configuration.cache_ttl)
150
+ logger.debug "#{identifier}.METHOD:authorize.JSESSIONID_OBTAINED"
151
+ jsessionid
152
+ else
153
+ logger.error "#{identifier}.METHOD:authorize.NO_JSESSIONID_IN_RESPONSE"
154
+ nil
155
+ end
156
+ rescue RestClient::Unauthorized, RestClient::Forbidden => e
157
+ logger.error "#{identifier}.METHOD:authorize.AUTH_ERROR:#{e.message}"
158
+ nil
159
+ rescue StandardError => e
160
+ logger.error "#{identifier}.METHOD:authorize.ERROR:#{e.message}"
161
+ nil
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ # Load nested service classes after LMS is defined
169
+ require_relative 'lms/job'
170
+ require_relative 'lms/domain'
171
+ require_relative 'lms/dashboard'
172
+ require_relative 'lms/preset'
173
+ require_relative 'lms/partner'
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conversant
4
+ module V3
5
+ module Services
6
+ class OSS
7
+ class Partner
8
+ # OSS (Object Storage Service) analytics for partner-level reporting
9
+ #
10
+ # Provides partner-level analytics and reporting for Object Storage Service
11
+ # including storage usage metrics for S3-compatible storage across multiple
12
+ # customer accounts.
13
+ #
14
+ # @since 1.0.12
15
+ class Analytics
16
+ # @return [Conversant::V3::Services::OSS] the parent OSS service instance
17
+ attr_reader :parent
18
+
19
+ # Initialize partner OSS analytics service
20
+ #
21
+ # Note: OSS analytics uses the CDN service endpoint for API calls.
22
+ #
23
+ # @param parent [Conversant::V3::Services::OSS] the parent OSS service instance
24
+ def initialize(parent)
25
+ @parent = parent
26
+ end
27
+
28
+ # Get storage usage metrics for OSS (full year data)
29
+ #
30
+ # Retrieves Object Storage Service usage data including storage capacity,
31
+ # bandwidth usage, and request counts aggregated by month for billing
32
+ # and capacity planning purposes. Returns all months for the specified year.
33
+ #
34
+ # @param year [String, Integer] year in YYYY format
35
+ # @param params [Hash] optional query parameters
36
+ # @option params [String] :end end date for the report
37
+ #
38
+ # @return [Array<Hash>] array of monthly storage usage data, or empty array on error
39
+ #
40
+ # @example Get OSS storage usage for a year
41
+ # usages = oss.partner.analytics.usages("2025")
42
+ # usages.each do |month|
43
+ # puts "#{month['month']}: #{month['diskUsage']} bytes"
44
+ # end
45
+ #
46
+ # @since 1.0.8
47
+ def usages(year, params = {})
48
+ query_string = params.map { |k, v| "#{k}=#{v}" }.join('&')
49
+ uri = "/oss/storage_usages/#{year}"
50
+ uri += "?#{query_string}" unless query_string.empty?
51
+ response = @parent.send(:call, 'GET', uri)
52
+ JSON.parse(response) || []
53
+ rescue StandardError => e
54
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
55
+ []
56
+ end
57
+
58
+ # Get storage usage for current month
59
+ #
60
+ # Retrieves disk usage for the month containing the specified date (or current month).
61
+ # This is useful for getting current month's storage usage for billing purposes.
62
+ #
63
+ # @param payload [Hash] Parameters for the usage query
64
+ # @option payload [Date, String, Time] :end End date for the query (defaults to current date)
65
+ #
66
+ # @return [Integer, nil] Disk usage in bytes, or nil on error
67
+ #
68
+ # @example Get current month's storage usage
69
+ # usage = oss.partner.analytics.current_month_usage
70
+ # puts "Current usage: #{usage / 1_073_741_824.0} GB"
71
+ #
72
+ # @example Get specific month's storage usage
73
+ # usage = oss.partner.analytics.current_month_usage(end: '2025-04-30')
74
+ # puts "April 2025 usage: #{usage / 1_073_741_824.0} GB"
75
+ #
76
+ # @since 1.0.12
77
+ def current_month_usage(payload = {})
78
+ require 'date'
79
+
80
+ # Extract the end date from the payload, defaulting to the current date
81
+ today = if payload.[](:end) || payload.[]('end')
82
+ (payload[:end] || payload['end']).to_date
83
+ else
84
+ Date.today
85
+ end
86
+
87
+ # Fetch storage usage data for the year containing the end date
88
+ yearly_data = usages(today.year.to_s)
89
+ return 0 if yearly_data.nil? || yearly_data.empty?
90
+
91
+ # Find the storage usage for the specific month
92
+ today_month_format = today.strftime('%Y-%m')
93
+ month_data = yearly_data.find { |item| item&.[]('month') == today_month_format }
94
+
95
+ month_data&.[]('diskUsage') || 0
96
+ rescue StandardError => e
97
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
98
+ nil
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conversant
4
+ module V3
5
+ module Services
6
+ class OSS
7
+ # Partner service for OSS partner-level operations
8
+ #
9
+ # Provides access to partner-level analytics for Object Storage Service
10
+ # that aggregate data across multiple customer accounts.
11
+ #
12
+ # @example Access partner analytics
13
+ # oss = Conversant::V3.oss(12345)
14
+ #
15
+ # # Partner-level OSS storage analytics
16
+ # usages = oss.partner.analytics.usages("2025", end: "2025-12-31")
17
+ # puts "Total storage: #{usages['total_storage']} GB"
18
+ #
19
+ # @since 1.0.12
20
+ class Partner
21
+ # @return [OSS] the parent OSS service instance
22
+ attr_reader :parent
23
+
24
+ # Initialize partner service
25
+ #
26
+ # @param parent [OSS] the parent OSS service instance
27
+ def initialize(parent)
28
+ @parent = parent
29
+ end
30
+
31
+ # Get partner analytics service instance
32
+ #
33
+ # Provides partner-level analytics for OSS services including storage
34
+ # usage, capacity metrics, and bandwidth across multiple customers.
35
+ #
36
+ # @return [Analytics] OSS partner analytics service
37
+ # @since 1.0.12
38
+ def analytics
39
+ @analytics ||= Analytics.new(@parent)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ # Load nested analytics class after Partner is defined
48
+ require_relative 'partner/analytics'
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'authorization'
4
+
5
+ module Conversant
6
+ module V3
7
+ module Services
8
+ # Object Storage Service (OSS) for S3-compatible storage operations
9
+ #
10
+ # Provides comprehensive functionality for object storage operations including:
11
+ # - Storage usage metrics and analytics
12
+ # - Partner-level reporting across multiple customers
13
+ # - Capacity planning and billing data
14
+ #
15
+ # @example Basic usage
16
+ # oss = Conversant::V3.oss(12345)
17
+ #
18
+ # # Get storage usage for a year
19
+ # usages = oss.partner.analytics.usages("2025")
20
+ #
21
+ # @since 1.0.12
22
+ class OSS < ::Conversant::V3::Base
23
+ include Authorization
24
+
25
+ # Get partner service instance
26
+ #
27
+ # Provides access to partner-level analytics for Object Storage Service
28
+ # that aggregate data across multiple customer accounts.
29
+ #
30
+ # @return [Partner] partner service for OSS analytics
31
+ # @since 1.0.12
32
+ #
33
+ # @example Access partner analytics
34
+ # oss = Conversant::V3.oss(12345)
35
+ # usages = oss.partner.analytics.usages("2025")
36
+ def partner
37
+ @partner ||= Partner.new(self)
38
+ end
39
+
40
+ protected
41
+
42
+ def call(method, uri, payload = nil)
43
+ # OSS API calls use PRIVATE_OSS_ENDPOINT
44
+ url = "#{configuration.private_oss_endpoint}#{uri}"
45
+
46
+ # Get authorized headers (SESSION cookie from OSS)
47
+ headers = authorized_headers
48
+
49
+ # Check for SESSION in Cookie header
50
+ if headers['Cookie'].nil? || !headers['Cookie'].include?('SESSION=')
51
+ logger.error "#{identifier}.METHOD:call.NO_SESSION"
52
+ raise AuthenticationError, 'Missing SESSION for OSS'
53
+ end
54
+
55
+ code, response = request(method, url, payload, headers)
56
+
57
+ if code >= 400
58
+ logger.error "#{identifier}.METHOD:call.HTTP_ERROR:#{code}"
59
+ raise ApiError, "OSS API error: #{code}"
60
+ end
61
+
62
+ response.body
63
+ end
64
+
65
+ private
66
+
67
+ def fetch_new_session
68
+ sessions = authenticate
69
+ return nil unless sessions && sessions[:session] && sessions[:sso_gw_session2]
70
+
71
+ # OSS has its own authorization URL
72
+ timestamp = (Time.now.to_f * 1000).to_i
73
+ signature_url = "#{configuration.private_oss_endpoint}/?customerId=#{customer_id}&isHideMenu=f&t=#{timestamp}"
74
+ logger.debug "#{identifier}.METHOD:authorize.REQUESTING_SESSION"
75
+
76
+ response = RestClient.get(
77
+ signature_url,
78
+ {
79
+ authority: URI.parse(configuration.private_oss_endpoint).hostname,
80
+ referer: portal_endpoint,
81
+ user_agent: configuration.default_ua,
82
+ cookies: {
83
+ 'SESSION': sessions[:session],
84
+ 'SSO_GW_SESSION2': sessions[:sso_gw_session2]
85
+ },
86
+ timeout: 30,
87
+ open_timeout: 30
88
+ }
89
+ )
90
+
91
+ if response.cookies['SESSION']
92
+ session_cookie = response.cookies['SESSION']
93
+ redis.set(session_cache_key, session_cookie, ex: configuration.cache_ttl)
94
+ logger.debug "#{identifier}.METHOD:authorize.SESSION_OBTAINED"
95
+ session_cookie
96
+ else
97
+ logger.error "#{identifier}.METHOD:authorize.NO_SESSION_IN_RESPONSE"
98
+ nil
99
+ end
100
+ rescue RestClient::Unauthorized, RestClient::Forbidden => e
101
+ logger.error "#{identifier}.METHOD:authorize.AUTH_ERROR:#{e.message}"
102
+ nil
103
+ rescue StandardError => e
104
+ logger.error "#{identifier}.METHOD:authorize.ERROR:#{e.message}"
105
+ nil
106
+ end
107
+
108
+ protected
109
+
110
+ def service_endpoint
111
+ # OSS uses its own endpoint for both authentication and API calls
112
+ configuration.private_oss_endpoint
113
+ end
114
+
115
+ def session_cookie_name
116
+ 'SESSION'
117
+ end
118
+
119
+ def requires_session?
120
+ true
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ # Load nested service classes after OSS is defined
128
+ require_relative 'oss/partner'
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conversant
4
+ module V3
5
+ module Services
6
+ class Portal
7
+ # Dashboard service for Portal customer metadata and reporting
8
+ #
9
+ # Provides access to customer-related metadata including product usage,
10
+ # geographic distribution, industry classification, and organizational hierarchy.
11
+ #
12
+ # @since 1.0.8
13
+ class Dashboard
14
+ # @return [Portal] the parent Portal service instance
15
+ attr_reader :parent
16
+
17
+ # Initialize dashboard service
18
+ #
19
+ # @param parent [Portal] the parent Portal service instance
20
+ def initialize(parent)
21
+ @parent = parent
22
+ end
23
+
24
+ # Get product usage report for a customer
25
+ #
26
+ # Retrieves information about which products (CDN, LMS, VMS, OSS) the customer
27
+ # is using and their usage statistics.
28
+ #
29
+ # @param params [Hash] query parameters
30
+ # @option params [String, Integer] :customerId customer ID
31
+ # @option params [Integer] :_ timestamp in milliseconds (for cache busting)
32
+ #
33
+ # @return [Hash] product report data, or empty hash on error
34
+ #
35
+ # @example Get product report
36
+ # report = portal.dashboard.products(customerId: "31723", _: Time.now.to_i * 1000)
37
+ # puts "CDN enabled: #{report['cdn_enabled']}"
38
+ #
39
+ # @since 1.0.8
40
+ def products(params = {})
41
+ query_string = params.map { |k, v| "#{k}=#{v}" }.join('&')
42
+ response = @parent.send(:call, 'GET', "/getProductReport?#{query_string}")
43
+ JSON.parse(response)
44
+ rescue StandardError => e
45
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
46
+ {}
47
+ end
48
+
49
+ # Get list of countries
50
+ #
51
+ # Retrieves the list of countries available in the system, typically used
52
+ # for filtering or categorizing customers by geographic location.
53
+ #
54
+ # @return [Array<Hash>] array of country data, or empty array on error
55
+ #
56
+ # @example Get all countries
57
+ # countries = portal.dashboard.countries
58
+ # countries.each { |country| puts "#{country['name']} (#{country['code']})" }
59
+ #
60
+ # @since 1.0.8
61
+ def countries
62
+ response = @parent.send(:call, 'GET', '/getCountry')
63
+ JSON.parse(response)
64
+ rescue StandardError => e
65
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
66
+ []
67
+ end
68
+
69
+ # Get list of industries
70
+ #
71
+ # Retrieves the list of industry classifications available in the system,
72
+ # used for categorizing customers by business sector.
73
+ #
74
+ # @return [Array<Hash>] array of industry data, or empty array on error
75
+ #
76
+ # @example Get all industries
77
+ # industries = portal.dashboard.industries
78
+ # industries.each { |industry| puts "#{industry['name']}" }
79
+ #
80
+ # @since 1.0.8
81
+ def industries
82
+ response = @parent.send(:call, 'GET', '/getIndustry')
83
+ JSON.parse(response)
84
+ rescue StandardError => e
85
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
86
+ []
87
+ end
88
+
89
+ # Get customer organizational tree view
90
+ #
91
+ # Retrieves the hierarchical organization structure showing parent-child
92
+ # relationships between customers, useful for partner/reseller views.
93
+ #
94
+ # @param payload [Hash] tree view query parameters (optional)
95
+ #
96
+ # @return [Hash] tree structure data, or empty hash on error
97
+ #
98
+ # @example Get customer tree
99
+ # tree = portal.dashboard.tree_view
100
+ # puts "Root customers: #{tree['children'].length}"
101
+ #
102
+ # @since 1.0.8
103
+ def tree_view(payload = {})
104
+ response = @parent.send(:call, 'POST', '/customer/tree_view/', payload)
105
+ JSON.parse(response)
106
+ rescue StandardError => e
107
+ @parent.send(:logger).error "#{@parent.send(:identifier)}.METHOD:#{__method__}.EXCEPTION:#{e.message}"
108
+ {}
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end