cognito-client 0.4.1 → 0.5.0

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