apimatic_core 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 (35) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +26 -0
  3. data/README.md +101 -0
  4. data/lib/apimatic-core/api_call.rb +103 -0
  5. data/lib/apimatic-core/authentication/header_auth.rb +21 -0
  6. data/lib/apimatic-core/authentication/multiple/and_auth_group.rb +28 -0
  7. data/lib/apimatic-core/authentication/multiple/auth_group.rb +45 -0
  8. data/lib/apimatic-core/authentication/multiple/or_auth_group.rb +29 -0
  9. data/lib/apimatic-core/authentication/multiple/single_auth.rb +48 -0
  10. data/lib/apimatic-core/authentication/query_auth.rb +21 -0
  11. data/lib/apimatic-core/configurations/global_configuration.rb +149 -0
  12. data/lib/apimatic-core/exceptions/invalid_auth_credential.rb +5 -0
  13. data/lib/apimatic-core/factories/http_response_factory.rb +14 -0
  14. data/lib/apimatic-core/http/configurations/http_client_configuration.rb +32 -0
  15. data/lib/apimatic-core/http/request/http_request.rb +50 -0
  16. data/lib/apimatic-core/http/response/api_response.rb +35 -0
  17. data/lib/apimatic-core/http/response/http_response.rb +24 -0
  18. data/lib/apimatic-core/logger/endpoint_logger.rb +28 -0
  19. data/lib/apimatic-core/request_builder.rb +361 -0
  20. data/lib/apimatic-core/response_handler.rb +269 -0
  21. data/lib/apimatic-core/types/error_case.rb +37 -0
  22. data/lib/apimatic-core/types/parameter.rb +116 -0
  23. data/lib/apimatic-core/types/sdk/api_exception.rb +15 -0
  24. data/lib/apimatic-core/types/sdk/base_model.rb +22 -0
  25. data/lib/apimatic-core/types/sdk/file_wrapper.rb +11 -0
  26. data/lib/apimatic-core/types/sdk/validation_exception.rb +10 -0
  27. data/lib/apimatic-core/types/xml_attributes.rb +37 -0
  28. data/lib/apimatic-core/utilities/api_helper.rb +551 -0
  29. data/lib/apimatic-core/utilities/auth_helper.rb +49 -0
  30. data/lib/apimatic-core/utilities/comparison_helper.rb +77 -0
  31. data/lib/apimatic-core/utilities/date_time_helper.rb +126 -0
  32. data/lib/apimatic-core/utilities/file_helper.rb +21 -0
  33. data/lib/apimatic-core/utilities/xml_helper.rb +215 -0
  34. data/lib/apimatic_core.rb +44 -0
  35. metadata +215 -0
