fortnox-api 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +3 -2
- data/.env.test +3 -0
- data/.rubocop.yml +32 -23
- data/.travis.yml +3 -2
- data/Guardfile +2 -2
- data/LICENSE.txt +165 -22
- data/README.md +56 -37
- data/fortnox-api.gemspec +9 -6
- data/lib/fortnox/api.rb +18 -2
- data/lib/fortnox/api/base.rb +5 -3
- data/lib/fortnox/api/environment_validation.rb +51 -6
- data/lib/fortnox/api/mappers.rb +49 -0
- data/lib/fortnox/api/mappers/base.rb +47 -0
- data/lib/fortnox/api/mappers/base/from_json.rb +78 -0
- data/lib/fortnox/api/mappers/base/to_json.rb +65 -0
- data/lib/fortnox/api/mappers/customer.rb +27 -0
- data/lib/fortnox/api/mappers/default_delivery_types.rb +13 -0
- data/lib/fortnox/api/mappers/default_templates.rb +15 -0
- data/lib/fortnox/api/mappers/edi_information.rb +22 -0
- data/lib/fortnox/api/mappers/email_information.rb +18 -0
- data/lib/fortnox/api/mappers/invoice.rb +27 -0
- data/lib/fortnox/api/mappers/invoice_row.rb +20 -0
- data/lib/fortnox/api/mappers/order.rb +23 -0
- data/lib/fortnox/api/mappers/order_row.rb +16 -0
- data/lib/fortnox/api/models.rb +2 -0
- data/lib/fortnox/api/models/base.rb +56 -13
- data/lib/fortnox/api/models/customer.rb +112 -101
- data/lib/fortnox/api/models/document_base.rb +189 -0
- data/lib/fortnox/api/models/invoice.rb +29 -195
- data/lib/fortnox/api/models/label.rb +17 -0
- data/lib/fortnox/api/models/order.rb +27 -0
- data/lib/fortnox/api/repositories.rb +2 -0
- data/lib/fortnox/api/repositories/base.rb +4 -5
- data/lib/fortnox/api/repositories/base/loaders.rb +22 -14
- data/lib/fortnox/api/repositories/base/savers.rb +30 -16
- data/lib/fortnox/api/repositories/customer.rb +3 -25
- data/lib/fortnox/api/repositories/invoice.rb +3 -22
- data/lib/fortnox/api/repositories/order.rb +16 -0
- data/lib/fortnox/api/request_handling.rb +3 -3
- data/lib/fortnox/api/types.rb +44 -0
- data/lib/fortnox/api/types/default_delivery_types.rb +20 -0
- data/lib/fortnox/api/types/default_templates.rb +23 -0
- data/lib/fortnox/api/types/defaulted.rb +11 -0
- data/lib/fortnox/api/types/document_row.rb +65 -0
- data/lib/fortnox/api/types/edi_information.rb +29 -0
- data/lib/fortnox/api/types/email_information.rb +26 -0
- data/lib/fortnox/api/types/enums.rb +75 -0
- data/lib/fortnox/api/types/invoice_row.rb +19 -0
- data/lib/fortnox/api/types/model.rb +40 -0
- data/lib/fortnox/api/types/nullable.rb +21 -0
- data/lib/fortnox/api/types/order_row.rb +16 -0
- data/lib/fortnox/api/types/required.rb +13 -0
- data/lib/fortnox/api/types/sized.rb +25 -0
- data/lib/fortnox/api/version.rb +1 -1
- data/spec/fortnox/api/base_spec.rb +85 -14
- data/spec/fortnox/api/mappers/base/from_json_spec.rb +70 -0
- data/spec/fortnox/api/mappers/base/to_json_spec.rb +76 -0
- data/spec/fortnox/api/mappers/base_spec.rb +156 -0
- data/spec/fortnox/api/mappers/contexts/json_conversion.rb +56 -0
- data/spec/fortnox/api/mappers/customer_spec.rb +25 -0
- data/spec/fortnox/api/mappers/default_delivery_types_spec.rb +12 -0
- data/spec/fortnox/api/mappers/edi_information_spec.rb +21 -0
- data/spec/fortnox/api/mappers/email_information_spec.rb +17 -0
- data/spec/fortnox/api/mappers/examples/mapper.rb +26 -0
- data/spec/fortnox/api/mappers/invoice_row_spec.rb +19 -0
- data/spec/fortnox/api/mappers/invoice_spec.rb +24 -0
- data/spec/fortnox/api/mappers/order_row_spec.rb +14 -0
- data/spec/fortnox/api/mappers/order_spec.rb +20 -0
- data/spec/fortnox/api/models/base_spec.rb +44 -22
- data/spec/fortnox/api/models/customer_spec.rb +9 -0
- data/spec/fortnox/api/models/examples/document_base.rb +13 -0
- data/spec/fortnox/api/models/examples/model.rb +13 -0
- data/spec/fortnox/api/models/invoice_spec.rb +7 -31
- data/spec/fortnox/api/models/order_spec.rb +13 -0
- data/spec/fortnox/api/repositories/customer_spec.rb +20 -76
- data/spec/fortnox/api/repositories/examples/all.rb +17 -0
- data/spec/fortnox/api/repositories/examples/find.rb +25 -0
- data/spec/fortnox/api/repositories/examples/only.rb +42 -0
- data/spec/fortnox/api/repositories/examples/save.rb +69 -0
- data/spec/fortnox/api/repositories/examples/save_with_nested_model.rb +32 -0
- data/spec/fortnox/api/repositories/examples/save_with_specially_named_attribute.rb +27 -0
- data/spec/fortnox/api/repositories/examples/search.rb +31 -0
- data/spec/fortnox/api/repositories/invoice_spec.rb +36 -5
- data/spec/fortnox/api/repositories/order_spec.rb +35 -0
- data/spec/fortnox/api/types/account_number_spec.rb +28 -0
- data/spec/fortnox/api/types/default_delivery_types_spec.rb +10 -0
- data/spec/fortnox/api/types/edi_information_spec.rb +13 -0
- data/spec/fortnox/api/types/email_information_spec.rb +13 -0
- data/spec/fortnox/api/types/email_spec.rb +29 -0
- data/spec/fortnox/api/types/enums_spec.rb +13 -0
- data/spec/fortnox/api/types/examples/document_row.rb +15 -0
- data/spec/fortnox/api/types/examples/enum.rb +48 -0
- data/spec/fortnox/api/types/examples/types.rb +9 -0
- data/spec/fortnox/api/types/house_work_types_spec.rb +60 -0
- data/spec/fortnox/api/types/invoice_row_spec.rb +9 -0
- data/spec/fortnox/api/types/model_spec.rb +56 -0
- data/spec/fortnox/api/types/nullable_spec.rb +57 -0
- data/spec/fortnox/api/types/order_row_spec.rb +13 -0
- data/spec/fortnox/api/types/required_spec.rb +42 -0
- data/spec/fortnox/api/types/sized_spec.rb +74 -0
- data/spec/fortnox/api_spec.rb +16 -15
- data/spec/spec_helper.rb +19 -9
- data/spec/support/helpers/dummy_class_helper.rb +19 -0
- data/spec/support/helpers/environment_helper.rb +7 -0
- data/spec/support/helpers/repository_helper.rb +8 -0
- data/spec/support/helpers/when_performing_helper.rb +5 -0
- data/spec/support/matchers.rb +1 -1
- data/spec/support/matchers/type.rb +17 -0
- data/spec/support/matchers/type/attribute_matcher.rb +39 -0
- data/spec/support/matchers/type/enum_matcher.rb +21 -0
- data/spec/support/matchers/type/have_account_number_matcher.rb +21 -0
- data/spec/support/matchers/type/have_country_code_matcher.rb +13 -0
- data/spec/support/matchers/type/have_currency_matcher.rb +7 -0
- data/spec/support/matchers/type/have_customer_type_matcher.rb +13 -0
- data/spec/support/matchers/type/have_default_delivery_type_matcher.rb +7 -0
- data/spec/support/matchers/type/have_discount_type_matcher.rb +7 -0
- data/spec/support/matchers/type/have_email_matcher.rb +22 -0
- data/spec/support/matchers/type/have_house_work_type_matcher.rb +7 -0
- data/spec/support/matchers/type/have_nullable_date_matcher.rb +58 -0
- data/spec/support/matchers/type/have_nullable_matcher.rb +52 -0
- data/spec/support/matchers/type/have_nullable_string_matcher.rb +49 -0
- data/spec/support/matchers/type/have_sized_float_matcher.rb +8 -0
- data/spec/support/matchers/type/have_sized_integer_matcher.rb +8 -0
- data/spec/support/matchers/type/have_sized_string_matcher.rb +35 -0
- data/spec/support/matchers/type/have_vat_type_matcher.rb +7 -0
- data/spec/support/matchers/type/numeric_matcher.rb +50 -0
- data/spec/support/matchers/type/require_attribute_matcher.rb +69 -0
- data/spec/support/matchers/type/type_matcher.rb +38 -0
- data/spec/vcr_cassettes/customers/all.yml +119 -9
- data/spec/vcr_cassettes/customers/find_id_1.yml +8 -9
- data/spec/vcr_cassettes/customers/find_new.yml +46 -0
- data/spec/vcr_cassettes/customers/save_new.yml +9 -11
- data/spec/vcr_cassettes/customers/save_old.yml +9 -12
- data/spec/vcr_cassettes/customers/save_with_specially_named_attribute.yml +45 -0
- data/spec/vcr_cassettes/customers/search_by_name.yml +66 -0
- data/spec/vcr_cassettes/customers/search_miss.yml +45 -0
- data/spec/vcr_cassettes/invoices/all.yml +104 -0
- data/spec/vcr_cassettes/invoices/filter_hit.yml +46 -0
- data/spec/vcr_cassettes/invoices/filter_invalid.yml +42 -0
- data/spec/vcr_cassettes/invoices/find_id_1.yml +47 -0
- data/spec/vcr_cassettes/invoices/find_new.yml +49 -0
- data/spec/vcr_cassettes/invoices/save_new.yml +48 -0
- data/spec/vcr_cassettes/invoices/save_old.yml +49 -0
- data/spec/vcr_cassettes/invoices/save_with_nested_model.yml +47 -0
- data/spec/vcr_cassettes/invoices/save_with_specially_named_attribute.yml +47 -0
- data/spec/vcr_cassettes/invoices/search_by_name.yml +48 -0
- data/spec/vcr_cassettes/invoices/search_miss.yml +45 -0
- data/spec/vcr_cassettes/orders/all.yml +144 -0
- data/spec/vcr_cassettes/orders/filter_hit.yml +48 -0
- data/spec/vcr_cassettes/orders/filter_invalid.yml +42 -0
- data/spec/vcr_cassettes/orders/find_id_1.yml +48 -0
- data/spec/vcr_cassettes/orders/find_new.yml +49 -0
- data/spec/vcr_cassettes/orders/house_work_type_babysitting.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_cleaning.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_construction.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_cooking.yml +43 -0
- data/spec/vcr_cassettes/orders/house_work_type_electricity.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_gardening.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_glassmetalwork.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_grounddrainagework.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_hvac.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_masonry.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_othercare.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_othercosts.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_paintingwallpapering.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_snowplowing.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_textileclothing.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_tutoring.yml +43 -0
- data/spec/vcr_cassettes/orders/save_new.yml +48 -0
- data/spec/vcr_cassettes/orders/save_old.yml +49 -0
- data/spec/vcr_cassettes/orders/save_with_nested_model.yml +47 -0
- data/spec/vcr_cassettes/orders/search_by_name.yml +47 -0
- data/spec/vcr_cassettes/orders/search_miss.yml +45 -0
- metadata +301 -71
- data/lib/fortnox/api/models/attributes/country_code.rb +0 -17
- data/lib/fortnox/api/models/attributes/currency.rb +0 -17
- data/lib/fortnox/api/models/edi_information.rb +0 -28
- data/lib/fortnox/api/models/email_information.rb +0 -25
- data/lib/fortnox/api/models/row.rb +0 -82
- data/lib/fortnox/api/repositories/base/json_convertion.rb +0 -68
- data/lib/fortnox/api/repositories/base/options.rb +0 -33
- data/lib/fortnox/api/validators.rb +0 -1
- data/lib/fortnox/api/validators/attributes/country_code.rb +0 -42
- data/lib/fortnox/api/validators/attributes/currency.rb +0 -38
- data/lib/fortnox/api/validators/base.rb +0 -70
- data/lib/fortnox/api/validators/constant.rb +0 -21
- data/lib/fortnox/api/validators/customer.rb +0 -29
- data/lib/fortnox/api/validators/edi_information.rb +0 -11
- data/lib/fortnox/api/validators/email_information.rb +0 -19
- data/lib/fortnox/api/validators/invoice.rb +0 -33
- data/lib/fortnox/api/validators/row.rb +0 -22
- data/spec/fortnox/api/models/attributes/country_code_spec.rb +0 -23
- data/spec/fortnox/api/models/attributes/currency_spec.rb +0 -23
- data/spec/fortnox/api/models/attributes/dummy_model_context.rb +0 -9
- data/spec/fortnox/api/models/row_spec.rb +0 -13
- data/spec/fortnox/api/repositories/context.rb +0 -10
- data/spec/fortnox/api/repositories/examples.rb +0 -16
- data/spec/fortnox/api/validators/attributes/country_code_spec.rb +0 -9
- data/spec/fortnox/api/validators/attributes/currency_spec.rb +0 -9
- data/spec/fortnox/api/validators/attributes/examples_for_validate.rb +0 -29
- data/spec/fortnox/api/validators/base_spec.rb +0 -61
- data/spec/fortnox/api/validators/constant_spec.rb +0 -12
- data/spec/fortnox/api/validators/context.rb +0 -102
- data/spec/fortnox/api/validators/customer_spec.rb +0 -31
- data/spec/fortnox/api/validators/edi_information_spec.rb +0 -18
- data/spec/fortnox/api/validators/email_information_spec.rb +0 -26
- data/spec/fortnox/api/validators/invoice_spec.rb +0 -36
- data/spec/fortnox/api/validators/row_spec.rb +0 -27
- data/spec/fortnox/api/validators/validator_examples.rb +0 -20
- data/spec/support/matchers/models.rb +0 -27
- data/spec/support/matchers/validators.rb +0 -36
data/fortnox-api.gemspec
CHANGED
@@ -6,12 +6,12 @@ require 'fortnox/api/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "fortnox-api"
|
8
8
|
spec.version = Fortnox::API::VERSION
|
9
|
-
spec.authors = ["Jonas Schubert Erlandsson"]
|
9
|
+
spec.authors = ["Jonas Schubert Erlandsson", "Hannes Elvemyr"]
|
10
10
|
spec.email = ["jonas.schubert.erlandsson@my-codeworks.com"]
|
11
11
|
spec.summary = "Gem to use Fortnox REST API in Ruby."
|
12
12
|
spec.description = "This gem uses the HTTParty library to abstract away the REST calls. It gives you access to a number of objects that behave a lot like ActiveRecord instances, giving you access to methods like `all`, `find`, `find_by_...` and so on. And each individual instance can be easaly persistable to Fortnox again using the `save` method."
|
13
13
|
spec.homepage = "http://github.com/my-codeworks/fortnox-api"
|
14
|
-
spec.license = "
|
14
|
+
spec.license = "LGPL-3.0"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}){ |f| File.basename(f) }
|
@@ -20,17 +20,20 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_dependency "httparty", "~> 0.13"
|
22
22
|
spec.add_dependency "dotenv", "~> 2.0"
|
23
|
-
spec.add_dependency "
|
24
|
-
spec.add_dependency "
|
23
|
+
spec.add_dependency "dry-struct", "~> 0.1"
|
24
|
+
spec.add_dependency "dry-types", "~> 0.8"
|
25
25
|
|
26
26
|
spec.add_development_dependency "bundler", "~> 1.9"
|
27
27
|
spec.add_development_dependency "rake", "~> 10.4"
|
28
28
|
spec.add_development_dependency "rspec", "~> 3.2"
|
29
|
+
spec.add_development_dependency "rspec-collection_matchers", "~> 0"
|
29
30
|
spec.add_development_dependency "guard", "~> 2.12"
|
30
31
|
spec.add_development_dependency "guard-rspec", "~> 4.5"
|
31
32
|
spec.add_development_dependency "webmock", "~> 1.21"
|
32
33
|
spec.add_development_dependency "vcr", "~> 3.0"
|
33
|
-
spec.add_development_dependency "pry"
|
34
|
-
spec.add_development_dependency "codeclimate-test-reporter"
|
34
|
+
spec.add_development_dependency "pry", "~> 0"
|
35
|
+
spec.add_development_dependency "codeclimate-test-reporter", "~> 0"
|
36
|
+
spec.add_development_dependency "rubocop", "~> 0.41"
|
37
|
+
spec.add_development_dependency "rubocop-rspec", "~> 1.7"
|
35
38
|
|
36
39
|
end
|
data/lib/fortnox/api.rb
CHANGED
@@ -1,28 +1,44 @@
|
|
1
1
|
require "set"
|
2
2
|
require "dotenv"
|
3
|
+
require "dry-struct"
|
3
4
|
require "fortnox/api/base"
|
4
5
|
require "fortnox/api/version"
|
6
|
+
require 'logger'
|
5
7
|
|
6
8
|
Dotenv.load unless ENV['RUBY_ENV'] == 'test'
|
7
9
|
|
8
10
|
module Fortnox
|
9
11
|
module API
|
10
12
|
|
11
|
-
class
|
13
|
+
class Exception < StandardError
|
14
|
+
end
|
15
|
+
|
16
|
+
class AttributeError < Fortnox::API::Exception
|
17
|
+
end
|
18
|
+
|
19
|
+
class RemoteServerError < Fortnox::API::Exception
|
20
|
+
end
|
21
|
+
|
22
|
+
class MissingAttributeError < Fortnox::API::Exception
|
12
23
|
end
|
13
24
|
|
14
25
|
class << self
|
15
26
|
@debugging = false
|
27
|
+
@logger = Logger.new(STDOUT)
|
16
28
|
attr_accessor :debugging
|
29
|
+
attr_accessor :logger
|
30
|
+
|
31
|
+
@logger.level = Logger::WARN
|
17
32
|
end
|
18
33
|
|
19
34
|
def self.get_access_token
|
20
35
|
Base.get_access_token
|
21
36
|
end
|
22
37
|
|
38
|
+
Registry = Dry::Container.new
|
23
39
|
end
|
24
40
|
end
|
25
41
|
|
26
42
|
require "fortnox/api/models"
|
27
43
|
require "fortnox/api/repositories"
|
28
|
-
require "fortnox/api/
|
44
|
+
require "fortnox/api/mappers"
|
data/lib/fortnox/api/base.rb
CHANGED
@@ -17,9 +17,9 @@ module Fortnox
|
|
17
17
|
DEFAULT_HEADERS = {
|
18
18
|
'Content-Type' => 'application/json',
|
19
19
|
'Accept' => 'application/json',
|
20
|
-
}
|
20
|
+
}.freeze
|
21
21
|
|
22
|
-
HTTP_METHODS = [ :get, :put, :post, :delete ]
|
22
|
+
HTTP_METHODS = [ :get, :put, :post, :delete ].freeze
|
23
23
|
|
24
24
|
attr_accessor :headers
|
25
25
|
|
@@ -28,12 +28,14 @@ module Fortnox
|
|
28
28
|
|
29
29
|
self.headers = DEFAULT_HEADERS.merge({
|
30
30
|
'Client-Secret' => get_client_secret,
|
31
|
-
'Access-Token' => get_access_token,
|
32
31
|
})
|
32
|
+
|
33
|
+
check_access_tokens!
|
33
34
|
end
|
34
35
|
|
35
36
|
HTTP_METHODS.each do |method|
|
36
37
|
define_method method do |*args|
|
38
|
+
self.headers['Access-Token'] = get_access_token
|
37
39
|
execute do |remote|
|
38
40
|
remote.send( method, *args )
|
39
41
|
end
|
@@ -1,33 +1,78 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module Fortnox
|
2
4
|
module API
|
3
5
|
module EnvironmentValidation
|
4
6
|
|
7
|
+
class MissingEnvironmentVariable < ArgumentError
|
8
|
+
end
|
9
|
+
|
10
|
+
class CircularQueue
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
def initialize *items
|
14
|
+
@queue = [ *items ]
|
15
|
+
@@next_index = random_start_index
|
16
|
+
end
|
17
|
+
|
18
|
+
# support some general Array methods that fit Queues well
|
19
|
+
def_delegators :@queue, :new, :[], :size
|
20
|
+
|
21
|
+
def next
|
22
|
+
value = @queue[ @@next_index ]
|
23
|
+
if @@next_index == size - 1
|
24
|
+
@@next_index = 0
|
25
|
+
else
|
26
|
+
@@next_index += 1
|
27
|
+
end
|
28
|
+
return value
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def random_start_index
|
34
|
+
Random.rand(@queue.size)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
5
38
|
private
|
6
39
|
|
7
40
|
def get_base_url
|
8
41
|
base_url = ENV['FORTNOX_API_BASE_URL']
|
9
|
-
fail
|
42
|
+
fail MissingEnvironmentVariable, 'You have to provide a base url.' unless base_url
|
10
43
|
base_url
|
11
44
|
end
|
12
45
|
|
13
46
|
def get_client_secret
|
14
47
|
client_secret = ENV['FORTNOX_API_CLIENT_SECRET']
|
15
|
-
fail
|
48
|
+
fail MissingEnvironmentVariable, 'You have to provide your client secret.' unless client_secret
|
16
49
|
client_secret
|
17
50
|
end
|
18
51
|
|
19
52
|
def get_access_token
|
20
|
-
|
21
|
-
|
22
|
-
access_token
|
53
|
+
@access_tokens ||= load_access_tokens
|
54
|
+
@access_tokens.next
|
23
55
|
end
|
24
56
|
|
25
57
|
def get_authorization_code
|
26
58
|
authorization_code = ENV['FORTNOX_API_AUTHORIZATION_CODE']
|
27
|
-
fail
|
59
|
+
fail MissingEnvironmentVariable, 'You have to provide your authorization code.' unless authorization_code
|
28
60
|
authorization_code
|
29
61
|
end
|
30
62
|
|
63
|
+
def load_access_tokens
|
64
|
+
check_access_tokens!
|
65
|
+
access_token_string = ENV['FORTNOX_API_ACCESS_TOKEN']
|
66
|
+
access_tokens = access_token_string.split(",").map(&:strip)
|
67
|
+
CircularQueue.new( *access_tokens )
|
68
|
+
end
|
69
|
+
|
70
|
+
def check_access_tokens!
|
71
|
+
unless ENV['FORTNOX_API_ACCESS_TOKEN']
|
72
|
+
fail MissingEnvironmentVariable, 'You have to provide your access token.'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
31
76
|
end
|
32
77
|
end
|
33
78
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'fortnox/api/types'
|
2
|
+
|
3
|
+
module Fortnox
|
4
|
+
module API
|
5
|
+
module Mapper
|
6
|
+
Identity = ->(value){ value }
|
7
|
+
|
8
|
+
Date = ->(value){ value.to_s }
|
9
|
+
|
10
|
+
Array = ->(array) do
|
11
|
+
array.each_with_object( [] ) do |item, converted_array|
|
12
|
+
name = Fortnox::API::Mapper::Base.canonical_name_sym( item )
|
13
|
+
converted_array << Fortnox::API::Registry[ name ].call( item )
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Registry.register( :fixnum, Fortnox::API::Mapper::Identity )
|
18
|
+
Registry.register( :int, Fortnox::API::Mapper::Identity )
|
19
|
+
Registry.register( :float, Fortnox::API::Mapper::Identity )
|
20
|
+
Registry.register( :string, Fortnox::API::Mapper::Identity )
|
21
|
+
Registry.register( :boolean, Fortnox::API::Mapper::Identity )
|
22
|
+
Registry.register( :falseclass, Fortnox::API::Mapper::Identity )
|
23
|
+
Registry.register( :trueclass, Fortnox::API::Mapper::Identity )
|
24
|
+
Registry.register( :nilclass, Fortnox::API::Mapper::Identity )
|
25
|
+
Registry.register( :array, Fortnox::API::Mapper::Array )
|
26
|
+
Registry.register( :date, Fortnox::API::Mapper::Date )
|
27
|
+
|
28
|
+
Registry.register( :account_number, Fortnox::API::Mapper::Identity )
|
29
|
+
Registry.register( :country_code, Fortnox::API::Mapper::Identity )
|
30
|
+
Registry.register( :currency, Fortnox::API::Mapper::Identity )
|
31
|
+
Registry.register( :customer_type, Fortnox::API::Mapper::Identity )
|
32
|
+
Registry.register( :discount_type, Fortnox::API::Mapper::Identity )
|
33
|
+
Registry.register( :email, Fortnox::API::Mapper::Identity )
|
34
|
+
Registry.register( :house_work_type, Fortnox::API::Mapper::Identity )
|
35
|
+
Registry.register( :vat_type, Fortnox::API::Mapper::Identity )
|
36
|
+
Registry.register( :labels, Fortnox::API::Mapper::Array )
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
require 'fortnox/api/mappers/customer'
|
42
|
+
require 'fortnox/api/mappers/default_delivery_types'
|
43
|
+
require 'fortnox/api/mappers/default_templates'
|
44
|
+
require 'fortnox/api/mappers/edi_information'
|
45
|
+
require 'fortnox/api/mappers/email_information'
|
46
|
+
require 'fortnox/api/mappers/invoice_row'
|
47
|
+
require 'fortnox/api/mappers/invoice'
|
48
|
+
require 'fortnox/api/mappers/order_row'
|
49
|
+
require 'fortnox/api/mappers/order'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "fortnox/api/mappers/base/from_json"
|
2
|
+
require "fortnox/api/mappers/base/to_json"
|
3
|
+
|
4
|
+
module Fortnox
|
5
|
+
module API
|
6
|
+
module Mapper
|
7
|
+
class Base
|
8
|
+
include FromJSON
|
9
|
+
include ToJSON
|
10
|
+
|
11
|
+
Hash = ->(hash) do
|
12
|
+
hash.each do |key, value|
|
13
|
+
name = Fortnox::API::Mapper::Base.canonical_name_sym( value )
|
14
|
+
hash[key] = Fortnox::API::Registry[ name ].call( value )
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Registry.register( :hash, Fortnox::API::Mapper::Base::Hash )
|
19
|
+
|
20
|
+
def self.canonical_name_sym( *values )
|
21
|
+
klass = if values.empty?
|
22
|
+
self
|
23
|
+
elsif values.first.is_a? Class
|
24
|
+
values.first
|
25
|
+
else
|
26
|
+
values.first.class
|
27
|
+
end
|
28
|
+
|
29
|
+
klass.name.split('::').last.downcase.to_sym
|
30
|
+
end
|
31
|
+
|
32
|
+
def diff( entity_hash, parent_hash )
|
33
|
+
hash_diff( entity_hash[self.class::JSON_ENTITY_WRAPPER],
|
34
|
+
parent_hash[self.class::JSON_ENTITY_WRAPPER] )
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def hash_diff( hash1, hash2 )
|
40
|
+
hash1.dup.
|
41
|
+
delete_if{ |k, v| hash2[k] == v }.
|
42
|
+
merge!(hash2.dup.delete_if{ |k, _| hash1.has_key?(k) })
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Fortnox
|
2
|
+
module API
|
3
|
+
module Mapper
|
4
|
+
module FromJSON
|
5
|
+
|
6
|
+
class MissingModelOrMapperException < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
def wrapped_json_collection_to_entities_hash( json_collection_hash )
|
10
|
+
entities_hash = []
|
11
|
+
json_collection_hash[ self.class::JSON_COLLECTION_WRAPPER ].each do |json_hash|
|
12
|
+
entities_hash << json_hash_to_entity_hash( json_hash, self.class::KEY_MAP )
|
13
|
+
end
|
14
|
+
|
15
|
+
entities_hash
|
16
|
+
end
|
17
|
+
|
18
|
+
def wrapped_json_hash_to_entity_hash( json_entity_hash )
|
19
|
+
json_hash_to_entity_hash( json_entity_hash[self.class::JSON_ENTITY_WRAPPER],
|
20
|
+
self.class::KEY_MAP )
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def json_hash_to_entity_hash( hash, key_map )
|
26
|
+
hash.each_with_object( {} ) do |(key, value), json_hash|
|
27
|
+
converted_key = convert_key_from_json( key, key_map )
|
28
|
+
json_hash[converted_key] =
|
29
|
+
if value.respond_to?(:each)
|
30
|
+
convert_collection( key, value )
|
31
|
+
else
|
32
|
+
value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def convert_collection( key, collection )
|
38
|
+
mapper_name = key.downcase
|
39
|
+
if Registry.key?(mapper_name)
|
40
|
+
convert_nested_data( mapper_name, key, collection )
|
41
|
+
else
|
42
|
+
# NOTE: This probably means this is a nested model that we have
|
43
|
+
# not implemented yet, or that is missing a mapper.
|
44
|
+
# Raise exception during test run if this happens so that we can
|
45
|
+
# add it before a new release.
|
46
|
+
|
47
|
+
raise MissingModelOrMapperException, "for #{ key } (#{ mapper_name }, #{ Fortnox::API::Mapper::DefaultTemplates.canonical_name_sym }) with #{ collection }" if ENV['RUBY_ENV']
|
48
|
+
Fortnox::API.logger.warn( "Missing Model or Mapper implementation for #{ key } with attributes: #{ collection }" )
|
49
|
+
return convert_hash_keys_from_json_format( collection, {} )
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def convert_nested_data( _mapper_name, key, nested_data )
|
54
|
+
nested_mapper = Registry[ key.downcase ]
|
55
|
+
if nested_data.is_a?(::Array) # Array of nested models
|
56
|
+
return nested_data.each_with_object( [] ) do |value, nested_models|
|
57
|
+
nested_models << json_hash_to_entity_hash( value, nested_mapper::KEY_MAP )
|
58
|
+
end
|
59
|
+
else # Assume Hash of nested model
|
60
|
+
return json_hash_to_entity_hash( nested_data, nested_mapper::KEY_MAP )
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def convert_key_from_json( key, key_map )
|
65
|
+
key_map.key( key ) || default_key_from_json_transform( key )
|
66
|
+
end
|
67
|
+
|
68
|
+
def default_key_from_json_transform( key )
|
69
|
+
key = key.to_s
|
70
|
+
unless key =~ /\A[A-Z]+\z/
|
71
|
+
key = key.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z])([A-Z])/, '\1_\2')
|
72
|
+
end
|
73
|
+
key.downcase.to_sym
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Fortnox
|
2
|
+
module API
|
3
|
+
module Mapper
|
4
|
+
module ToJSON
|
5
|
+
def self.included(base)
|
6
|
+
base.instance_eval do
|
7
|
+
|
8
|
+
# PUBLIC
|
9
|
+
|
10
|
+
def call( entity, keys_to_filter = {} )
|
11
|
+
entity_hash = entity.to_hash
|
12
|
+
clean_entity_hash = sanitise( entity_hash, keys_to_filter )
|
13
|
+
clean_entity_hash = convert_hash_keys_to_json_format( clean_entity_hash )
|
14
|
+
Registry[:hash].call( clean_entity_hash )
|
15
|
+
end
|
16
|
+
|
17
|
+
# PRIVATE
|
18
|
+
|
19
|
+
def convert_hash_keys_to_json_format( hash )
|
20
|
+
hash.each_with_object( {} ) do |(key, value), json_hash|
|
21
|
+
json_hash[ convert_key_to_json( key ) ] = value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def convert_key_to_json( key )
|
26
|
+
self::KEY_MAP.fetch( key ){ default_key_to_json_transform( key ) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_key_to_json_transform( key )
|
30
|
+
key.to_s.split('_').map(&:capitalize).join('')
|
31
|
+
end
|
32
|
+
|
33
|
+
def sanitise( hash, keys_to_filter )
|
34
|
+
hash.select do |key, value|
|
35
|
+
next false if keys_to_filter.include?( key )
|
36
|
+
value != nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private_class_method :convert_hash_keys_to_json_format,
|
41
|
+
:convert_key_to_json,
|
42
|
+
:default_key_to_json_transform,
|
43
|
+
:sanitise
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def entity_to_hash( entity, keys_to_filter )
|
48
|
+
entity_json_hash = Registry[ mapper_name_for( entity ) ]
|
49
|
+
.call( entity, keys_to_filter )
|
50
|
+
wrapp_entity_json_hash( entity_json_hash )
|
51
|
+
end
|
52
|
+
|
53
|
+
def wrapp_entity_json_hash( entity_json_hash )
|
54
|
+
{ self.class::JSON_ENTITY_WRAPPER => entity_json_hash }
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def mapper_name_for( value )
|
60
|
+
value.class.name.split('::').last.downcase.to_sym
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "fortnox/api/mappers/base"
|
2
|
+
|
3
|
+
module Fortnox
|
4
|
+
module API
|
5
|
+
module Mapper
|
6
|
+
class Customer < Fortnox::API::Mapper::Base
|
7
|
+
|
8
|
+
KEY_MAP = {
|
9
|
+
vat_type: 'VATType',
|
10
|
+
vat_number: 'VATNumber',
|
11
|
+
email_invoice_bcc: 'EmailInvoiceBCC',
|
12
|
+
email_invoice_cc: 'EmailInvoiceCC',
|
13
|
+
email_offer_bcc: 'EmailOfferBCC',
|
14
|
+
email_offer_cc: 'EmailOfferCC',
|
15
|
+
email_order_bcc: 'EmailOrderBCC',
|
16
|
+
email_order_cc: 'EmailOrderCC',
|
17
|
+
show_price_vat_included: 'ShowPriceVATIncluded',
|
18
|
+
}.freeze
|
19
|
+
JSON_ENTITY_WRAPPER = 'Customer'.freeze
|
20
|
+
JSON_COLLECTION_WRAPPER = 'Customers'.freeze
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
Registry.register( Customer.canonical_name_sym, Customer )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|