io-complyance-unify-sdk 3.0.0
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/CHANGELOG.md +26 -0
- data/README.md +595 -0
- data/lib/complyance/circuit_breaker.rb +99 -0
- data/lib/complyance/persistent_queue_manager.rb +474 -0
- data/lib/complyance/retry_strategy.rb +198 -0
- data/lib/complyance_sdk/config/retry_config.rb +127 -0
- data/lib/complyance_sdk/config/sdk_config.rb +212 -0
- data/lib/complyance_sdk/exceptions/circuit_breaker_open_error.rb +14 -0
- data/lib/complyance_sdk/exceptions/sdk_exception.rb +93 -0
- data/lib/complyance_sdk/generators/config_generator.rb +67 -0
- data/lib/complyance_sdk/generators/install_generator.rb +22 -0
- data/lib/complyance_sdk/generators/templates/complyance_initializer.rb +36 -0
- data/lib/complyance_sdk/http/authentication_middleware.rb +43 -0
- data/lib/complyance_sdk/http/client.rb +223 -0
- data/lib/complyance_sdk/http/logging_middleware.rb +153 -0
- data/lib/complyance_sdk/jobs/base_job.rb +63 -0
- data/lib/complyance_sdk/jobs/process_document_job.rb +92 -0
- data/lib/complyance_sdk/jobs/sidekiq_job.rb +165 -0
- data/lib/complyance_sdk/middleware/rack_middleware.rb +39 -0
- data/lib/complyance_sdk/models/country.rb +205 -0
- data/lib/complyance_sdk/models/country_policy_registry.rb +159 -0
- data/lib/complyance_sdk/models/document_type.rb +52 -0
- data/lib/complyance_sdk/models/environment.rb +144 -0
- data/lib/complyance_sdk/models/logical_doc_type.rb +228 -0
- data/lib/complyance_sdk/models/mode.rb +47 -0
- data/lib/complyance_sdk/models/operation.rb +47 -0
- data/lib/complyance_sdk/models/policy_result.rb +145 -0
- data/lib/complyance_sdk/models/purpose.rb +52 -0
- data/lib/complyance_sdk/models/source.rb +104 -0
- data/lib/complyance_sdk/models/source_ref.rb +130 -0
- data/lib/complyance_sdk/models/unify_request.rb +208 -0
- data/lib/complyance_sdk/models/unify_response.rb +198 -0
- data/lib/complyance_sdk/queue/persistent_queue_manager.rb +609 -0
- data/lib/complyance_sdk/railtie.rb +29 -0
- data/lib/complyance_sdk/retry/circuit_breaker.rb +159 -0
- data/lib/complyance_sdk/retry/retry_manager.rb +108 -0
- data/lib/complyance_sdk/retry/retry_strategy.rb +225 -0
- data/lib/complyance_sdk/version.rb +5 -0
- data/lib/complyance_sdk.rb +935 -0
- metadata +322 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ComplyanceSDK
|
|
4
|
+
module Models
|
|
5
|
+
# Source type enumeration
|
|
6
|
+
module SourceType
|
|
7
|
+
# First-party source
|
|
8
|
+
FIRST_PARTY = :first_party
|
|
9
|
+
|
|
10
|
+
# Third-party source
|
|
11
|
+
THIRD_PARTY = :third_party
|
|
12
|
+
|
|
13
|
+
# Marketplace source
|
|
14
|
+
MARKETPLACE = :marketplace
|
|
15
|
+
|
|
16
|
+
# Get all valid source types
|
|
17
|
+
#
|
|
18
|
+
# @return [Array<Symbol>] All valid source types
|
|
19
|
+
def self.all
|
|
20
|
+
[FIRST_PARTY, THIRD_PARTY, MARKETPLACE]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Source model representing the origin of documents
|
|
25
|
+
class Source
|
|
26
|
+
# Source ID
|
|
27
|
+
attr_accessor :id
|
|
28
|
+
|
|
29
|
+
# Source type
|
|
30
|
+
attr_accessor :type
|
|
31
|
+
|
|
32
|
+
# Source name
|
|
33
|
+
attr_accessor :name
|
|
34
|
+
|
|
35
|
+
# Source version
|
|
36
|
+
attr_accessor :version
|
|
37
|
+
|
|
38
|
+
# Source metadata
|
|
39
|
+
attr_accessor :metadata
|
|
40
|
+
|
|
41
|
+
# Initialize a new source
|
|
42
|
+
#
|
|
43
|
+
# @param options [Hash] Source options
|
|
44
|
+
# @option options [String] :id Source ID
|
|
45
|
+
# @option options [Symbol, String] :type Source type
|
|
46
|
+
# @option options [String] :name Source name
|
|
47
|
+
# @option options [String] :version Source version
|
|
48
|
+
# @option options [Hash] :metadata Source metadata
|
|
49
|
+
def initialize(options = {})
|
|
50
|
+
@id = options[:id]
|
|
51
|
+
@type = parse_source_type(options[:type])
|
|
52
|
+
@name = options[:name]
|
|
53
|
+
@version = options[:version]
|
|
54
|
+
@metadata = options[:metadata] || {}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Convert the source to a hash
|
|
58
|
+
#
|
|
59
|
+
# @return [Hash] The source as a hash
|
|
60
|
+
def to_h
|
|
61
|
+
{
|
|
62
|
+
name: @name,
|
|
63
|
+
version: @version,
|
|
64
|
+
type: @type.to_s.upcase,
|
|
65
|
+
identity: "#{@name}:#{@version}",
|
|
66
|
+
id: "#{@name}:#{@version}"
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Convert the source to JSON
|
|
71
|
+
#
|
|
72
|
+
# @return [String] The source as JSON
|
|
73
|
+
def to_json(*args)
|
|
74
|
+
to_h.to_json(*args)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Check if the source is valid
|
|
78
|
+
#
|
|
79
|
+
# @return [Boolean] True if valid, false otherwise
|
|
80
|
+
def valid?
|
|
81
|
+
!@id.nil? && !@id.empty? &&
|
|
82
|
+
!@name.nil? && !@name.empty? &&
|
|
83
|
+
SourceType.all.include?(@type)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def parse_source_type(type)
|
|
89
|
+
return SourceType::FIRST_PARTY if type.nil?
|
|
90
|
+
|
|
91
|
+
case type.to_s.downcase
|
|
92
|
+
when "first_party", "firstparty", "first-party"
|
|
93
|
+
SourceType::FIRST_PARTY
|
|
94
|
+
when "third_party", "thirdparty", "third-party"
|
|
95
|
+
SourceType::THIRD_PARTY
|
|
96
|
+
when "marketplace"
|
|
97
|
+
SourceType::MARKETPLACE
|
|
98
|
+
else
|
|
99
|
+
SourceType::FIRST_PARTY
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ComplyanceSDK
|
|
4
|
+
module Models
|
|
5
|
+
# Reference to a source using name and version (simplified identity)
|
|
6
|
+
# This replaces the need for source ID bookkeeping on the client side
|
|
7
|
+
class SourceRef
|
|
8
|
+
attr_reader :name, :version, :type
|
|
9
|
+
|
|
10
|
+
# Initialize a new source reference
|
|
11
|
+
#
|
|
12
|
+
# @param name [String] The source name
|
|
13
|
+
# @param version [String] The source version
|
|
14
|
+
# @param type [Symbol, String] The source type (defaults to :first_party)
|
|
15
|
+
# @raise [ArgumentError] If name or version is blank
|
|
16
|
+
def initialize(name, version, type = :first_party)
|
|
17
|
+
# Allow empty values for mapping purposes
|
|
18
|
+
@name = name.nil? ? '' : name.to_s.strip
|
|
19
|
+
@version = version.nil? ? '' : version.to_s.strip
|
|
20
|
+
@type = type
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Get the identity string (name:version)
|
|
24
|
+
#
|
|
25
|
+
# @return [String] The identity string
|
|
26
|
+
def identity
|
|
27
|
+
if @name.empty? && @version.empty?
|
|
28
|
+
"mapping:unknown"
|
|
29
|
+
else
|
|
30
|
+
"#{@name}:#{@version}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Convert to hash representation
|
|
35
|
+
#
|
|
36
|
+
# @return [Hash] Hash representation of the source reference
|
|
37
|
+
def to_h
|
|
38
|
+
{
|
|
39
|
+
name: @name,
|
|
40
|
+
version: @version,
|
|
41
|
+
type: @type.to_s.upcase,
|
|
42
|
+
identity: identity,
|
|
43
|
+
id: identity
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Convert to JSON-compatible hash (for API requests)
|
|
48
|
+
#
|
|
49
|
+
# @return [Hash] JSON-compatible hash
|
|
50
|
+
def to_json_hash
|
|
51
|
+
{
|
|
52
|
+
'name' => @name,
|
|
53
|
+
'version' => @version
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Create from hash
|
|
58
|
+
#
|
|
59
|
+
# @param hash [Hash] Hash with name and version keys
|
|
60
|
+
# @return [SourceRef] New source reference instance
|
|
61
|
+
def self.from_h(hash)
|
|
62
|
+
new(hash[:name] || hash['name'], hash[:version] || hash['version'])
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# String representation
|
|
66
|
+
#
|
|
67
|
+
# @return [String] String representation
|
|
68
|
+
def to_s
|
|
69
|
+
identity
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Inspect representation
|
|
73
|
+
#
|
|
74
|
+
# @return [String] Inspect representation
|
|
75
|
+
def inspect
|
|
76
|
+
"#<#{self.class.name}:#{object_id} name=#{@name.inspect} version=#{@version.inspect}>"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Equality comparison
|
|
80
|
+
#
|
|
81
|
+
# @param other [SourceRef] Other source reference to compare
|
|
82
|
+
# @return [Boolean] True if equal
|
|
83
|
+
def ==(other)
|
|
84
|
+
return false unless other.is_a?(SourceRef)
|
|
85
|
+
|
|
86
|
+
@name == other.name && @version == other.version
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Hash code for equality
|
|
90
|
+
#
|
|
91
|
+
# @return [Integer] Hash code
|
|
92
|
+
def hash
|
|
93
|
+
[@name, @version].hash
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Alias for equality
|
|
97
|
+
alias_method :eql?, :==
|
|
98
|
+
|
|
99
|
+
# Validate the source reference
|
|
100
|
+
#
|
|
101
|
+
# @return [Boolean] True if valid
|
|
102
|
+
def valid?
|
|
103
|
+
!@name.empty? && !@version.empty?
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Get validation errors
|
|
107
|
+
#
|
|
108
|
+
# @return [Array<String>] Array of validation error messages
|
|
109
|
+
def validation_errors
|
|
110
|
+
errors = []
|
|
111
|
+
errors << 'Name cannot be blank' if @name.empty?
|
|
112
|
+
errors << 'Version cannot be blank' if @version.empty?
|
|
113
|
+
errors
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Check if source reference is valid, raise error if not
|
|
117
|
+
#
|
|
118
|
+
# @raise [ComplyanceSDK::Exceptions::ValidationError] If invalid
|
|
119
|
+
def validate!
|
|
120
|
+
return true if valid?
|
|
121
|
+
|
|
122
|
+
error_message = validation_errors.join(', ')
|
|
123
|
+
raise ComplyanceSDK::Exceptions::ValidationError.new(
|
|
124
|
+
"Invalid source reference: #{error_message}",
|
|
125
|
+
context: { name: @name, version: @version }
|
|
126
|
+
)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module ComplyanceSDK
|
|
6
|
+
module Models
|
|
7
|
+
# UnifyRequest model representing the main API request structure
|
|
8
|
+
class UnifyRequest
|
|
9
|
+
# Source of the request
|
|
10
|
+
attr_accessor :source
|
|
11
|
+
|
|
12
|
+
# Document type
|
|
13
|
+
attr_accessor :document_type
|
|
14
|
+
|
|
15
|
+
# Country code
|
|
16
|
+
attr_accessor :country
|
|
17
|
+
|
|
18
|
+
# Operation type
|
|
19
|
+
attr_accessor :operation
|
|
20
|
+
|
|
21
|
+
# Mode
|
|
22
|
+
attr_accessor :mode
|
|
23
|
+
|
|
24
|
+
# Purpose
|
|
25
|
+
attr_accessor :purpose
|
|
26
|
+
|
|
27
|
+
# Request payload
|
|
28
|
+
attr_accessor :payload
|
|
29
|
+
|
|
30
|
+
# Destinations
|
|
31
|
+
attr_accessor :destinations
|
|
32
|
+
|
|
33
|
+
# Request metadata
|
|
34
|
+
attr_accessor :metadata
|
|
35
|
+
|
|
36
|
+
# Initialize a new UnifyRequest
|
|
37
|
+
#
|
|
38
|
+
# @param options [Hash] Request options
|
|
39
|
+
# @option options [Source] :source The source of the request
|
|
40
|
+
# @option options [Symbol, String] :document_type The document type
|
|
41
|
+
# @option options [String] :country The country code
|
|
42
|
+
# @option options [Symbol, String] :operation The operation type
|
|
43
|
+
# @option options [Symbol, String] :mode The mode
|
|
44
|
+
# @option options [Symbol, String] :purpose The purpose
|
|
45
|
+
# @option options [Hash] :payload The request payload
|
|
46
|
+
# @option options [Array] :destinations The destinations
|
|
47
|
+
# @option options [Hash] :metadata The request metadata
|
|
48
|
+
def initialize(options = {})
|
|
49
|
+
@source = options[:source]
|
|
50
|
+
@document_type = DocumentType.normalize(options[:document_type])
|
|
51
|
+
@country = options[:country]
|
|
52
|
+
@operation = Operation.normalize(options[:operation])
|
|
53
|
+
@mode = Mode.normalize(options[:mode])
|
|
54
|
+
@purpose = Purpose.normalize(options[:purpose])
|
|
55
|
+
@payload = options[:payload] || {}
|
|
56
|
+
@destinations = options[:destinations] || []
|
|
57
|
+
@metadata = build_metadata(options[:metadata] || {})
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Check if the request is valid
|
|
61
|
+
#
|
|
62
|
+
# @return [Boolean] True if valid, false otherwise
|
|
63
|
+
def valid?
|
|
64
|
+
errors.empty?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Get validation errors
|
|
68
|
+
#
|
|
69
|
+
# @return [Array<String>] Array of validation error messages
|
|
70
|
+
def errors
|
|
71
|
+
errors = []
|
|
72
|
+
|
|
73
|
+
errors << "Source is required" if @source.nil?
|
|
74
|
+
errors << "Source must be valid" if @source && !@source.valid?
|
|
75
|
+
errors << "Document type is required" if @document_type.nil?
|
|
76
|
+
errors << "Document type must be valid" unless DocumentType.valid?(@document_type)
|
|
77
|
+
errors << "Country is required" if @country.nil? || @country.empty?
|
|
78
|
+
errors << "Operation is required" if @operation.nil?
|
|
79
|
+
errors << "Operation must be valid" unless Operation.valid?(@operation)
|
|
80
|
+
errors << "Mode is required" if @mode.nil?
|
|
81
|
+
errors << "Mode must be valid" unless Mode.valid?(@mode)
|
|
82
|
+
errors << "Purpose is required" if @purpose.nil?
|
|
83
|
+
errors << "Purpose must be valid" unless Purpose.valid?(@purpose)
|
|
84
|
+
errors << "Payload is required" if @payload.nil? || @payload.empty?
|
|
85
|
+
|
|
86
|
+
errors
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Convert the request to a hash
|
|
90
|
+
#
|
|
91
|
+
# @return [Hash] The request as a hash
|
|
92
|
+
def to_h
|
|
93
|
+
{
|
|
94
|
+
source: @source&.to_h,
|
|
95
|
+
country: @country.to_s.upcase,
|
|
96
|
+
operation: @operation.to_s.upcase,
|
|
97
|
+
mode: @mode.to_s.upcase,
|
|
98
|
+
payload: @payload,
|
|
99
|
+
apiKey: @metadata[:api_key],
|
|
100
|
+
requestId: @metadata[:request_id],
|
|
101
|
+
timestamp: @metadata[:timestamp],
|
|
102
|
+
env: @metadata[:environment],
|
|
103
|
+
destinations: format_destinations(@destinations),
|
|
104
|
+
correlationId: @metadata[:correlation_id],
|
|
105
|
+
documentType: @document_type.to_s.upcase,
|
|
106
|
+
purpose: @purpose.to_s.downcase
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Convert the request to JSON
|
|
111
|
+
#
|
|
112
|
+
# @return [String] The request as JSON
|
|
113
|
+
def to_json(*args)
|
|
114
|
+
to_h.to_json(*args)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Create a UnifyRequest from a hash
|
|
118
|
+
#
|
|
119
|
+
# @param hash [Hash] The hash to convert
|
|
120
|
+
# @return [UnifyRequest] The UnifyRequest object
|
|
121
|
+
def self.from_h(hash)
|
|
122
|
+
return nil unless hash.is_a?(Hash)
|
|
123
|
+
|
|
124
|
+
options = hash.dup
|
|
125
|
+
|
|
126
|
+
# Convert source hash to Source object if needed
|
|
127
|
+
if options[:source].is_a?(Hash)
|
|
128
|
+
options[:source] = Source.new(options[:source])
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
new(options)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Create a UnifyRequest from JSON
|
|
135
|
+
#
|
|
136
|
+
# @param json [String] The JSON string to convert
|
|
137
|
+
# @return [UnifyRequest] The UnifyRequest object
|
|
138
|
+
def self.from_json(json)
|
|
139
|
+
hash = JSON.parse(json, symbolize_names: true)
|
|
140
|
+
from_h(hash)
|
|
141
|
+
rescue JSON::ParserError => e
|
|
142
|
+
raise ComplyanceSDK::Exceptions::ValidationError.new(
|
|
143
|
+
"Invalid JSON: #{e.message}",
|
|
144
|
+
context: { json: json }
|
|
145
|
+
)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
private
|
|
149
|
+
|
|
150
|
+
def format_destinations(destinations)
|
|
151
|
+
return [] if destinations.nil? || destinations.empty?
|
|
152
|
+
|
|
153
|
+
destinations.map do |dest|
|
|
154
|
+
# Handle both symbol and string keys
|
|
155
|
+
dest = dest.is_a?(Hash) ? dest : dest.to_h
|
|
156
|
+
|
|
157
|
+
# Check if destination has nested details structure or flat structure
|
|
158
|
+
if dest.key?(:details) || dest.key?("details")
|
|
159
|
+
# Nested structure: {type: "...", details: {country: "...", authority: "...", document_type: "..."}}
|
|
160
|
+
details = dest[:details] || dest["details"] || {}
|
|
161
|
+
document_type = details[:document_type] || details["document_type"]
|
|
162
|
+
else
|
|
163
|
+
# Flat structure: {type: "...", country: "...", authority: "...", document_type: "..."}
|
|
164
|
+
details = dest
|
|
165
|
+
document_type = dest[:document_type] || dest["document_type"]
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Map document_type to documentType based on the actual document type
|
|
169
|
+
# Logic: if it contains "credit" -> credit_note, if it contains "debit" -> debit_note, else tax_invoice
|
|
170
|
+
if document_type&.downcase&.include?("credit")
|
|
171
|
+
document_type = "credit_note"
|
|
172
|
+
elsif document_type&.downcase&.include?("debit")
|
|
173
|
+
document_type = "debit_note"
|
|
174
|
+
elsif document_type == "tax_invoice"
|
|
175
|
+
document_type = "tax_invoice" # Keep as tax_invoice for regular invoices
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
{
|
|
179
|
+
type: (dest[:type] || dest["type"])&.to_s&.upcase,
|
|
180
|
+
details: {
|
|
181
|
+
country: (details[:country] || details["country"])&.to_s,
|
|
182
|
+
authority: (details[:authority] || details["authority"])&.to_s,
|
|
183
|
+
documentType: document_type&.to_s
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def build_metadata(metadata)
|
|
190
|
+
default_metadata = {
|
|
191
|
+
api_key: nil, # Will be set by the HTTP client
|
|
192
|
+
request_id: generate_request_id,
|
|
193
|
+
timestamp: Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ'),
|
|
194
|
+
environment: nil, # Will be set by the HTTP client
|
|
195
|
+
sdk_version: ComplyanceSDK::VERSION,
|
|
196
|
+
sdk_language: "ruby"
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
default_metadata.merge(metadata)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def generate_request_id
|
|
203
|
+
require "securerandom"
|
|
204
|
+
SecureRandom.uuid
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module ComplyanceSDK
|
|
6
|
+
module Models
|
|
7
|
+
# UnifyResponse model representing the API response structure
|
|
8
|
+
class UnifyResponse
|
|
9
|
+
# Response status
|
|
10
|
+
attr_reader :status
|
|
11
|
+
|
|
12
|
+
# Response message
|
|
13
|
+
attr_reader :message
|
|
14
|
+
|
|
15
|
+
# Response data
|
|
16
|
+
attr_reader :data
|
|
17
|
+
|
|
18
|
+
# Error details
|
|
19
|
+
attr_reader :error
|
|
20
|
+
|
|
21
|
+
# Response metadata
|
|
22
|
+
attr_reader :metadata
|
|
23
|
+
|
|
24
|
+
# Initialize a new UnifyResponse
|
|
25
|
+
#
|
|
26
|
+
# @param options [Hash] Response options
|
|
27
|
+
# @option options [String] :status The response status
|
|
28
|
+
# @option options [String] :message The response message
|
|
29
|
+
# @option options [Hash] :data The response data
|
|
30
|
+
# @option options [Hash] :error The error details
|
|
31
|
+
# @option options [Hash] :metadata The response metadata
|
|
32
|
+
def initialize(options = {})
|
|
33
|
+
@status = options[:status]
|
|
34
|
+
@message = options[:message]
|
|
35
|
+
@data = options[:data]
|
|
36
|
+
@error = options[:error]
|
|
37
|
+
@metadata = options[:metadata] || {}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Check if the response indicates success
|
|
41
|
+
#
|
|
42
|
+
# @return [Boolean] True if successful, false otherwise
|
|
43
|
+
def success?
|
|
44
|
+
(@status == "success" || @status == "completed") && !has_validation_errors?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Check if the response indicates an error
|
|
48
|
+
#
|
|
49
|
+
# @return [Boolean] True if error, false otherwise
|
|
50
|
+
def error?
|
|
51
|
+
!success?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Check if the response has validation errors
|
|
55
|
+
#
|
|
56
|
+
# @return [Boolean] True if validation errors exist, false otherwise
|
|
57
|
+
def has_validation_errors?
|
|
58
|
+
return false unless @data.is_a?(Hash)
|
|
59
|
+
|
|
60
|
+
validation_errors = @data.dig("validation", "errors") || @data.dig(:validation, :errors)
|
|
61
|
+
validation_errors.is_a?(Array) && !validation_errors.empty?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Get validation errors from the response
|
|
65
|
+
#
|
|
66
|
+
# @return [Array] Array of validation error hashes
|
|
67
|
+
def validation_errors
|
|
68
|
+
return [] unless @data.is_a?(Hash)
|
|
69
|
+
|
|
70
|
+
validation_errors = @data.dig("validation", "errors") || @data.dig(:validation, :errors)
|
|
71
|
+
return [] unless validation_errors.is_a?(Array)
|
|
72
|
+
|
|
73
|
+
validation_errors
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Get the request ID from metadata
|
|
77
|
+
#
|
|
78
|
+
# @return [String, nil] The request ID
|
|
79
|
+
def request_id
|
|
80
|
+
@metadata[:request_id] || @metadata["request_id"]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Get the processing time from metadata
|
|
84
|
+
#
|
|
85
|
+
# @return [Numeric, nil] The processing time in milliseconds
|
|
86
|
+
def processing_time
|
|
87
|
+
@metadata[:processing_time] || @metadata["processing_time"]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Get the trace ID from metadata
|
|
91
|
+
#
|
|
92
|
+
# @return [String, nil] The trace ID
|
|
93
|
+
def trace_id
|
|
94
|
+
@metadata[:trace_id] || @metadata["trace_id"]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Get the submission ID from data
|
|
98
|
+
# Checks multiple possible locations: data.submission_id, data.submission.submissionId, data.submission.submission_id
|
|
99
|
+
#
|
|
100
|
+
# @return [String, nil] The submission ID
|
|
101
|
+
def submission_id
|
|
102
|
+
return nil unless @data.is_a?(Hash)
|
|
103
|
+
|
|
104
|
+
# Check direct submission_id
|
|
105
|
+
submission_id = @data[:submission_id] || @data["submission_id"]
|
|
106
|
+
return submission_id if submission_id
|
|
107
|
+
|
|
108
|
+
# Check nested submission.submissionId or submission.submission_id
|
|
109
|
+
submission = @data[:submission] || @data["submission"]
|
|
110
|
+
return nil unless submission.is_a?(Hash)
|
|
111
|
+
|
|
112
|
+
submission[:submissionId] || submission["submissionId"] ||
|
|
113
|
+
submission[:submission_id] || submission["submission_id"]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Convert the response to a hash
|
|
117
|
+
#
|
|
118
|
+
# @return [Hash] The response as a hash
|
|
119
|
+
def to_h
|
|
120
|
+
{
|
|
121
|
+
status: @status,
|
|
122
|
+
message: @message,
|
|
123
|
+
data: @data,
|
|
124
|
+
error: @error,
|
|
125
|
+
metadata: @metadata
|
|
126
|
+
}
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Convert the response to JSON
|
|
130
|
+
#
|
|
131
|
+
# @return [String] The response as JSON
|
|
132
|
+
def to_json(*args)
|
|
133
|
+
to_h.to_json(*args)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Create a UnifyResponse from a hash
|
|
137
|
+
#
|
|
138
|
+
# @param hash [Hash] The hash to convert
|
|
139
|
+
# @return [UnifyResponse] The UnifyResponse object
|
|
140
|
+
def self.from_h(hash)
|
|
141
|
+
return nil unless hash.is_a?(Hash)
|
|
142
|
+
|
|
143
|
+
# Convert string keys to symbol keys for consistency
|
|
144
|
+
normalized_hash = {}
|
|
145
|
+
hash.each do |key, value|
|
|
146
|
+
symbol_key = key.is_a?(String) ? key.to_sym : key
|
|
147
|
+
normalized_hash[symbol_key] = value
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
new(normalized_hash)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Create a UnifyResponse from JSON
|
|
154
|
+
#
|
|
155
|
+
# @param json [String] The JSON string to convert
|
|
156
|
+
# @return [UnifyResponse] The UnifyResponse object
|
|
157
|
+
def self.from_json(json)
|
|
158
|
+
hash = JSON.parse(json, symbolize_names: true)
|
|
159
|
+
from_h(hash)
|
|
160
|
+
rescue JSON::ParserError => e
|
|
161
|
+
raise ComplyanceSDK::Exceptions::ValidationError.new(
|
|
162
|
+
"Invalid JSON: #{e.message}",
|
|
163
|
+
context: { json: json }
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Create a success response
|
|
168
|
+
#
|
|
169
|
+
# @param data [Hash] The response data
|
|
170
|
+
# @param message [String] The success message
|
|
171
|
+
# @param metadata [Hash] The response metadata
|
|
172
|
+
# @return [UnifyResponse] The success response
|
|
173
|
+
def self.success(data = {}, message = "Success", metadata = {})
|
|
174
|
+
new(
|
|
175
|
+
status: "success",
|
|
176
|
+
message: message,
|
|
177
|
+
data: data,
|
|
178
|
+
metadata: metadata
|
|
179
|
+
)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Create an error response
|
|
183
|
+
#
|
|
184
|
+
# @param error [Hash] The error details
|
|
185
|
+
# @param message [String] The error message
|
|
186
|
+
# @param metadata [Hash] The response metadata
|
|
187
|
+
# @return [UnifyResponse] The error response
|
|
188
|
+
def self.error(error = {}, message = "Error", metadata = {})
|
|
189
|
+
new(
|
|
190
|
+
status: "error",
|
|
191
|
+
message: message,
|
|
192
|
+
error: error,
|
|
193
|
+
metadata: metadata
|
|
194
|
+
)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|