@@ -0,0 +1,551 @@
1
+ require 'erb'
2
+ module CoreLibrary
3
+ # API utility class involved in executing an API
4
+ class ApiHelper
5
+ # Serializes an array parameter (creates key value pairs).
6
+ # @param [String] key The name of the parameter.
7
+ # @param [Array] array The value of the parameter.
8
+ # @param [String] formatting The format of the serialization.
9
+ def self.serialize_array(key, array, formatting: 'indexed')
10
+ tuples = []
11
+
12
+ tuples += case formatting
13
+ when 'csv'
14
+ [[key, array.map { |element| CGI.escape(element.to_s) }.join(',')]]
15
+ when 'psv'
16
+ [[key, array.map { |element| CGI.escape(element.to_s) }.join('|')]]
17
+ when 'tsv'
18
+ [[key, array.map { |element| CGI.escape(element.to_s) }.join("\t")]]
19
+ else
20
+ array.map { |element| [key, element] }
21
+ end
22
+ tuples
23
+ end
24
+
25
+ # Deserializes primitive types like Boolean, String etc.
26
+ # @param response The response received.
27
+ # @param type Type to be deserialized.
28
+ # @param is_array Is the response provided an array or not
29
+ def self.deserialize_primitive_types(response, type, is_array, should_symbolize)
30
+ return json_deserialize(response, should_symbolize) if is_array
31
+ raise ArgumentError, 'callable has not been not provided for deserializer.' if type.nil?
32
+
33
+ type.call(response)
34
+ end
35
+
36
+ # Deserializes datetime.
37
+ # @param response The response received.
38
+ # @param datetime_format Current format of datetime.
39
+ # @param is_array Is the response provided an array or not
40
+ def self.deserialize_datetime(response, datetime_format, is_array, should_symbolize)
41
+ decoded = json_deserialize(response, should_symbolize) if is_array
42
+
43
+ case datetime_format
44
+ when DateTimeFormat::HTTP_DATE_TIME
45
+ return DateTimeHelper.from_rfc1123(response) unless is_array
46
+
47
+ decoded.map { |element| DateTimeHelper.from_rfc1123(element) }
48
+ when DateTimeFormat::RFC3339_DATE_TIME
49
+ return DateTimeHelper.from_rfc3339(response) unless is_array
50
+
51
+ decoded.map { |element| DateTimeHelper.from_rfc3339(element) }
52
+ when DateTimeFormat::UNIX_DATE_TIME
53
+ return DateTimeHelper.from_unix(response) unless is_array
54
+
55
+ decoded.map { |element| DateTimeHelper.from_unix(element) }
56
+ end
57
+ end
58
+
59
+ # Deserializes date.
60
+ # @param response The response received.
61
+ # @param is_array Is the response provided an array or not
62
+ def self.date_deserializer(response, is_array, should_symbolize)
63
+ if is_array
64
+ decoded = json_deserialize(response, should_symbolize)
65
+ return decoded.map { |element| Date.iso8601(element) }
66
+ end
67
+ Date.iso8601(response)
68
+ end
69
+
70
+ # Deserializer to use when the type of response is not known beforehand.
71
+ # @param response The response received.
72
+ def self.dynamic_deserializer(response, should_symbolize)
73
+ decoded = json_deserialize(response, should_symbolize) unless response.nil? ||
74
+ response.to_s.strip.empty?
75
+ decoded
76
+ end
77
+
78
+ # Deserializes response to a known custom model type.
79
+ # @param response The response received.
80
+ # @param deserialize_into The custom model type to deserialize into.
81
+ # @param is_array Is the response provided an array or not
82
+ def self.custom_type_deserializer(response, deserialize_into, is_array, should_symbolize)
83
+ decoded = json_deserialize(response, should_symbolize)
84
+ return deserialize_into.call(decoded) unless is_array
85
+
86
+ decoded.map { |element| deserialize_into.call(element) }
87
+ end
88
+
89
+ # Replaces template parameters in the given url.
90
+ # @param [String] query_builder The query string builder to replace the template
91
+ # parameters.
92
+ # @param [Hash] parameters The parameters to replace in the url.
93
+ def self.append_url_with_template_parameters(query_builder, parameters)
94
+ # perform parameter validation
95
+ unless query_builder.instance_of? String
96
+ raise ArgumentError, 'Given value for parameter \"query_builder\" is
97
+ invalid.'
98
+ end
99
+
100
+ # Return if there are no parameters to replace.
101
+ return query_builder if parameters.nil?
102
+
103
+ parameters.each do |key, val|
104
+ if val.nil?
105
+ replace_value = ''
106
+ elsif val['value'].instance_of? Array
107
+ if val['encode'] == true
108
+ val['value'].map! { |element| CGI.escape(element.to_s) }
109
+ else
110
+ val['value'].map!(&:to_s)
111
+ end
112
+ replace_value = val['value'].join('/')
113
+ else
114
+ replace_value = if val['encode'] == true
115
+ CGI.escape(val['value'].to_s)
116
+ else
117
+ val['value'].to_s
118
+ end
119
+ end
120
+
121
+ # Find the template parameter and replace it with its value.
122
+ query_builder = query_builder.gsub("{#{key}}", replace_value)
123
+ end
124
+ query_builder
125
+ end
126
+
127
+ # Replaces the template parameters in the given user-agent string.
128
+ # @param [String] user_agent The user_agent value to be replaced with the given
129
+ # parameters.
130
+ # @param [Hash] parameters The parameters to replace in the user_agent.
131
+ def self.update_user_agent_value_with_parameters(user_agent, parameters)
132
+ # perform parameter validation
133
+ unless user_agent.instance_of? String
134
+ raise ArgumentError, 'Given value for \"user_agent\" is
135
+ invalid.'
136
+ end
137
+
138
+ # Return if there are no parameters to replace.
139
+ return user_agent if parameters.nil?
140
+
141
+ parameters.each do |key, val|
142
+ if val.nil?
143
+ replace_value = ''
144
+ elsif val['value'].instance_of? Array
145
+ if val['encode'] == true
146
+ val['value'].map! { |element| ERB::Util.url_encode(element.to_s) }
147
+ else
148
+ val['value'].map!(&:to_s)
149
+ end
150
+ replace_value = val['value'].join('/')
151
+ else
152
+ replace_value = if val['encode'] == true
153
+ ERB::Util.url_encode(val['value'].to_s)
154
+ else
155
+ val['value'].to_s
156
+ end
157
+ end
158
+
159
+ # Find the template parameter and replace it with its value.
160
+ user_agent = user_agent.gsub(key.to_s, replace_value)
161
+ end
162
+ user_agent
163
+ end
164
+
165
+ # Appends the given set of parameters to the given query string.
166
+ # @param [String] query_builder The query string builder to add the query parameters to.
167
+ # @param [Hash] parameters The parameters to append.
168
+ # @param [String] array_serialization The serialization format
169
+ def self.append_url_with_query_parameters(query_builder, parameters,
170
+ array_serialization = ArraySerializationFormat::INDEXED)
171
+ # Perform parameter validation.
172
+ unless query_builder.instance_of? String
173
+ raise ArgumentError, 'Given value for parameter \"query_builder\"
174
+ is invalid.'
175
+ end
176
+
177
+ # Return if there are no parameters to replace.
178
+ return query_builder if parameters.nil?
179
+
180
+ parameters = process_complex_types_parameters(parameters, array_serialization)
181
+
182
+ parameters.each do |key, value|
183
+ seperator = query_builder.include?('?') ? '&' : '?'
184
+ unless value.nil?
185
+ if value.instance_of? Array
186
+ value.compact!
187
+ serialize_array(
188
+ key, value, formatting: array_serialization
189
+ ).each do |element|
190
+ seperator = query_builder.include?('?') ? '&' : '?'
191
+ query_builder += "#{seperator}#{element[0]}=#{element[1]}"
192
+ end
193
+ else
194
+ query_builder += "#{seperator}#{key}=#{CGI.escape(value.to_s)}"
195
+ end
196
+ end
197
+ end
198
+ query_builder
199
+ end
200
+
201
+ # Validates and processes the given Url.
202
+ # @param [String] url The given Url to process.
203
+ # @return [String] Pre-processed Url as string.
204
+ def self.clean_url(url)
205
+ # Perform parameter validation.
206
+ raise ArgumentError, 'Invalid Url.' unless url.instance_of? String
207
+
208
+ # Ensure that the urls are absolute.
209
+ matches = url.match(%r{^(https?://[^/]+)})
210
+ raise ArgumentError, 'Invalid Url format.' if matches.nil?
211
+
212
+ # Get the http protocol match.
213
+ protocol = matches[1]
214
+
215
+ # Check if parameters exist.
216
+ index = url.index('?')
217
+
218
+ # Remove redundant forward slashes.
219
+ query = url[protocol.length...(!index.nil? ? index : url.length)]
220
+ query.gsub!(%r{//+}, '/')
221
+
222
+ # Get the parameters.
223
+ parameters = !index.nil? ? url[url.index('?')...url.length] : ''
224
+
225
+ # Return processed url.
226
+ protocol + query + parameters
227
+ end
228
+
229
+ # Parses JSON string.
230
+ # @param [String] json A JSON string.
231
+ # rubocop:disable Style/OptionalBooleanParameter
232
+ def self.json_deserialize(json, should_symbolize = false)
233
+ JSON.parse(json, symbolize_names: should_symbolize)
234
+ rescue StandardError
235
+ raise TypeError, 'Server responded with invalid JSON.'
236
+ end
237
+ # rubocop:enable Style/OptionalBooleanParameter
238
+
239
+ # Parses JSON string.
240
+ # @param [object] obj The object to serialize.
241
+ def self.json_serialize(obj)
242
+ serializable_types.map { |x| obj.is_a? x }.any? ? obj.to_s : obj.to_json
243
+ end
244
+
245
+ # Removes elements with empty values from a hash.
246
+ # @param [Hash] hash The hash to clean.
247
+ def self.clean_hash(hash)
248
+ hash.delete_if { |_key, value| value.to_s.strip.empty? }
249
+ end
250
+
251
+ # Form encodes a hash of parameters.
252
+ # @param [Hash] form_parameters The hash of parameters to encode.
253
+ # @return [Hash] A hash with the same parameters form encoded.
254
+ def self.form_encode_parameters(form_parameters, array_serialization)
255
+ encoded = {}
256
+ form_parameters.each do |key, value|
257
+ encoded.merge!(form_encode(value, key, formatting:
258
+ array_serialization))
259
+ end
260
+ encoded
261
+ end
262
+
263
+ # Process complex types in query_params.
264
+ # @param [Hash] query_parameters The hash of query parameters.
265
+ # @return [Hash] array_serialization A hash with the processed query parameters.
266
+ def self.process_complex_types_parameters(query_parameters, array_serialization)
267
+ processed_params = {}
268
+ query_parameters.each do |key, value|
269
+ processed_params.merge!(ApiHelper.form_encode(value, key, formatting:
270
+ array_serialization))
271
+ end
272
+ processed_params
273
+ end
274
+
275
+ def self.custom_merge(a, b)
276
+ x = {}
277
+ a.each do |key, value_a|
278
+ b.each do |k, value_b|
279
+ next unless key == k
280
+
281
+ x[k] = []
282
+ if value_a.instance_of? Array
283
+ value_a.each do |v|
284
+ x[k].push(v)
285
+ end
286
+ else
287
+ x[k].push(value_a)
288
+ end
289
+ if value_b.instance_of? Array
290
+ value_b.each do |v|
291
+ x[k].push(v)
292
+ end
293
+ else
294
+ x[k].push(value_b)
295
+ end
296
+ a.delete(k)
297
+ b.delete(k)
298
+ end
299
+ end
300
+ x.merge!(a)
301
+ x.merge!(b)
302
+ x
303
+ end
304
+
305
+ # Form encodes an object.
306
+ # @param [Dynamic] obj An object to form encode.
307
+ # @param [String] instance_name The name of the object.
308
+ # @return [Hash] A form encoded representation of the object in the form
309
+ # of a hash.
310
+ def self.form_encode(obj, instance_name, formatting: ArraySerializationFormat::INDEXED)
311
+ retval = {}
312
+
313
+ # If this is a structure, resolve it's field names.
314
+ obj = obj.to_hash if obj.is_a? BaseModel
315
+
316
+ # Create a form encoded hash for this object.
317
+ if obj.nil?
318
+ nil
319
+ elsif obj.instance_of? Array
320
+ if formatting == ArraySerializationFormat::INDEXED
321
+ obj.each_with_index do |value, index|
322
+ retval.merge!(form_encode(value, "#{instance_name}[#{index}]"))
323
+ end
324
+ elsif serializable_types.map { |x| obj[0].is_a? x }.any?
325
+ obj.each do |value|
326
+ abc = if formatting == ArraySerializationFormat::UN_INDEXED
327
+ form_encode(value, "#{instance_name}[]",
328
+ formatting: formatting)
329
+ else
330
+ form_encode(value, instance_name,
331
+ formatting: formatting)
332
+ end
333
+ retval = custom_merge(retval, abc)
334
+ end
335
+ else
336
+ obj.each_with_index do |value, index|
337
+ retval.merge!(form_encode(value, "#{instance_name}[#{index}]",
338
+ formatting: formatting))
339
+ end
340
+ end
341
+ elsif obj.instance_of? Hash
342
+ obj.each do |key, value|
343
+ retval.merge!(form_encode(value, "#{instance_name}[#{key}]",
344
+ formatting: formatting))
345
+ end
346
+ elsif obj.instance_of? File
347
+ retval[instance_name] = UploadIO.new(
348
+ obj, 'application/octet-stream', File.basename(obj.path)
349
+ )
350
+ else
351
+ retval[instance_name] = obj
352
+ end
353
+ retval
354
+ end
355
+
356
+ # Retrieves a field from a Hash/Array based on an Array of keys/indexes
357
+ # @param [Hash, Array] obj The hash to extract data from
358
+ # @param [Array<String, Integer>] keys The keys/indexes to use
359
+ # @return [Object] The extracted value
360
+ def self.map_response(obj, keys)
361
+ val = obj
362
+ begin
363
+ keys.each do |key|
364
+ val = if val.is_a? Array
365
+ if key.to_i.to_s == key
366
+ val[key.to_i]
367
+ else
368
+ val = nil
369
+ end
370
+ else
371
+ val.fetch(key.to_sym)
372
+ end
373
+ end
374
+ rescue NoMethodError, TypeError, IndexError
375
+ val = nil
376
+ end
377
+ val
378
+ end
379
+
380
+ # Deserialize the value against the template (group of types).
381
+ # @param [String] value The value to be deserialized.
382
+ # @param [String] template The type-combination group for which the value will be mapped (oneOf(Integer, String)).
383
+ def self.deserialize(template, value, sdk_module, should_symbolize)
384
+ decoded = json_deserialize(value, should_symbolize)
385
+ map_types(decoded, template, sdk_module: sdk_module)
386
+ end
387
+
388
+ # Validates and processes the value against the template(group of types).
389
+ # @param [String] value The value to be mapped against the template.
390
+ # @param [String] template The parameter indicates the group of types (oneOf(Integer, String)).
391
+ # @param [String] group_name The parameter indicates the group (oneOf|anyOf).
392
+ def self.map_types(value, template, group_name: nil, sdk_module: nil)
393
+ result_value = nil
394
+ matches = 0
395
+ types = []
396
+ group_name = template.partition('(').first if group_name.nil? && template.match?(/anyOf|oneOf/)
397
+
398
+ return if value.nil?
399
+
400
+ if template.end_with?('{}') || template.end_with?('[]')
401
+ types = template.split(group_name, 2).last.gsub(/\s+/, '').split
402
+ else
403
+ template = template.split(group_name, 2).last.delete_prefix('(').delete_suffix(')')
404
+ types = template.scan(/(anyOf|oneOf)[(]([^[)]]*)[)]/).flatten.combination(2).map { |a, b| "#{a}(#{b})" }
405
+ types.each { |t| template = template.gsub(", #{t}", '') }
406
+ types = template.gsub(/\s+/, '').split(',').push(*types)
407
+ end
408
+ types.each do |element|
409
+ if element.match?(/^(oneOf|anyOf)[(].*$/)
410
+ begin
411
+ result_value = map_types(value, element, matches)
412
+ matches += 1
413
+ rescue ValidationException
414
+ next
415
+ end
416
+ elsif element.end_with?('{}')
417
+ result_value, matches = map_hash_type(value, element, group_name, matches)
418
+ elsif element.end_with?('[]')
419
+ result_value, matches = map_array_type(value, element, group_name, matches)
420
+ else
421
+ begin
422
+ result_value, matches = map_type(value, element, group_name, matches, sdk_module)
423
+ rescue StandardError
424
+ next
425
+ end
426
+ end
427
+ break if group_name == 'anyOf' && matches == 1
428
+ end
429
+ raise ValidationException, "The value #{value} provided doesn't validate against the schema #{template}" unless
430
+ matches == 1
431
+
432
+ value = result_value unless result_value.nil?
433
+ value
434
+ end
435
+
436
+ # Validates and processes the value against the [Hash] type.
437
+ # @param [String] value The value to be mapped against the type.
438
+ # @param [String] type The possible type of the value.
439
+ # @param [String] group_name The parameter indicates the group (oneOf|anyOf).
440
+ # @param [Integer] matches The parameter indicates the number of matches of value against types.
441
+ def self.map_hash_type(value, type, group_name, matches)
442
+ if value.instance_of? Hash
443
+ decoded = {}
444
+ value.each do |key, val|
445
+ type = type.chomp('{}').to_s
446
+ val = map_types(val, type, group_name: group_name)
447
+ decoded[key] = val unless type.empty?
448
+ rescue ValidationException
449
+ next
450
+ end
451
+ matches += 1 if decoded.length == value.length
452
+ value = decoded unless decoded.empty?
453
+ end
454
+ [value, matches]
455
+ end
456
+
457
+ # Validates and processes the value against the [Array] type.
458
+ # @param [String] value The value to be mapped against the type.
459
+ # @param [String] type The possible type of the value.
460
+ # @param [String] group_name The parameter indicates the group (oneOf|anyOf).
461
+ # @param [Integer] matches The parameter indicates the number of matches of value against types.
462
+ def self.map_array_type(value, type, group_name, matches)
463
+ if value.instance_of? Array
464
+ decoded = []
465
+ value.each do |val|
466
+ type = type.chomp('[]').to_s
467
+ val = map_types(val, type, group_name: group_name)
468
+ decoded.append(val) unless type.empty?
469
+ rescue ValidationException
470
+ next
471
+ end
472
+ matches += 1 if decoded.length == value.length
473
+ value = decoded unless decoded.empty?
474
+ end
475
+ [value, matches]
476
+ end
477
+
478
+ # Validates and processes the value against the type.
479
+ # @param [String] value The value to be mapped against the type.
480
+ # @param [String] type The possible type of the value.
481
+ # @param [String] _group_name The parameter indicates the group (oneOf|anyOf).
482
+ # @param [Integer] matches The parameter indicates the number of matches of value against types.
483
+ def self.map_type(value, type, _group_name, matches, sdk_module)
484
+ if sdk_module.constants.select do |c|
485
+ sdk_module.const_get(c).to_s == "#{sdk_module}::#{type}"
486
+ end.empty?
487
+ value, matches = map_data_type(value, type, matches)
488
+ else
489
+ value, matches = map_complex_type(value, type, matches, sdk_module)
490
+ end
491
+ [value, matches]
492
+ end
493
+
494
+ # Validates and processes the value against the complex types.
495
+ # @param [String] value The value to be mapped against the type.
496
+ # @param [String] type The possible type of the value.
497
+ # @param [Integer] matches The parameter indicates the number of matches of value against types.
498
+ def self.map_complex_type(value, type, matches, sdk_module)
499
+ # TODO: Add a nil check on sdk_module?
500
+ obj = sdk_module.const_get(type)
501
+ value = if obj.respond_to? 'from_hash'
502
+ obj.send('from_hash', value)
503
+ else
504
+ obj.constants.find { |k| obj.const_get(k) == value }
505
+ end
506
+ matches += 1 unless value.nil?
507
+ [value, matches]
508
+ end
509
+
510
+ # Validates and processes the value against the data types.
511
+ # @param [String] value The value to be mapped against the type.
512
+ # @param [String] element The possible type of the value.
513
+ # @param [Integer] matches The parameter indicates the number of matches of value against types.
514
+ def self.map_data_type(value, element, matches)
515
+ element = element.split('|').map { |x| Object.const_get x }
516
+ matches += 1 if element.all? { |x| data_types.include?(x) } &&
517
+ element.any? { |x| (value.instance_of? x) || (value.class.ancestors.include? x) }
518
+ [value, matches]
519
+ end
520
+
521
+ # Validates the value against the template(group of types).
522
+ # @param [String] value The value to be mapped against the type.
523
+ # @param [String] template The parameter indicates the group of types (oneOf(Integer, String)).
524
+ def self.validate_types(value, template, sdk_module, should_symbolize)
525
+ map_types(json_deserialize(value.to_json, should_symbolize), template, sdk_module: sdk_module)
526
+ end
527
+
528
+ # Get content-type depending on the value
529
+ # @param [Object] value The value for which the content-type is resolved.
530
+ def self.get_content_type(value)
531
+ if serializable_types.map { |x| value.is_a? x }.any?
532
+ 'text/plain; charset=utf-8'
533
+ else
534
+ 'application/json; charset=utf-8'
535
+ end
536
+ end
537
+
538
+ # Array of serializable types
539
+ def self.serializable_types
540
+ [String, Numeric, TrueClass,
541
+ FalseClass, Date, DateTime]
542
+ end
543
+
544
+ # Array of supported data types
545
+ def self.data_types
546
+ [String, Float, Integer,
547
+ TrueClass, FalseClass, Date,
548
+ DateTime, Array, Hash, Object]
549
+ end
550
+ end
551
+ end
@@ -0,0 +1,49 @@
1
+ require 'base64'
2
+
3
+ module CoreLibrary
4
+ # A utility class for authentication flow
5
+ class AuthHelper
6
+ # Performs the Base64 encodes the provided parameters after joining the parameters with delimiter.
7
+ # @param [String] *props The string properties which should participate in encoding.
8
+ # @param [String|Optional] delimiter The delimiter to use while joining the properties.
9
+ # @return [String] The encoded Base64 string.
10
+ def self.get_base64_encoded_value(*props, delimiter: ':')
11
+ return if props.any?(&:nil?)
12
+
13
+ joined = props.join(delimiter)
14
+ Base64.strict_encode64(joined)
15
+ end
16
+
17
+ # Checks if OAuth token has expired.
18
+ # @param [int] token_expiry The expiring of a token.
19
+ # @return [Boolean] true if token has expired, false otherwise.
20
+ def self.token_expired?(token_expiry)
21
+ raise ArgumentError, 'Token expiry can not be nil.' if token_expiry.nil?
22
+
23
+ token_expiry < Time.now.utc.to_i
24
+ end
25
+
26
+ # Calculates the expiry after adding the expires_in value to the current timestamp.
27
+ # @param [int] expires_in The number of ticks after which the token would get expired.
28
+ # @param [int] current_timestamp The current timestamp.
29
+ # @return [Time] The calculated expiry time of the token.
30
+ def self.get_token_expiry(expires_in, current_timestamp)
31
+ current_timestamp + expires_in
32
+ end
33
+
34
+ # Checks whether the provided auth parameters does not contain any nil key/value.
35
+ # @param [Hash] auth_params The auth parameters hash to check against.
36
+ # @return [Boolean] True if there is not any nil key/value in the given auth parameters.
37
+ def self.valid_auth?(auth_params)
38
+ !auth_params.nil? and !auth_params.empty? and
39
+ auth_params.all? { |key, value| !(key.nil? or value.nil?) }
40
+ end
41
+
42
+ # Applies callable to each entry of the hash.
43
+ # @param [Hash] auth_params The auth parameters hash to apply against.
44
+ # @param [Callable] func The callable function to apply for each entry of the provided hash.
45
+ def self.apply(auth_params, func)
46
+ auth_params.each { |key, value| func.call(key, value) }
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,77 @@
1
+ module CoreLibrary
2
+ # A utility that perform the comparison of the Response body and headers.
3
+ class ComparisonHelper
4
+
5
+ # Class method to compare the received headers with the expected headers.
6
+ # @param [Hash] expected_headers A hash of expected headers (keys in lower case).
7
+ # @param [Hash] actual_headers A hash of actual headers.
8
+ # @param [Boolean, optional] allow_extra A flag which determines if we allow extra headers.
9
+ def self.match_headers(expected_headers,
10
+ actual_headers,
11
+ allow_extra: true)
12
+ return false if ((actual_headers.length < expected_headers.length) ||
13
+ ((allow_extra == false) && (actual_headers.length > expected_headers.length)))
14
+
15
+ actual_headers = Hash[actual_headers.map{|k, v| [k.to_s.downcase, v]}]
16
+ expected_headers = Hash[expected_headers.map{|k, v| [k.to_s.downcase, v]}]
17
+
18
+ expected_headers.each do |e_key, e_value|
19
+ return false unless actual_headers.key?(e_key)
20
+ return false if ((e_value != nil) &&
21
+ (e_value != actual_headers[e_key]))
22
+ end
23
+
24
+ return true
25
+ end
26
+
27
+ # Class method to compare the received body with the expected body.
28
+ # @param [Dynamic] expected_body The expected body.
29
+ # @param [Dynamic] actual_body The actual body.
30
+ # @param [Boolean, optional] check_values A flag which determines if we check values in dictionaries.
31
+ # @param [Boolean, optional] check_order A flag which determines if we check the order of array elements.
32
+ # @param [Boolean, optional] check_count A flag which determines if we check the count of array elements.
33
+ def self.match_body(expected_body,
34
+ actual_body,
35
+ check_values: false,
36
+ check_order: false,
37
+ check_count: false)
38
+ if expected_body.instance_of? Hash
39
+ return false unless actual_body.instance_of? Hash
40
+ for key in expected_body.keys
41
+ return false unless actual_body.keys.include? key
42
+ if check_values or expected_body[key].instance_of? Hash
43
+ return false unless match_body(expected_body[key],
44
+ actual_body[key],
45
+ check_values: check_values,
46
+ check_order: check_order,
47
+ check_count: check_count)
48
+ end
49
+ end
50
+ elsif expected_body.instance_of? Array
51
+ return false unless actual_body.instance_of? Array
52
+ if check_count == true && (expected_body.length != actual_body.length)
53
+ return false
54
+ else
55
+ previous_matches = Array.new
56
+ expected_body.each.with_index do |expected_element, i|
57
+ matches = (actual_body.map.with_index do |received_element, j|
58
+ j if match_body(expected_element,
59
+ received_element,
60
+ check_values: check_values,
61
+ check_order: check_order,
62
+ check_count: check_count)
63
+ end).compact
64
+ return false if matches.length == 0
65
+ if check_order == true
66
+ return false if (i != 0 && matches.map{|x| previous_matches.map{|y| y > x}.all?}.all?)
67
+ previous_matches = matches
68
+ end
69
+ end
70
+ end
71
+ elsif expected_body != actual_body
72
+ return false
73
+ end
74
+ return true
75
+ end
76
+ end
77
+ end