fortnox-api 0.1.0 → 0.2.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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +3 -2
  3. data/.env.test +3 -0
  4. data/.rubocop.yml +32 -23
  5. data/.travis.yml +3 -2
  6. data/Guardfile +2 -2
  7. data/LICENSE.txt +165 -22
  8. data/README.md +56 -37
  9. data/fortnox-api.gemspec +9 -6
  10. data/lib/fortnox/api.rb +18 -2
  11. data/lib/fortnox/api/base.rb +5 -3
  12. data/lib/fortnox/api/environment_validation.rb +51 -6
  13. data/lib/fortnox/api/mappers.rb +49 -0
  14. data/lib/fortnox/api/mappers/base.rb +47 -0
  15. data/lib/fortnox/api/mappers/base/from_json.rb +78 -0
  16. data/lib/fortnox/api/mappers/base/to_json.rb +65 -0
  17. data/lib/fortnox/api/mappers/customer.rb +27 -0
  18. data/lib/fortnox/api/mappers/default_delivery_types.rb +13 -0
  19. data/lib/fortnox/api/mappers/default_templates.rb +15 -0
  20. data/lib/fortnox/api/mappers/edi_information.rb +22 -0
  21. data/lib/fortnox/api/mappers/email_information.rb +18 -0
  22. data/lib/fortnox/api/mappers/invoice.rb +27 -0
  23. data/lib/fortnox/api/mappers/invoice_row.rb +20 -0
  24. data/lib/fortnox/api/mappers/order.rb +23 -0
  25. data/lib/fortnox/api/mappers/order_row.rb +16 -0
  26. data/lib/fortnox/api/models.rb +2 -0
  27. data/lib/fortnox/api/models/base.rb +56 -13
  28. data/lib/fortnox/api/models/customer.rb +112 -101
  29. data/lib/fortnox/api/models/document_base.rb +189 -0
  30. data/lib/fortnox/api/models/invoice.rb +29 -195
  31. data/lib/fortnox/api/models/label.rb +17 -0
  32. data/lib/fortnox/api/models/order.rb +27 -0
  33. data/lib/fortnox/api/repositories.rb +2 -0
  34. data/lib/fortnox/api/repositories/base.rb +4 -5
  35. data/lib/fortnox/api/repositories/base/loaders.rb +22 -14
  36. data/lib/fortnox/api/repositories/base/savers.rb +30 -16
  37. data/lib/fortnox/api/repositories/customer.rb +3 -25
  38. data/lib/fortnox/api/repositories/invoice.rb +3 -22
  39. data/lib/fortnox/api/repositories/order.rb +16 -0
  40. data/lib/fortnox/api/request_handling.rb +3 -3
  41. data/lib/fortnox/api/types.rb +44 -0
  42. data/lib/fortnox/api/types/default_delivery_types.rb +20 -0
  43. data/lib/fortnox/api/types/default_templates.rb +23 -0
  44. data/lib/fortnox/api/types/defaulted.rb +11 -0
  45. data/lib/fortnox/api/types/document_row.rb +65 -0
  46. data/lib/fortnox/api/types/edi_information.rb +29 -0
  47. data/lib/fortnox/api/types/email_information.rb +26 -0
  48. data/lib/fortnox/api/types/enums.rb +75 -0
  49. data/lib/fortnox/api/types/invoice_row.rb +19 -0
  50. data/lib/fortnox/api/types/model.rb +40 -0
  51. data/lib/fortnox/api/types/nullable.rb +21 -0
  52. data/lib/fortnox/api/types/order_row.rb +16 -0
  53. data/lib/fortnox/api/types/required.rb +13 -0
  54. data/lib/fortnox/api/types/sized.rb +25 -0
  55. data/lib/fortnox/api/version.rb +1 -1
  56. data/spec/fortnox/api/base_spec.rb +85 -14
  57. data/spec/fortnox/api/mappers/base/from_json_spec.rb +70 -0
  58. data/spec/fortnox/api/mappers/base/to_json_spec.rb +76 -0
  59. data/spec/fortnox/api/mappers/base_spec.rb +156 -0
  60. data/spec/fortnox/api/mappers/contexts/json_conversion.rb +56 -0
  61. data/spec/fortnox/api/mappers/customer_spec.rb +25 -0
  62. data/spec/fortnox/api/mappers/default_delivery_types_spec.rb +12 -0
  63. data/spec/fortnox/api/mappers/edi_information_spec.rb +21 -0
  64. data/spec/fortnox/api/mappers/email_information_spec.rb +17 -0
  65. data/spec/fortnox/api/mappers/examples/mapper.rb +26 -0
  66. data/spec/fortnox/api/mappers/invoice_row_spec.rb +19 -0
  67. data/spec/fortnox/api/mappers/invoice_spec.rb +24 -0
  68. data/spec/fortnox/api/mappers/order_row_spec.rb +14 -0
  69. data/spec/fortnox/api/mappers/order_spec.rb +20 -0
  70. data/spec/fortnox/api/models/base_spec.rb +44 -22
  71. data/spec/fortnox/api/models/customer_spec.rb +9 -0
  72. data/spec/fortnox/api/models/examples/document_base.rb +13 -0
  73. data/spec/fortnox/api/models/examples/model.rb +13 -0
  74. data/spec/fortnox/api/models/invoice_spec.rb +7 -31
  75. data/spec/fortnox/api/models/order_spec.rb +13 -0
  76. data/spec/fortnox/api/repositories/customer_spec.rb +20 -76
  77. data/spec/fortnox/api/repositories/examples/all.rb +17 -0
  78. data/spec/fortnox/api/repositories/examples/find.rb +25 -0
  79. data/spec/fortnox/api/repositories/examples/only.rb +42 -0
  80. data/spec/fortnox/api/repositories/examples/save.rb +69 -0
  81. data/spec/fortnox/api/repositories/examples/save_with_nested_model.rb +32 -0
  82. data/spec/fortnox/api/repositories/examples/save_with_specially_named_attribute.rb +27 -0
  83. data/spec/fortnox/api/repositories/examples/search.rb +31 -0
  84. data/spec/fortnox/api/repositories/invoice_spec.rb +36 -5
  85. data/spec/fortnox/api/repositories/order_spec.rb +35 -0
  86. data/spec/fortnox/api/types/account_number_spec.rb +28 -0
  87. data/spec/fortnox/api/types/default_delivery_types_spec.rb +10 -0
  88. data/spec/fortnox/api/types/edi_information_spec.rb +13 -0
  89. data/spec/fortnox/api/types/email_information_spec.rb +13 -0
  90. data/spec/fortnox/api/types/email_spec.rb +29 -0
  91. data/spec/fortnox/api/types/enums_spec.rb +13 -0
  92. data/spec/fortnox/api/types/examples/document_row.rb +15 -0
  93. data/spec/fortnox/api/types/examples/enum.rb +48 -0
  94. data/spec/fortnox/api/types/examples/types.rb +9 -0
  95. data/spec/fortnox/api/types/house_work_types_spec.rb +60 -0
  96. data/spec/fortnox/api/types/invoice_row_spec.rb +9 -0
  97. data/spec/fortnox/api/types/model_spec.rb +56 -0
  98. data/spec/fortnox/api/types/nullable_spec.rb +57 -0
  99. data/spec/fortnox/api/types/order_row_spec.rb +13 -0
  100. data/spec/fortnox/api/types/required_spec.rb +42 -0
  101. data/spec/fortnox/api/types/sized_spec.rb +74 -0
  102. data/spec/fortnox/api_spec.rb +16 -15
  103. data/spec/spec_helper.rb +19 -9
  104. data/spec/support/helpers/dummy_class_helper.rb +19 -0
  105. data/spec/support/helpers/environment_helper.rb +7 -0
  106. data/spec/support/helpers/repository_helper.rb +8 -0
  107. data/spec/support/helpers/when_performing_helper.rb +5 -0
  108. data/spec/support/matchers.rb +1 -1
  109. data/spec/support/matchers/type.rb +17 -0
  110. data/spec/support/matchers/type/attribute_matcher.rb +39 -0
  111. data/spec/support/matchers/type/enum_matcher.rb +21 -0
  112. data/spec/support/matchers/type/have_account_number_matcher.rb +21 -0
  113. data/spec/support/matchers/type/have_country_code_matcher.rb +13 -0
  114. data/spec/support/matchers/type/have_currency_matcher.rb +7 -0
  115. data/spec/support/matchers/type/have_customer_type_matcher.rb +13 -0
  116. data/spec/support/matchers/type/have_default_delivery_type_matcher.rb +7 -0
  117. data/spec/support/matchers/type/have_discount_type_matcher.rb +7 -0
  118. data/spec/support/matchers/type/have_email_matcher.rb +22 -0
  119. data/spec/support/matchers/type/have_house_work_type_matcher.rb +7 -0
  120. data/spec/support/matchers/type/have_nullable_date_matcher.rb +58 -0
  121. data/spec/support/matchers/type/have_nullable_matcher.rb +52 -0
  122. data/spec/support/matchers/type/have_nullable_string_matcher.rb +49 -0
  123. data/spec/support/matchers/type/have_sized_float_matcher.rb +8 -0
  124. data/spec/support/matchers/type/have_sized_integer_matcher.rb +8 -0
  125. data/spec/support/matchers/type/have_sized_string_matcher.rb +35 -0
  126. data/spec/support/matchers/type/have_vat_type_matcher.rb +7 -0
  127. data/spec/support/matchers/type/numeric_matcher.rb +50 -0
  128. data/spec/support/matchers/type/require_attribute_matcher.rb +69 -0
  129. data/spec/support/matchers/type/type_matcher.rb +38 -0
  130. data/spec/vcr_cassettes/customers/all.yml +119 -9
  131. data/spec/vcr_cassettes/customers/find_id_1.yml +8 -9
  132. data/spec/vcr_cassettes/customers/find_new.yml +46 -0
  133. data/spec/vcr_cassettes/customers/save_new.yml +9 -11
  134. data/spec/vcr_cassettes/customers/save_old.yml +9 -12
  135. data/spec/vcr_cassettes/customers/save_with_specially_named_attribute.yml +45 -0
  136. data/spec/vcr_cassettes/customers/search_by_name.yml +66 -0
  137. data/spec/vcr_cassettes/customers/search_miss.yml +45 -0
  138. data/spec/vcr_cassettes/invoices/all.yml +104 -0
  139. data/spec/vcr_cassettes/invoices/filter_hit.yml +46 -0
  140. data/spec/vcr_cassettes/invoices/filter_invalid.yml +42 -0
  141. data/spec/vcr_cassettes/invoices/find_id_1.yml +47 -0
  142. data/spec/vcr_cassettes/invoices/find_new.yml +49 -0
  143. data/spec/vcr_cassettes/invoices/save_new.yml +48 -0
  144. data/spec/vcr_cassettes/invoices/save_old.yml +49 -0
  145. data/spec/vcr_cassettes/invoices/save_with_nested_model.yml +47 -0
  146. data/spec/vcr_cassettes/invoices/save_with_specially_named_attribute.yml +47 -0
  147. data/spec/vcr_cassettes/invoices/search_by_name.yml +48 -0
  148. data/spec/vcr_cassettes/invoices/search_miss.yml +45 -0
  149. data/spec/vcr_cassettes/orders/all.yml +144 -0
  150. data/spec/vcr_cassettes/orders/filter_hit.yml +48 -0
  151. data/spec/vcr_cassettes/orders/filter_invalid.yml +42 -0
  152. data/spec/vcr_cassettes/orders/find_id_1.yml +48 -0
  153. data/spec/vcr_cassettes/orders/find_new.yml +49 -0
  154. data/spec/vcr_cassettes/orders/house_work_type_babysitting.yml +47 -0
  155. data/spec/vcr_cassettes/orders/house_work_type_cleaning.yml +47 -0
  156. data/spec/vcr_cassettes/orders/house_work_type_construction.yml +47 -0
  157. data/spec/vcr_cassettes/orders/house_work_type_cooking.yml +43 -0
  158. data/spec/vcr_cassettes/orders/house_work_type_electricity.yml +47 -0
  159. data/spec/vcr_cassettes/orders/house_work_type_gardening.yml +47 -0
  160. data/spec/vcr_cassettes/orders/house_work_type_glassmetalwork.yml +47 -0
  161. data/spec/vcr_cassettes/orders/house_work_type_grounddrainagework.yml +47 -0
  162. data/spec/vcr_cassettes/orders/house_work_type_hvac.yml +47 -0
  163. data/spec/vcr_cassettes/orders/house_work_type_masonry.yml +47 -0
  164. data/spec/vcr_cassettes/orders/house_work_type_othercare.yml +47 -0
  165. data/spec/vcr_cassettes/orders/house_work_type_othercosts.yml +47 -0
  166. data/spec/vcr_cassettes/orders/house_work_type_paintingwallpapering.yml +47 -0
  167. data/spec/vcr_cassettes/orders/house_work_type_snowplowing.yml +47 -0
  168. data/spec/vcr_cassettes/orders/house_work_type_textileclothing.yml +47 -0
  169. data/spec/vcr_cassettes/orders/house_work_type_tutoring.yml +43 -0
  170. data/spec/vcr_cassettes/orders/save_new.yml +48 -0
  171. data/spec/vcr_cassettes/orders/save_old.yml +49 -0
  172. data/spec/vcr_cassettes/orders/save_with_nested_model.yml +47 -0
  173. data/spec/vcr_cassettes/orders/search_by_name.yml +47 -0
  174. data/spec/vcr_cassettes/orders/search_miss.yml +45 -0
  175. metadata +301 -71
  176. data/lib/fortnox/api/models/attributes/country_code.rb +0 -17
  177. data/lib/fortnox/api/models/attributes/currency.rb +0 -17
  178. data/lib/fortnox/api/models/edi_information.rb +0 -28
  179. data/lib/fortnox/api/models/email_information.rb +0 -25
  180. data/lib/fortnox/api/models/row.rb +0 -82
  181. data/lib/fortnox/api/repositories/base/json_convertion.rb +0 -68
  182. data/lib/fortnox/api/repositories/base/options.rb +0 -33
  183. data/lib/fortnox/api/validators.rb +0 -1
  184. data/lib/fortnox/api/validators/attributes/country_code.rb +0 -42
  185. data/lib/fortnox/api/validators/attributes/currency.rb +0 -38
  186. data/lib/fortnox/api/validators/base.rb +0 -70
  187. data/lib/fortnox/api/validators/constant.rb +0 -21
  188. data/lib/fortnox/api/validators/customer.rb +0 -29
  189. data/lib/fortnox/api/validators/edi_information.rb +0 -11
  190. data/lib/fortnox/api/validators/email_information.rb +0 -19
  191. data/lib/fortnox/api/validators/invoice.rb +0 -33
  192. data/lib/fortnox/api/validators/row.rb +0 -22
  193. data/spec/fortnox/api/models/attributes/country_code_spec.rb +0 -23
  194. data/spec/fortnox/api/models/attributes/currency_spec.rb +0 -23
  195. data/spec/fortnox/api/models/attributes/dummy_model_context.rb +0 -9
  196. data/spec/fortnox/api/models/row_spec.rb +0 -13
  197. data/spec/fortnox/api/repositories/context.rb +0 -10
  198. data/spec/fortnox/api/repositories/examples.rb +0 -16
  199. data/spec/fortnox/api/validators/attributes/country_code_spec.rb +0 -9
  200. data/spec/fortnox/api/validators/attributes/currency_spec.rb +0 -9
  201. data/spec/fortnox/api/validators/attributes/examples_for_validate.rb +0 -29
  202. data/spec/fortnox/api/validators/base_spec.rb +0 -61
  203. data/spec/fortnox/api/validators/constant_spec.rb +0 -12
  204. data/spec/fortnox/api/validators/context.rb +0 -102
  205. data/spec/fortnox/api/validators/customer_spec.rb +0 -31
  206. data/spec/fortnox/api/validators/edi_information_spec.rb +0 -18
  207. data/spec/fortnox/api/validators/email_information_spec.rb +0 -26
  208. data/spec/fortnox/api/validators/invoice_spec.rb +0 -36
  209. data/spec/fortnox/api/validators/row_spec.rb +0 -27
  210. data/spec/fortnox/api/validators/validator_examples.rb +0 -20
  211. data/spec/support/matchers/models.rb +0 -27
  212. data/spec/support/matchers/validators.rb +0 -36
@@ -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 = "MIT"
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 "virtus", "~> 1.0"
24
- spec.add_dependency "vanguard", "~> 0.0"
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
@@ -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 RemoteServerError < ::RuntimeError
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/validators"
44
+ require "fortnox/api/mappers"
@@ -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 ArgumentError, 'You have to provide a base url.' unless base_url
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 ArgumentError, 'You have to provide your client secret.' unless client_secret
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
- access_token = ENV['FORTNOX_API_ACCESS_TOKEN']
21
- fail ArgumentError, 'You have to provide your access token.' unless access_token
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 ArgumentError, 'You have to provide your access token.' unless authorization_code
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