apimatic_core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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