appnigma-sdk 0.1.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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +359 -0
  4. data/lib/appnigma/checks.rb +90 -0
  5. data/lib/appnigma/client.rb +118 -0
  6. data/lib/appnigma/connections.rb +31 -0
  7. data/lib/appnigma/constants.rb +80 -0
  8. data/lib/appnigma/crm.rb +40 -0
  9. data/lib/appnigma/errors.rb +135 -0
  10. data/lib/appnigma/generated/api/default_api.rb +972 -0
  11. data/lib/appnigma/generated/api_client.rb +441 -0
  12. data/lib/appnigma/generated/api_error.rb +58 -0
  13. data/lib/appnigma/generated/api_model_base.rb +88 -0
  14. data/lib/appnigma/generated/configuration.rb +392 -0
  15. data/lib/appnigma/generated/models/connection_status.rb +41 -0
  16. data/lib/appnigma/generated/models/create_connection_link_input.rb +265 -0
  17. data/lib/appnigma/generated/models/create_connection_link_output.rb +205 -0
  18. data/lib/appnigma/generated/models/crm_company.rb +322 -0
  19. data/lib/appnigma/generated/models/crm_company_get_output.rb +177 -0
  20. data/lib/appnigma/generated/models/crm_company_list_output.rb +192 -0
  21. data/lib/appnigma/generated/models/crm_contact.rb +404 -0
  22. data/lib/appnigma/generated/models/crm_contact_get_output.rb +177 -0
  23. data/lib/appnigma/generated/models/crm_contact_list_output.rb +192 -0
  24. data/lib/appnigma/generated/models/crm_custom_field.rb +189 -0
  25. data/lib/appnigma/generated/models/crm_deal.rb +479 -0
  26. data/lib/appnigma/generated/models/crm_deal_get_output.rb +177 -0
  27. data/lib/appnigma/generated/models/crm_deal_list_output.rb +192 -0
  28. data/lib/appnigma/generated/models/crm_email.rb +249 -0
  29. data/lib/appnigma/generated/models/crm_get_input.rb +272 -0
  30. data/lib/appnigma/generated/models/crm_labelled.rb +190 -0
  31. data/lib/appnigma/generated/models/crm_list_input.rb +303 -0
  32. data/lib/appnigma/generated/models/crm_phone.rb +249 -0
  33. data/lib/appnigma/generated/models/function_entry.rb +346 -0
  34. data/lib/appnigma/generated/models/function_owner.rb +105 -0
  35. data/lib/appnigma/generated/models/function_owner_one_of.rb +201 -0
  36. data/lib/appnigma/generated/models/function_owner_one_of1.rb +227 -0
  37. data/lib/appnigma/generated/models/get_connection_output.rb +251 -0
  38. data/lib/appnigma/generated/models/get_guide_output.rb +178 -0
  39. data/lib/appnigma/generated/models/json_value.rb +106 -0
  40. data/lib/appnigma/generated/models/list_functions_output.rb +179 -0
  41. data/lib/appnigma/generated/models/platform_request_input.rb +314 -0
  42. data/lib/appnigma/generated/models/platform_request_input_body.rb +104 -0
  43. data/lib/appnigma/generated/models/platform_request_output.rb +221 -0
  44. data/lib/appnigma/generated/models/post_input.rb +327 -0
  45. data/lib/appnigma/generated/models/post_input_record_value.rb +104 -0
  46. data/lib/appnigma/generated/models/post_output.rb +203 -0
  47. data/lib/appnigma/generated/models/pull_input.rb +335 -0
  48. data/lib/appnigma/generated/models/pull_output.rb +191 -0
  49. data/lib/appnigma/generated/models/pull_output_records_inner.rb +205 -0
  50. data/lib/appnigma/generated/models/run_input.rb +332 -0
  51. data/lib/appnigma/generated/models/run_output.rb +164 -0
  52. data/lib/appnigma/generated/models/sdk_error.rb +177 -0
  53. data/lib/appnigma/generated/models/sdk_error_error.rb +254 -0
  54. data/lib/appnigma/generated/models/sdk_error_error_details.rb +211 -0
  55. data/lib/appnigma/generated/models/sdk_problem.rb +205 -0
  56. data/lib/appnigma/generated/version.rb +15 -0
  57. data/lib/appnigma/generated.rb +82 -0
  58. data/lib/appnigma/names.rb +62 -0
  59. data/lib/appnigma/retry.rb +80 -0
  60. data/lib/appnigma/transport.rb +138 -0
  61. data/lib/appnigma/version.rb +9 -0
  62. data/lib/appnigma.rb +16 -0
  63. metadata +155 -0
