nova-api 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,126 @@
1
+ require 'httparty'
2
+ require 'forwardable'
3
+
4
+ module Nova
5
+ module API
6
+ class Base < Nova::API::Utils::BaseStruct
7
+ extend Forwardable
8
+ include HTTParty
9
+
10
+ format :json
11
+
12
+ SCHEME = 'https'
13
+ HOST = 'nova.money'
14
+
15
+ def self.endpoint
16
+ raise EndpointNotConfiguredError, 'Each class must implement its own endpoint'
17
+ end
18
+
19
+ def self.base_url
20
+ raise Nova::API::MissingSubdomainError, 'The subdomain must be informed' if configuration.subdomain.nil? || configuration.subdomain.empty?
21
+
22
+ "#{SCHEME}://#{configuration.subdomain}.#{HOST}"
23
+ end
24
+
25
+ def endpoint
26
+ raise EndpointNotConfiguredError, 'Each class must implement its own endpoint'
27
+ end
28
+
29
+ protected
30
+
31
+ def allowed_attributes
32
+ return attributes unless self.class.const_defined?('ALLOWED_ATTRIBUTES')
33
+
34
+ data = attributes.dup
35
+
36
+ data.keys.each { |key| data.delete(key.to_sym) unless self.class.const_get('ALLOWED_ATTRIBUTES').include?(key.to_sym) }
37
+
38
+ data
39
+ end
40
+
41
+ def self.initialize_empty_model_with_id(klass, id, additional_attributes = {})
42
+ data = klass.attribute_names.map { |key| additional_attributes[key] ? [key, additional_attributes[key]] : [key, nil] }
43
+
44
+ klass.new(Hash[*data.flatten].merge(id: id))
45
+ end
46
+
47
+ def self.do_get_search(endpoint, query, object = self)
48
+ response = perform_get(endpoint, query, headers)
49
+
50
+ Nova::API::ListResponse.build(response, object)
51
+ end
52
+
53
+ def self.do_get(endpoint, query, object = self)
54
+ response = perform_get(endpoint, query, headers)
55
+
56
+ Nova::API::Response.build(response, object)
57
+ end
58
+ def_delegator self, :do_get
59
+
60
+ def do_delete(endpoint)
61
+ set_base_uri
62
+
63
+ response = self.class.delete(endpoint, headers: authorization_header)
64
+
65
+ Nova::API::Response.build(response)
66
+ end
67
+
68
+ def do_patch(endpoint, data)
69
+ set_base_uri
70
+
71
+ if data.nil? || data.empty?
72
+ response = self.class.patch(endpoint, headers: authorization_header)
73
+
74
+ Nova::API::Response.build(response)
75
+ else
76
+ payload = data.dup
77
+ payload.delete(:id)
78
+
79
+ response = self.class.patch(endpoint, body: payload, headers: authorization_header)
80
+
81
+ Nova::API::Response.build(response, self)
82
+ end
83
+ end
84
+
85
+ def do_post(endpoint, data)
86
+ set_base_uri
87
+
88
+ response = self.class.post(endpoint, body: data, headers: authorization_header)
89
+
90
+ Nova::API::Response.build(response, self)
91
+ end
92
+
93
+ def protect_operation_from_missing_value(attribute = :id)
94
+ raise Nova::API::MissingIdError, 'This operation requires an ID to be set' if send(attribute).nil?
95
+ end
96
+
97
+ private
98
+
99
+ def self.perform_get(endpoint, query, headers = {})
100
+ set_base_uri
101
+
102
+ response =
103
+ if query
104
+ self.get(endpoint, query: query, headers: headers.merge(authorization_header))
105
+ else
106
+ self.get(endpoint, headers: headers.merge(authorization_header))
107
+ end
108
+ end
109
+
110
+ def self.authorization_header
111
+ { 'Persistent-Token': configuration.api_key }
112
+ end
113
+ def_delegator self, :authorization_header
114
+
115
+ def self.configuration
116
+ Nova::API.configuration
117
+ end
118
+ def_delegator self, :configuration
119
+
120
+ def self.set_base_uri
121
+ base_uri base_url
122
+ end
123
+ def_delegator self, :set_base_uri
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,42 @@
1
+ require 'httparty'
2
+ require 'dry-struct'
3
+ require 'dry-types'
4
+
5
+ module Nova
6
+ module API
7
+ class ListResponse < Nova::API::Utils::BaseStruct
8
+ attribute :records, Dry::Types['strict.array'].of(Dry::Types['nominal.any']).optional
9
+ attribute :errors, Dry::Types['strict.array'].of(Dry::Types['coercible.string'])
10
+ attribute :success, Dry::Types['strict.bool']
11
+
12
+ def self.build(response, klass)
13
+ success = response.success?
14
+
15
+ parsed_response = response.parsed_response
16
+
17
+ records = nil
18
+
19
+ if parsed_response.is_a?(Array)
20
+ records = klass.nil? ? nil : parsed_response.map { |object| klass.new(object) }
21
+ else
22
+ parsed_response = parsed_response.to_h
23
+
24
+ errors = extract_error_from_response('error', parsed_response)
25
+ errors ||= extract_error_from_response('errors', parsed_response)
26
+ end
27
+
28
+ errors ||= []
29
+
30
+ new(success: success, errors: errors, records: records)
31
+ end
32
+
33
+ private
34
+
35
+ def self.extract_error_from_response(field, response)
36
+ return unless response.has_key?(field)
37
+
38
+ response[field].is_a?(Array) ? response[field] : [response[field]]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,80 @@
1
+ module Nova
2
+ module API
3
+ module Resource
4
+ class Apportionment < Nova::API::Base
5
+ ALLOWED_ATTRIBUTES = %i[name]
6
+
7
+ attribute? :id, Dry::Types['coercible.integer'].optional
8
+ attribute :name, Dry::Types['coercible.string']
9
+ attribute? :active, Dry::Types['strict.bool'].optional
10
+ attribute? :values, Dry::Types['strict.array'].of(Nova::API::Resource::ApportionmentValue).optional
11
+
12
+ def self.endpoint
13
+ '/api/apportionments'
14
+ end
15
+
16
+ def self.list(parameters = {})
17
+ do_get_search(endpoint, parameters.to_h)
18
+ end
19
+
20
+ def self.create(parameters)
21
+ model = new parameters
22
+
23
+ model.attributes.delete(:id)
24
+
25
+ model.save
26
+ end
27
+
28
+ def self.update(id, parameters)
29
+ model = new parameters.merge(id: id)
30
+
31
+ model.update
32
+ end
33
+
34
+ def self.destroy(id)
35
+ model = initialize_empty_model_with_id(self, id)
36
+
37
+ model.destroy
38
+ end
39
+
40
+ def self.reactivate(id)
41
+ model = initialize_empty_model_with_id(self, id)
42
+
43
+ model.reactivate
44
+ end
45
+
46
+ def endpoint
47
+ protect_operation_from_missing_value
48
+
49
+ "/api/apportionments/#{id}"
50
+ end
51
+
52
+ def save
53
+ if id.nil?
54
+ do_post(self.class.endpoint, allowed_attributes)
55
+ else
56
+ do_patch("#{endpoint}", allowed_attributes)
57
+ end
58
+ end
59
+
60
+ def update
61
+ protect_operation_from_missing_value
62
+
63
+ do_patch("#{endpoint}", allowed_attributes)
64
+ end
65
+
66
+ def destroy
67
+ protect_operation_from_missing_value
68
+
69
+ do_delete("#{endpoint}")
70
+ end
71
+
72
+ def reactivate
73
+ protect_operation_from_missing_value
74
+
75
+ do_patch("#{endpoint}/reactivate", {})
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,74 @@
1
+ module Nova
2
+ module API
3
+ module Resource
4
+ class ApportionmentValue < Nova::API::Base
5
+ ALLOWED_ATTRIBUTES = %i[name]
6
+
7
+ attribute? :id, Dry::Types['coercible.integer'].optional
8
+ attribute :name, Dry::Types['coercible.string']
9
+ attribute? :active, Dry::Types['strict.bool'].optional
10
+ attribute? :apportionment_id, Dry::Types['coercible.integer']
11
+
12
+ def self.endpoint(apportionment_id)
13
+ "/api/apportionments/#{apportionment_id}/apportionment_values"
14
+ end
15
+
16
+ def self.create(apportionment_id, parameters)
17
+ model = new parameters.merge(apportionment_id: apportionment_id)
18
+
19
+ model.attributes.delete(:id)
20
+
21
+ model.save
22
+ end
23
+
24
+ def self.update(apportionment_id, id, parameters)
25
+ model = new parameters.merge(id: id, apportionment_id: apportionment_id)
26
+
27
+ model.update
28
+ end
29
+
30
+ def self.destroy(apportionment_id, id)
31
+ model = initialize_empty_model_with_id(self, id, apportionment_id: apportionment_id)
32
+
33
+ model.destroy
34
+ end
35
+
36
+ def self.reactivate(apportionment_id, id)
37
+ model = initialize_empty_model_with_id(self, id, apportionment_id: apportionment_id)
38
+
39
+ model.reactivate
40
+ end
41
+
42
+ def endpoint
43
+ "/api/apportionments/#{apportionment_id}/apportionment_values/#{id}"
44
+ end
45
+
46
+ def save
47
+ if id.nil?
48
+ do_post(self.class.endpoint(apportionment_id), allowed_attributes)
49
+ else
50
+ do_patch("#{endpoint}", allowed_attributes)
51
+ end
52
+ end
53
+
54
+ def update
55
+ protect_operation_from_missing_value
56
+
57
+ do_patch("#{endpoint}", allowed_attributes)
58
+ end
59
+
60
+ def destroy
61
+ protect_operation_from_missing_value
62
+
63
+ do_delete("#{endpoint}")
64
+ end
65
+
66
+ def reactivate
67
+ protect_operation_from_missing_value
68
+
69
+ do_patch("#{endpoint}/reactivate", {})
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,28 @@
1
+ module Nova
2
+ module API
3
+ module Resource
4
+ class Bank < Nova::API::Resource::CurrentAsset
5
+ ALLOWED_ATTRIBUTES = %i[]
6
+
7
+ attribute? :agency, Dry::Types['coercible.string'].optional
8
+ attribute? :current_account, Dry::Types['coercible.string'].optional
9
+ attribute? :institution do
10
+ attribute :code, Dry::Types['coercible.string'].optional
11
+ attribute :name, Dry::Types['coercible.string'].optional
12
+ end
13
+ attribute? :usage do
14
+ attribute :code, Dry::Types['coercible.integer'].optional
15
+ attribute :name, Dry::Types['coercible.string'].optional
16
+ end
17
+
18
+ def self.endpoint
19
+ '/api/banks'
20
+ end
21
+
22
+ def self.list(parameters = {})
23
+ do_get_search(endpoint, parameters.to_h)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ module Nova
2
+ module API
3
+ module Resource
4
+ class Card < Nova::API::Resource::CurrentAsset
5
+ ALLOWED_ATTRIBUTES = %i[]
6
+
7
+ attribute? :description, Dry::Types['coercible.string'].optional
8
+ attribute? :institution, Dry::Types['coercible.string'].optional
9
+
10
+ def self.endpoint
11
+ '/api/cards'
12
+ end
13
+
14
+ def self.list(parameters = {})
15
+ do_get_search(endpoint, parameters.to_h)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ module Nova
2
+ module API
3
+ module Resource
4
+ class Cash < Nova::API::Resource::CurrentAsset
5
+ ALLOWED_ATTRIBUTES = %i[]
6
+
7
+ attribute? :description, Dry::Types['coercible.string'].optional
8
+
9
+ def self.endpoint
10
+ '/api/cashes'
11
+ end
12
+
13
+ def self.list(parameters = {})
14
+ do_get_search(endpoint, parameters.to_h)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ module Nova
2
+ module API
3
+ module Resource
4
+ class Company < Nova::API::Base
5
+ ALLOWED_ATTRIBUTES = %i[]
6
+
7
+ attribute? :id, Dry::Types['coercible.integer'].optional
8
+ attribute? :name, Dry::Types['coercible.string'].optional
9
+ attribute? :trading_name, Dry::Types['coercible.string'].optional
10
+
11
+ def self.endpoint
12
+ '/api/companies'
13
+ end
14
+
15
+ def self.list
16
+ do_get_search(endpoint, nil)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ module Nova
2
+ module API
3
+ module Resource
4
+ class CurrentAsset < Nova::API::Base
5
+ ALLOWED_ATTRIBUTES = %i[]
6
+
7
+ attribute? :id, Dry::Types['coercible.integer'].optional
8
+ attribute? :company, Nova::API::Resource::Company.optional
9
+ attribute? :active, Dry::Types['strict.bool'].optional
10
+ attribute? :image, Dry::Types['coercible.string'].optional
11
+ attribute? :balance, Dry::Types['coercible.decimal'].optional
12
+
13
+ def self.endpoint
14
+ '/api/current_assets'
15
+ end
16
+
17
+ def self.statement(parameters = {})
18
+ do_get("#{endpoint}/statement", parameters.to_h, Nova::API::Resource::Response::CurrentAssetStatement.new)
19
+ end
20
+
21
+ def statement(initial_date, final_date)
22
+ protect_operation_from_missing_value
23
+
24
+ self.class.do_get("#{endpoint}/statement", { company_id: company.id, current_asset_id: id, final_date: final_date, initial_date: initial_date }, Nova::API::Resource::Response::CurrentAssetStatement.new)
25
+ end
26
+
27
+ def endpoint
28
+ "/api/current_assets"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end