nova-api 0.4.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.
@@ -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