@@ -0,0 +1,205 @@
1
+ =begin
2
+ #Appnigma SDK API
3
+
4
+ #The routes a customer's product calls through an Appnigma SDK. Every answer carries a request id in X-Request-Id; quote it when you write to support.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.26.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module Appnigma::Generated
17
+ class SdkProblem < ApiModelBase
18
+ # The field, as a dotted path for a nested one
19
+ attr_accessor :field
20
+
21
+ # What is wrong with it, in plain words
22
+ attr_accessor :problem
23
+
24
+ # Attribute mapping from ruby-style variable name to JSON key.
25
+ def self.attribute_map
26
+ {
27
+ :'field' => :'field',
28
+ :'problem' => :'problem'
29
+ }
30
+ end
31
+
32
+ # Returns attribute mapping this model knows about
33
+ def self.acceptable_attribute_map
34
+ attribute_map
35
+ end
36
+
37
+ # Returns all the JSON keys this model knows about
38
+ def self.acceptable_attributes
39
+ acceptable_attribute_map.values
40
+ end
41
+
42
+ # Attribute type mapping.
43
+ def self.openapi_types
44
+ {
45
+ :'field' => :'String',
46
+ :'problem' => :'String'
47
+ }
48
+ end
49
+
50
+ # List of attributes with nullable: true
51
+ def self.openapi_nullable
52
+ Set.new([
53
+ ])
54
+ end
55
+
56
+ # Returns attribute type mapping
57
+ def self.acceptable_openapi_types
58
+ openapi_types
59
+ end
60
+
61
+ # Returns the nullable attributes
62
+ def self.acceptable_openapi_nullable
63
+ openapi_nullable
64
+ end
65
+
66
+ # Initializes the object
67
+ # @param [Hash] attributes Model attributes in the form of hash
68
+ def initialize(attributes = {})
69
+ if (!attributes.is_a?(Hash))
70
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Appnigma::Generated::SdkProblem` initialize method"
71
+ end
72
+
73
+ # check to see if the attribute exists and convert string to symbol for hash key
74
+ acceptable_attribute_map = self.class.acceptable_attribute_map
75
+ attributes = attributes.each_with_object({}) { |(k, v), h|
76
+ if (!acceptable_attribute_map.key?(k.to_sym))
77
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Appnigma::Generated::SdkProblem`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
78
+ end
79
+ h[k.to_sym] = v
80
+ }
81
+
82
+ if attributes.key?(:'field')
83
+ self.field = attributes[:'field']
84
+ else
85
+ self.field = nil
86
+ end
87
+
88
+ if attributes.key?(:'problem')
89
+ self.problem = attributes[:'problem']
90
+ else
91
+ self.problem = nil
92
+ end
93
+ end
94
+
95
+ # Show invalid properties with the reasons. Usually used together with valid?
96
+ # @return Array for valid properties with the reasons
97
+ def list_invalid_properties
98
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
99
+ invalid_properties = Array.new
100
+ if @field.nil?
101
+ invalid_properties.push('invalid value for "field", field cannot be nil.')
102
+ end
103
+
104
+ if @problem.nil?
105
+ invalid_properties.push('invalid value for "problem", problem cannot be nil.')
106
+ end
107
+
108
+ invalid_properties
109
+ end
110
+
111
+ # Check to see if the all the properties in the model are valid
112
+ # @return true if the model is valid
113
+ def valid?
114
+ warn '[DEPRECATED] the `valid?` method is obsolete'
115
+ return false if @field.nil?
116
+ return false if @problem.nil?
117
+ true
118
+ end
119
+
120
+ # Custom attribute writer method with validation
121
+ # @param [Object] field Value to be assigned
122
+ def field=(field)
123
+ if field.nil?
124
+ fail ArgumentError, 'field cannot be nil'
125
+ end
126
+
127
+ @field = field
128
+ end
129
+
130
+ # Custom attribute writer method with validation
131
+ # @param [Object] problem Value to be assigned
132
+ def problem=(problem)
133
+ if problem.nil?
134
+ fail ArgumentError, 'problem cannot be nil'
135
+ end
136
+
137
+ @problem = problem
138
+ end
139
+
140
+ # Checks equality by comparing each attribute.
141
+ # @param [Object] Object to be compared
142
+ def ==(o)
143
+ return true if self.equal?(o)
144
+ self.class == o.class &&
145
+ field == o.field &&
146
+ problem == o.problem
147
+ end
148
+
149
+ # @see the `==` method
150
+ # @param [Object] Object to be compared
151
+ def eql?(o)
152
+ self == o
153
+ end
154
+
155
+ # Calculates hash code according to all attributes.
156
+ # @return [Integer] Hash code
157
+ def hash
158
+ [field, problem].hash
159
+ end
160
+
161
+ # Builds the object from hash
162
+ # @param [Hash] attributes Model attributes in the form of hash
163
+ # @return [Object] Returns the model itself
164
+ def self.build_from_hash(attributes)
165
+ return nil unless attributes.is_a?(Hash)
166
+ attributes = attributes.transform_keys(&:to_sym)
167
+ # include attributes inherited from allOf parents (the nearest declaration wins)
168
+ map = acceptable_attribute_map
169
+ transformed_hash = {}
170
+ acceptable_openapi_types.each_pair do |key, type|
171
+ if attributes.key?(map[key]) && attributes[map[key]].nil?
172
+ transformed_hash["#{key}"] = nil
173
+ elsif type =~ /\AArray<(.*)>/i
174
+ # check to ensure the input is an array given that the attribute
175
+ # is documented as an array but the input is not
176
+ if attributes[map[key]].is_a?(Array)
177
+ transformed_hash["#{key}"] = attributes[map[key]].map { |v| _deserialize($1, v) }
178
+ end
179
+ elsif !attributes[map[key]].nil?
180
+ transformed_hash["#{key}"] = _deserialize(type, attributes[map[key]])
181
+ end
182
+ end
183
+ new(transformed_hash)
184
+ end
185
+
186
+ # Returns the object in the form of hash
187
+ # @return [Hash] Returns the object in the form of hash
188
+ def to_hash
189
+ hash = {}
190
+ # include attributes inherited from allOf parents (the nearest declaration wins)
191
+ self.class.acceptable_attribute_map.each_pair do |attr, param|
192
+ value = self.send(attr)
193
+ if value.nil?
194
+ is_nullable = self.class.acceptable_openapi_nullable.include?(attr)
195
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
196
+ end
197
+
198
+ hash[param] = _to_hash(value)
199
+ end
200
+ hash
201
+ end
202
+
203
+ end
204
+
205
+ end
@@ -0,0 +1,15 @@
1
+ =begin
2
+ #Appnigma SDK API
3
+
4
+ #The routes a customer's product calls through an Appnigma SDK. Every answer carries a request id in X-Request-Id; quote it when you write to support.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.26.0
10
+
11
+ =end
12
+
13
+ module Appnigma::Generated
14
+ VERSION = '1.0.0'
15
+ end
@@ -0,0 +1,82 @@
1
+ =begin
2
+ #Appnigma SDK API
3
+
4
+ #The routes a customer's product calls through an Appnigma SDK. Every answer carries a request id in X-Request-Id; quote it when you write to support.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.26.0
10
+
11
+ =end
12
+
13
+ # Common files
14
+ require 'appnigma/generated/api_client'
15
+ require 'appnigma/generated/api_error'
16
+ require 'appnigma/generated/api_model_base'
17
+ require 'appnigma/generated/version'
18
+ require 'appnigma/generated/configuration'
19
+
20
+ # Models
21
+ require 'appnigma/generated/models/connection_status'
22
+ require 'appnigma/generated/models/create_connection_link_input'
23
+ require 'appnigma/generated/models/create_connection_link_output'
24
+ require 'appnigma/generated/models/crm_company'
25
+ require 'appnigma/generated/models/crm_company_get_output'
26
+ require 'appnigma/generated/models/crm_company_list_output'
27
+ require 'appnigma/generated/models/crm_contact'
28
+ require 'appnigma/generated/models/crm_contact_get_output'
29
+ require 'appnigma/generated/models/crm_contact_list_output'
30
+ require 'appnigma/generated/models/crm_custom_field'
31
+ require 'appnigma/generated/models/crm_deal'
32
+ require 'appnigma/generated/models/crm_deal_get_output'
33
+ require 'appnigma/generated/models/crm_deal_list_output'
34
+ require 'appnigma/generated/models/crm_email'
35
+ require 'appnigma/generated/models/crm_get_input'
36
+ require 'appnigma/generated/models/crm_labelled'
37
+ require 'appnigma/generated/models/crm_list_input'
38
+ require 'appnigma/generated/models/crm_phone'
39
+ require 'appnigma/generated/models/function_entry'
40
+ require 'appnigma/generated/models/function_owner'
41
+ require 'appnigma/generated/models/function_owner_one_of'
42
+ require 'appnigma/generated/models/function_owner_one_of1'
43
+ require 'appnigma/generated/models/get_connection_output'
44
+ require 'appnigma/generated/models/get_guide_output'
45
+ require 'appnigma/generated/models/json_value'
46
+ require 'appnigma/generated/models/list_functions_output'
47
+ require 'appnigma/generated/models/platform_request_input'
48
+ require 'appnigma/generated/models/platform_request_input_body'
49
+ require 'appnigma/generated/models/platform_request_output'
50
+ require 'appnigma/generated/models/post_input'
51
+ require 'appnigma/generated/models/post_input_record_value'
52
+ require 'appnigma/generated/models/post_output'
53
+ require 'appnigma/generated/models/pull_input'
54
+ require 'appnigma/generated/models/pull_output'
55
+ require 'appnigma/generated/models/pull_output_records_inner'
56
+ require 'appnigma/generated/models/run_input'
57
+ require 'appnigma/generated/models/run_output'
58
+ require 'appnigma/generated/models/sdk_error'
59
+ require 'appnigma/generated/models/sdk_error_error'
60
+ require 'appnigma/generated/models/sdk_error_error_details'
61
+ require 'appnigma/generated/models/sdk_problem'
62
+
63
+ # APIs
64
+ require 'appnigma/generated/api/default_api'
65
+
66
+ module Appnigma::Generated
67
+ class << self
68
+ # Customize default settings for the SDK using block.
69
+ # Appnigma::Generated.configure do |config|
70
+ # config.username = "xxx"
71
+ # config.password = "xxx"
72
+ # end
73
+ # If no block given, return the default Configuration object.
74
+ def configure
75
+ if block_given?
76
+ yield(Configuration.default)
77
+ else
78
+ Configuration.default
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appnigma
4
+ # The wire speaks camelCase; Ruby speaks snake_case. Only the known fields
5
+ # of a call cross this boundary: free-form content (a record, a function's
6
+ # input or output, a CRM record's raw form) passes through untouched.
7
+ module Names
8
+ module_function
9
+
10
+ def to_snake(name)
11
+ name.to_s.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
12
+ end
13
+
14
+ def to_camel(name)
15
+ first, *rest = name.to_s.split("_")
16
+ ([first] + rest.map(&:capitalize)).join
17
+ end
18
+
19
+ # The top level of an answer: known keys become snake_case symbols,
20
+ # values stay as they came. `only` names the keys to convert; the rest
21
+ # are kept under their wire name.
22
+ def snake_keys(hash, only: nil)
23
+ hash.to_h do |key, value|
24
+ convert = only.nil? || only.include?(key.to_s)
25
+ [convert ? to_snake(key).to_sym : key, value]
26
+ end
27
+ end
28
+
29
+ # Wire keys whose value is someone else's content, passed through as it
30
+ # came: a record's properties, a function's output and its schemas, a
31
+ # platform's headers and body, a CRM record's raw form, a custom field's
32
+ # value.
33
+ FREE_FORM = %w[properties output inputSchema outputSchema headers body raw value].freeze
34
+
35
+ # Wire keys whose value is a map keyed by someone else's names: the keys
36
+ # are kept, the values converted.
37
+ KEYED_BY_DATA = %w[customFields].freeze
38
+
39
+ # A whole answer: every key this gem knows becomes a snake_case symbol,
40
+ # at every depth, except inside free-form content.
41
+ def answer(value)
42
+ case value
43
+ when Array then value.map { |item| answer(item) }
44
+ when Hash then value.to_h { |key, item| [to_snake(key).to_sym, answer_field(key, item)] }
45
+ else value
46
+ end
47
+ end
48
+
49
+ def answer_field(key, value)
50
+ return value if FREE_FORM.include?(key)
51
+ return value.transform_values { |item| answer(item) } if KEYED_BY_DATA.include?(key) && value.is_a?(Hash)
52
+
53
+ answer(value)
54
+ end
55
+
56
+ # The top level of a request: snake_case keyword arguments become the
57
+ # wire's camelCase string keys, and a nil value is left out.
58
+ def camel_keys(hash)
59
+ hash.compact.to_h { |key, value| [to_camel(key), value] }
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "errors"
4
+
5
+ module Appnigma
6
+ # Tries again only when trying again is safe: the network failed, the
7
+ # server answered 5xx, or it answered 429. Never on 400, 401, 403, 404 or
8
+ # 409, because the same request would be refused the same way. The same
9
+ # rules, waits and jitter as the TypeScript package.
10
+ module Retry
11
+ # The wait before each retry when the server sent no Retry-After, with
12
+ # the last one repeated when there are more retries than steps.
13
+ BACKOFF_MS = [500, 1000].freeze
14
+
15
+ # Up to this share of the backoff is added at random, so many clients
16
+ # that failed together do not all try again in the same instant.
17
+ JITTER_SHARE = 0.25
18
+
19
+ # `sleep` takes milliseconds; `random` answers a number in [0, 1). Tests
20
+ # pin them; callers leave them nil for the real ones.
21
+ Policy = Struct.new(:max_retries, :max_retry_wait_ms, :sleep, :random, keyword_init: true)
22
+
23
+ DEFAULT_SLEEP = ->(ms) { Kernel.sleep(ms / 1000.0) }
24
+ DEFAULT_RANDOM = -> { Kernel.rand }
25
+
26
+ module_function
27
+
28
+ def retryable_status?(status)
29
+ status == 429 || status >= 500
30
+ end
31
+
32
+ # Retry-After wins when the server sent one and is taken as given: that
33
+ # wait is the server's instruction. Otherwise the fixed backoff plus
34
+ # jitter.
35
+ def wait_before_retry(answer, retry_index, random)
36
+ return answer[:retry_after_seconds] * 1000 if answer && !answer[:retry_after_seconds].nil?
37
+
38
+ base = BACKOFF_MS[[retry_index, BACKOFF_MS.length - 1].min]
39
+ base + (base * JITTER_SHARE * random.call).floor
40
+ end
41
+
42
+ # Runs the block up to `max_retries + 1` times. Returns the last answer,
43
+ # good or bad, so the caller judges it; raises only when no answer ever
44
+ # came back. Anything other than a NetworkError is a bug, not a bad
45
+ # second, and is raised at once.
46
+ def with_retries(policy, &block)
47
+ random = policy.random || DEFAULT_RANDOM
48
+ pause = policy.sleep || DEFAULT_SLEEP
49
+ retry_index = 0
50
+ loop do
51
+ answer = attempt(retry_index < policy.max_retries, &block)
52
+ wait_ms = wait_or_stop(answer, retry_index, policy, random)
53
+ return answer if wait_ms.nil?
54
+
55
+ pause.call(wait_ms)
56
+ retry_index += 1
57
+ end
58
+ end
59
+
60
+ # How long to wait before the next attempt, or nil to hand this answer
61
+ # back. A nil answer is a network failure with retries left (attempt
62
+ # raises when none are left).
63
+ def wait_or_stop(answer, retry_index, policy, random)
64
+ return wait_before_retry(nil, retry_index, random) if answer.nil?
65
+ return nil if !retryable_status?(answer[:status]) || retry_index >= policy.max_retries
66
+
67
+ wait_ms = wait_before_retry(answer, retry_index, random)
68
+ wait_ms > policy.max_retry_wait_ms ? nil : wait_ms
69
+ end
70
+
71
+ # One attempt: the answer, or nil when a network failure may be retried.
72
+ def attempt(retries_left)
73
+ yield
74
+ rescue NetworkError
75
+ raise unless retries_left
76
+
77
+ nil
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "uri"
5
+ require "faraday"
6
+ require_relative "constants"
7
+ require_relative "errors"
8
+ require_relative "version"
9
+
10
+ # The generated client is loaded with Ruby's warnings off. Its models
11
+ # redefine every setter after declaring it, and its API file requires "cgi",
12
+ # which Ruby 4.0 trimmed to the escaping it still uses; both only print
13
+ # under `ruby -w`, and neither is ours to change. The gem's own files load
14
+ # with warnings as usual.
15
+ begin
16
+ verbose = $VERBOSE
17
+ $VERBOSE = nil
18
+ require "appnigma/generated"
19
+ ensure
20
+ $VERBOSE = verbose
21
+ end
22
+
23
+ module Appnigma
24
+ # One attempt at one call, through the client generated from the API
25
+ # description. The only hand-written file that touches Appnigma::Generated.
26
+ # It reports what came back without judging it: the client decides what
27
+ # an answer means, and Retry decides whether to try again. The only thing
28
+ # raised here is NetworkError: nothing came back.
29
+ #
30
+ # The generated client knows the routes, the path and query values and the
31
+ # key header. It is asked for the body as text, which is parsed here with
32
+ # string keys, so free-form content (a record, a function's output) comes
33
+ # back exactly as the server sent it, and a body that is not JSON is seen
34
+ # as such.
35
+ class Transport
36
+ USER_AGENT = "appnigma-sdk-ruby/#{VERSION}".freeze
37
+
38
+ # Builds the generated API for one configuration. Tests pass their own.
39
+ DEFAULT_API_FACTORY = ->(config) { Generated::DefaultApi.new(Generated::ApiClient.new(config)) }
40
+
41
+ def initialize(base_url:, key:, api_factory: DEFAULT_API_FACTORY)
42
+ @base_url = URI.parse(base_url)
43
+ @key = key
44
+ @api_factory = api_factory
45
+ @apis = {}
46
+ @lock = Mutex.new
47
+ end
48
+
49
+ # Calls `<operation>_with_http_info` on the generated API with `args`,
50
+ # within `timeout_ms`. `opts` carries the generated method's optional
51
+ # values (catalog's platform). `header_params` adds request headers (a
52
+ # test uses it to pick a mock server's example). Answers
53
+ # `{ status:, ok:, request_id:, retry_after_seconds:, json: }`, where
54
+ # `json` is nil when the body was empty or not JSON.
55
+ def attempt(operation, *args, timeout_ms:, header_params: {}, opts: {})
56
+ body, status, headers = call_generated(operation, args, timeout_ms, header_params, opts)
57
+ answer(status, headers, body)
58
+ rescue Generated::ApiError => e
59
+ raise network_error(e, timeout_ms) if e.code.nil? || e.code.zero?
60
+
61
+ answer(e.code, e.response_headers, e.response_body)
62
+ rescue Faraday::Error => e
63
+ raise NetworkError.new(message: "Could not reach the Appnigma API: #{e.message}", cause: e)
64
+ end
65
+
66
+ private
67
+
68
+ def call_generated(operation, args, timeout_ms, header_params, opts)
69
+ api_for(timeout_ms).public_send(
70
+ :"#{operation}_with_http_info", *args, **opts, debug_return_type: "String", header_params: header_params.dup
71
+ )
72
+ end
73
+
74
+ def api_for(timeout_ms)
75
+ @lock.synchronize { @apis[timeout_ms] ||= @api_factory.call(configuration(timeout_ms)) }
76
+ end
77
+
78
+ def configuration(timeout_ms)
79
+ config = Generated::Configuration.new
80
+ config.scheme = @base_url.scheme
81
+ config.host = @base_url.port == @base_url.default_port ? @base_url.host : "#{@base_url.host}:#{@base_url.port}"
82
+ config.base_path = @base_url.path
83
+ config.access_token = @key
84
+ config.timeout = timeout_ms / 1000.0
85
+ # The server refuses a bad input, with the shape every other refusal
86
+ # has; the generated client's own checks would raise ArgumentError.
87
+ config.client_side_validation = false
88
+ config
89
+ end
90
+
91
+ def answer(status, headers, body)
92
+ {
93
+ status: status,
94
+ ok: (200..299).cover?(status),
95
+ request_id: header(headers, REQUEST_ID_HEADER),
96
+ retry_after_seconds: read_retry_after(header(headers, RETRY_AFTER_HEADER)),
97
+ json: parse_json(body)
98
+ }
99
+ end
100
+
101
+ def network_error(error, timeout_ms)
102
+ message = if error.message.start_with?("Connection timed out")
103
+ "No answer from the Appnigma API within #{timeout_ms} ms"
104
+ else
105
+ "Could not reach the Appnigma API: #{error.message.lines.first&.strip}"
106
+ end
107
+ NetworkError.new(message: message, cause: error)
108
+ end
109
+
110
+ # Header names are case-insensitive; Faraday's headers already are, a
111
+ # plain Hash is not.
112
+ def header(headers, name)
113
+ return nil if headers.nil?
114
+
115
+ headers.each { |key, value| return value if key.to_s.casecmp?(name) }
116
+ nil
117
+ end
118
+
119
+ # Our server sends seconds. Anything else (an HTTP date from a proxy,
120
+ # junk) reads as "not given" rather than as a wait.
121
+ def read_retry_after(value)
122
+ return nil if value.nil?
123
+
124
+ seconds = Float(value, exception: false)
125
+ return nil if seconds.nil? || !seconds.finite? || seconds.negative?
126
+
127
+ seconds == seconds.floor ? seconds.to_i : seconds
128
+ end
129
+
130
+ def parse_json(body)
131
+ return nil if body.nil? || body.to_s.empty?
132
+
133
+ JSON.parse(body.to_s)
134
+ rescue JSON::ParserError
135
+ nil
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appnigma
4
+ VERSION = "0.1.0"
5
+
6
+ # The version of the API description (openapi/sdk-v1.yaml) this gem was
7
+ # generated from. Written by bin/generate; a test ties it to the file.
8
+ API_DESCRIPTION_VERSION = "1.0.0"
9
+ end
data/lib/appnigma.rb ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "appnigma/version"
4
+ require_relative "appnigma/constants"
5
+ require_relative "appnigma/errors"
6
+ require_relative "appnigma/retry"
7
+ require_relative "appnigma/names"
8
+ require_relative "appnigma/checks"
9
+ require_relative "appnigma/transport"
10
+ require_relative "appnigma/connections"
11
+ require_relative "appnigma/crm"
12
+ require_relative "appnigma/client"
13
+
14
+ # The gem a customer's product installs to reach its Appnigma microserver.
15
+ module Appnigma
16
+ end