cognito-client 0.4.1 → 0.5.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -1
  3. data/Gemfile +37 -0
  4. data/Guardfile +4 -0
  5. data/README.md +55 -18
  6. data/Rakefile +3 -5
  7. data/bin/console +1 -1
  8. data/circle.yml +7 -0
  9. data/cognito.gemspec +4 -6
  10. data/config/devtools.yml +2 -0
  11. data/config/errors.yml +2 -0
  12. data/config/flay.yml +5 -0
  13. data/config/flog.yml +5 -0
  14. data/config/mutant.yml +6 -0
  15. data/config/reek.yml +111 -0
  16. data/config/rubocop.yml +91 -0
  17. data/config/yardstick.yml +2 -0
  18. data/lib/cognito/client.rb +59 -96
  19. data/lib/cognito/client/command.rb +34 -0
  20. data/lib/cognito/client/commands/create_identity.rb +22 -0
  21. data/lib/cognito/client/commands/create_identity_assessment.rb +26 -0
  22. data/lib/cognito/client/commands/create_identity_search.rb +37 -0
  23. data/lib/cognito/client/commands/create_profile.rb +24 -0
  24. data/lib/cognito/client/commands/mixins/create_behavior.rb +20 -0
  25. data/lib/cognito/client/commands/retrieve_identity_location.rb +28 -0
  26. data/lib/cognito/client/commands/retrieve_identity_search_job.rb +40 -0
  27. data/lib/cognito/client/connection.rb +36 -0
  28. data/lib/cognito/client/document.rb +32 -0
  29. data/lib/cognito/client/params.rb +13 -0
  30. data/lib/cognito/client/params/identity.rb +48 -0
  31. data/lib/cognito/client/params/identity_assessment.rb +23 -0
  32. data/lib/cognito/client/params/identity_search.rb +25 -0
  33. data/lib/cognito/client/params/omitted.rb +9 -0
  34. data/lib/cognito/client/request.rb +98 -0
  35. data/lib/cognito/client/resource.rb +124 -0
  36. data/lib/cognito/client/resource/identity_assessment.rb +53 -0
  37. data/lib/cognito/client/resource/identity_search.rb +64 -0
  38. data/lib/cognito/client/resource/identity_search_job.rb +12 -0
  39. data/lib/cognito/client/resource/profile.rb +10 -0
  40. data/lib/cognito/client/resource_identifier.rb +17 -0
  41. data/lib/cognito/client/response.rb +17 -0
  42. data/lib/cognito/client/response/builder.rb +48 -0
  43. data/lib/cognito/client/response/identity_assessment.rb +10 -0
  44. data/lib/cognito/client/response/identity_search.rb +32 -0
  45. data/lib/cognito/client/response/identity_search_job.rb +20 -0
  46. data/lib/cognito/client/response/profile.rb +10 -0
  47. data/lib/cognito/version.rb +3 -3
  48. metadata +77 -83
  49. data/.rubocop.yml +0 -57
  50. data/.travis.yml +0 -4
  51. data/lib/cognito.rb +0 -32
  52. data/lib/cognito/cleaner.rb +0 -30
  53. data/lib/cognito/constants.rb +0 -8
  54. data/lib/cognito/document.rb +0 -33
  55. data/lib/cognito/error.rb +0 -8
  56. data/lib/cognito/notary.rb +0 -56
  57. data/lib/cognito/resource.rb +0 -129
  58. data/lib/cognito/resource/identity_assessment.rb +0 -40
  59. data/lib/cognito/resource/identity_search.rb +0 -63
  60. data/lib/cognito/resource/identity_search_job.rb +0 -11
  61. data/lib/cognito/resource/profile.rb +0 -9
  62. data/lib/cognito/responder.rb +0 -61
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ class Cognito
3
+ class Client
4
+ class Document
5
+ include Anima.new(:data, :included), Adamantium
6
+
7
+ def initialize(data:, included: [])
8
+ super
9
+ end
10
+
11
+ def resolve(resource_object)
12
+ lookup.fetch(resource_object).first
13
+ end
14
+
15
+ def include(resource)
16
+ with(included: included + resource.included)
17
+ end
18
+
19
+ private
20
+
21
+ def lookup
22
+ resources.group_by(&ResourceIdentifier)
23
+ end
24
+ memoize(:lookup)
25
+
26
+ def resources
27
+ included + [data]
28
+ end
29
+ memoize :resources
30
+ end # Document
31
+ end # Client
32
+ end # Cognito
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ class Cognito
3
+ class Client
4
+ class Params
5
+ include Procto.call(:to_h),
6
+ AbstractType,
7
+ Adamantium
8
+
9
+ abstract_method :to_h
10
+ end # Params
11
+ private_constant(:Params)
12
+ end # Client
13
+ end # Cognito
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Cognito
4
+ class Client
5
+ class Params
6
+ class Identity < self
7
+ include AbstractType, Anima.new(:name, :phone_number)
8
+
9
+ abstract_method :relationships
10
+
11
+ def to_h
12
+ relationship_params.tap do |params|
13
+ params[:data][:attributes] = attributes unless attributes.empty?
14
+ end
15
+ end
16
+
17
+ def relationship_params
18
+ {
19
+ data: {
20
+ type: self.class::TYPE,
21
+ relationships: relationships
22
+ }
23
+ }
24
+ end
25
+
26
+ private
27
+
28
+ def attributes
29
+ full_attributes.reject { |_, val| val.instance_of?(Omitted) }
30
+ end
31
+ memoize :attributes
32
+
33
+ def full_attributes
34
+ {
35
+ name: name,
36
+ phone: phone
37
+ }
38
+ end
39
+
40
+ def phone
41
+ return phone_number if phone_number.instance_of?(Omitted)
42
+
43
+ { number: phone_number }
44
+ end
45
+ end # Identity
46
+ end # Params
47
+ end # Client
48
+ end # Cognito
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Cognito
4
+ class Client
5
+ class Params
6
+ class IdentityAssessment < Identity
7
+ include anima.add(:identity_search_identifier)
8
+
9
+ TYPE = 'identity_assessment'
10
+
11
+ private
12
+
13
+ def relationships
14
+ {
15
+ identity_search: {
16
+ data: identity_search_identifier
17
+ }
18
+ }
19
+ end
20
+ end # IdentityAssessment
21
+ end # Params
22
+ end # Client
23
+ end # Cognito
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Cognito
4
+ class Client
5
+ class Params
6
+ class IdentitySearch < Identity
7
+ include anima.add(:profile_id)
8
+
9
+ TYPE = 'identity_search'
10
+ RELATIONSHIP_TYPE = 'profile'
11
+
12
+ def relationships
13
+ {
14
+ profile: {
15
+ data: {
16
+ type: RELATIONSHIP_TYPE,
17
+ id: profile_id
18
+ }
19
+ }
20
+ }
21
+ end
22
+ end # IdentitySearch
23
+ end # Params
24
+ end # Client
25
+ end # Cognito
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ class Cognito
3
+ class Client
4
+ class Params
5
+ # Helpful type for clarifying when a caller intentionally omitted a param
6
+ Omitted = Class.new
7
+ end # Params
8
+ end # Client
9
+ end # Cognito
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+ class Cognito
3
+ class Client
4
+ class Request
5
+ CONSTANT_HEADERS = IceNine.deep_freeze(
6
+ 'Content-Type' => 'application/vnd.api+json'
7
+ )
8
+
9
+ include Anima.new(:verb, :endpoint, :data, :headers), Adamantium
10
+
11
+ def self.post(*arguments)
12
+ build(:post, *arguments)
13
+ end
14
+
15
+ def self.get(endpoint)
16
+ build(:get, endpoint, nil)
17
+ end
18
+
19
+ def self.build(verb, endpoint, data)
20
+ new(
21
+ verb: verb,
22
+ endpoint: endpoint,
23
+ data: data,
24
+ headers: CONSTANT_HEADERS
25
+ )
26
+ end
27
+ private_class_method :build
28
+
29
+ def sign(**signature_params)
30
+ with(headers: SigningHeaders.call(request: self, **signature_params))
31
+ end
32
+
33
+ class SigningHeaders
34
+ include Procto.call(:to_h), Anima.new(:request, :date, :api_key, :api_secret), Adamantium
35
+
36
+ DIGEST_HEADER = 'SHA-256=%<digest>s'
37
+ EMPTY_BODY = ''
38
+ SHA256 = IceNine.deep_freeze(OpenSSL::Digest::SHA256.new)
39
+
40
+ AUTHORIZATION_STRING = [
41
+ 'Signature keyId="%<api_key>s"',
42
+ 'algorithm="hmac-sha256"',
43
+ 'headers="date digest (request-target)"',
44
+ 'signature="%<signature>s"'
45
+ ].join(',').freeze
46
+
47
+ SIGNING_STRING = [
48
+ 'date: %<date>s',
49
+ 'digest: %<digest>s',
50
+ '(request-target): %<request_target>s'
51
+ ].join("\n").freeze
52
+
53
+ def to_h
54
+ request.headers.merge(
55
+ 'Authorization' => auth_header,
56
+ 'Digest' => digest_header,
57
+ 'Date' => date
58
+ )
59
+ end
60
+
61
+ private
62
+
63
+ def auth_header
64
+ format(AUTHORIZATION_STRING, api_key: api_key, signature: signature)
65
+ end
66
+
67
+ def signature
68
+ Base64.strict_encode64(OpenSSL::HMAC.digest(SHA256, api_secret, signing_string))
69
+ end
70
+
71
+ def signing_string
72
+ format(SIGNING_STRING, date: date, digest: digest_header, request_target: request_target)
73
+ end
74
+
75
+ def digest_header
76
+ format(DIGEST_HEADER, digest: digest)
77
+ end
78
+
79
+ def request_target
80
+ "#{request.verb} #{request.endpoint}"
81
+ end
82
+
83
+ def digest
84
+ Base64.strict_encode64(SHA256.digest(request_body))
85
+ end
86
+ memoize :digest
87
+
88
+ def request_body
89
+ return EMPTY_BODY unless request.data
90
+
91
+ JSON.dump(request.data)
92
+ end
93
+
94
+ private_constant(*constants(false))
95
+ end # SigningHeaders
96
+ end # Request
97
+ end # Client
98
+ end # Cognito
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+ class Cognito
3
+ class Client
4
+ class Resource
5
+ include AbstractType,
6
+ Memoizable,
7
+ Anima.new(:id, :type, :attributes, :relationships, :parent)
8
+
9
+ class MissingRelation < StandardError
10
+ MESSAGE = "Registered relation key, '%s', not in relationships"\
11
+ ' for resource: %p'
12
+
13
+ def initialize(key, resource)
14
+ super(format(MESSAGE, *[key, resource]))
15
+ end
16
+ end # MissingRelation
17
+
18
+ def self.register_type(type)
19
+ REGISTRY.add(type, self)
20
+ end
21
+ private_class_method :register_type
22
+
23
+ # ignores :reek:TooManyStatements:
24
+ def self.one(key)
25
+ define_relationship(key) do
26
+ identifier = relationship_data(key)
27
+ return unless identifier
28
+
29
+ find_identifier(identifier)
30
+ end
31
+ end
32
+ private_class_method :one
33
+
34
+ def self.many(key)
35
+ define_relationship(key) do
36
+ relationship_data(key).map(&method(:find_identifier))
37
+ end
38
+ end
39
+ private_class_method :many
40
+
41
+ def self.attribute(key, method_name = key)
42
+ define_method(method_name) do
43
+ attributes[key]
44
+ end
45
+ end
46
+
47
+ def self.define_relationship(key, &block)
48
+ memoize(define_method(key, &block))
49
+ end
50
+ private_class_method :define_relationship
51
+
52
+ def self.build(data, parent)
53
+ new(data.merge(parent: parent))
54
+ end
55
+
56
+ def include(resource)
57
+ with(parent: parent.include(resource))
58
+ end
59
+
60
+ def included
61
+ parent.included
62
+ end
63
+
64
+ def initialize(relationships: nil, **options)
65
+ super
66
+ end
67
+
68
+ protected
69
+
70
+ def resolve(identifier)
71
+ parent.resolve(identifier)
72
+ end
73
+
74
+ private
75
+
76
+ def find_identifier(identifier)
77
+ resource_class = REGISTRY.lookup(identifier.fetch(:type).to_sym)
78
+ resource_class.build(resolve(identifier), self)
79
+ end
80
+
81
+ def relationship_data(key)
82
+ fail MissingRelation.new(key, self) unless relationships && relationships.key?(key)
83
+
84
+ relationships.fetch(key).fetch(:data)
85
+ end
86
+
87
+ class Registry
88
+ include Concord.new(:registry)
89
+
90
+ class Error < StandardError
91
+ def initialize(type)
92
+ super(format(self.class::MESSAGE, type: type))
93
+ end
94
+ end # Error
95
+
96
+ class MissingError < Error
97
+ MESSAGE = 'Resource type not registered: %<type>s'
98
+ end # MissingError
99
+
100
+ class DuplicateTypeError < Error
101
+ MESSAGE = 'Resource type already registered: %<type>s'
102
+ end # DuplicateTypeError
103
+
104
+ def initialize
105
+ super({})
106
+ end
107
+
108
+ def lookup(type)
109
+ registry.fetch(type) do
110
+ fail MissingError, type
111
+ end
112
+ end
113
+
114
+ def add(type, resource_class)
115
+ fail DuplicateTypeError, type if registry.key?(type)
116
+
117
+ registry[type] = resource_class
118
+ end
119
+ end # Registry
120
+
121
+ REGISTRY = Registry.new
122
+ end # Resource
123
+ end # Client
124
+ end # Cognito
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ class Cognito
3
+ class Client
4
+ class Resource
5
+ class IdentityAssessment < self
6
+ register_type :identity_assessment
7
+
8
+ attribute :name
9
+ attribute :phone
10
+
11
+ many :identity_record_comparisons
12
+ end # IdentityAssessment
13
+
14
+ class IdentityRecordComparison < self
15
+ register_type :identity_record_comparison
16
+
17
+ many :name_comparisons
18
+ many :phone_comparisons
19
+ one :identity_record
20
+
21
+ attribute :score
22
+ end # IdentityRecordComparison
23
+
24
+ class PhoneComparison < self
25
+ register_type :phone_comparison
26
+
27
+ attribute :score
28
+
29
+ one :source_record
30
+ end # PhoneComparison
31
+
32
+ class NameComparison < self
33
+ register_type :name_comparison
34
+
35
+ attribute :score
36
+
37
+ one :source_record
38
+
39
+ class Components
40
+ include Anima.new(:first, :middle, :last), Adamantium
41
+
42
+ class Component
43
+ include Anima.new(:source, :input, :score)
44
+
45
+ class Name
46
+ include Concord.new(:name), Adamantium
47
+ end # Name
48
+ end # Component
49
+ end # Components
50
+ end # NameComparison
51
+ end # Resource
52
+ end # Client
53
+ end # Cognito
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+ class Cognito
3
+ class Client
4
+ class Resource
5
+ class IdentitySearch < self
6
+ register_type :identity_search
7
+
8
+ many :identity_records
9
+ end # IdentitySearch
10
+
11
+ class IdentityRecord < self
12
+ register_type :identity_record
13
+
14
+ many :addresses
15
+ many :names
16
+ many :ssns
17
+ many :phones
18
+ many :births
19
+ many :deaths
20
+ end # IdentityRecord
21
+
22
+ class USAddress < self
23
+ register_type :us_address
24
+
25
+ attribute :street
26
+ attribute :city
27
+ attribute :subdivision
28
+ attribute :postal_code
29
+ end # USAddress
30
+
31
+ class PartialName < self
32
+ register_type :name
33
+
34
+ attribute :first
35
+ attribute :middle
36
+ attribute :last
37
+ end # PartialName
38
+
39
+ class SSN < self
40
+ register_type :ssn
41
+
42
+ attribute :number
43
+ end # SSN
44
+
45
+ class Phone < self
46
+ register_type :phone
47
+ end # Phone
48
+
49
+ class PartialDate < self
50
+ attribute :day
51
+ attribute :month
52
+ attribute :year
53
+ end # PartialDate
54
+
55
+ class Birth < PartialDate
56
+ register_type :birth
57
+ end # Birth
58
+
59
+ class Death < PartialDate
60
+ register_type :death
61
+ end # Death
62
+ end # Resource
63
+ end # Client
64
+ end # Cognito