fortnox-api 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (205) hide show
  1. checksums.yaml +4 -4
  2. data/.env.template +7 -0
  3. data/.env.test +11 -3
  4. data/.gitignore +7 -1
  5. data/.rubocop.yml +17 -1
  6. data/.travis.yml +10 -9
  7. data/CHANGELOG.md +22 -8
  8. data/CONTRIBUTE.md +21 -9
  9. data/DEVELOPER_README.md +72 -0
  10. data/Guardfile +13 -4
  11. data/README.md +226 -64
  12. data/Rakefile +128 -0
  13. data/bin/get_tokens +79 -0
  14. data/bin/renew_tokens +28 -0
  15. data/fortnox-api.gemspec +10 -9
  16. data/lib/fortnox/api/mappers/base/from_json.rb +4 -3
  17. data/lib/fortnox/api/mappers/base/to_json.rb +2 -3
  18. data/lib/fortnox/api/models/base.rb +12 -10
  19. data/lib/fortnox/api/models/customer.rb +55 -55
  20. data/lib/fortnox/api/models/label.rb +2 -2
  21. data/lib/fortnox/api/repositories/authentication.rb +61 -0
  22. data/lib/fortnox/api/repositories/base/savers.rb +3 -1
  23. data/lib/fortnox/api/repositories/base.rb +21 -35
  24. data/lib/fortnox/api/repositories.rb +1 -0
  25. data/lib/fortnox/api/request_handling.rb +30 -18
  26. data/lib/fortnox/api/types/document_row.rb +3 -3
  27. data/lib/fortnox/api/types/enums.rb +27 -11
  28. data/lib/fortnox/api/types/model.rb +1 -4
  29. data/lib/fortnox/api/types/sized.rb +2 -2
  30. data/lib/fortnox/api/types.rb +14 -1
  31. data/lib/fortnox/api/version.rb +1 -1
  32. data/lib/fortnox/api.rb +12 -32
  33. data/spec/fortnox/api/mappers/base/canonical_name_sym_spec.rb +4 -4
  34. data/spec/fortnox/api/mappers/base/from_json_spec.rb +10 -12
  35. data/spec/fortnox/api/mappers/base/to_json_spec.rb +48 -57
  36. data/spec/fortnox/api/mappers/base_spec.rb +4 -7
  37. data/spec/fortnox/api/mappers/contexts/json_conversion.rb +38 -33
  38. data/spec/fortnox/api/mappers/unit_spec.rb +3 -4
  39. data/spec/fortnox/api/models/base_spec.rb +27 -16
  40. data/spec/fortnox/api/models/unit_spec.rb +5 -3
  41. data/spec/fortnox/api/repositories/article_spec.rb +14 -9
  42. data/spec/fortnox/api/repositories/authentication_spec.rb +103 -0
  43. data/spec/fortnox/api/repositories/base_spec.rb +106 -319
  44. data/spec/fortnox/api/repositories/customer_spec.rb +37 -7
  45. data/spec/fortnox/api/repositories/examples/all.rb +0 -1
  46. data/spec/fortnox/api/repositories/examples/find.rb +5 -8
  47. data/spec/fortnox/api/repositories/examples/only.rb +4 -13
  48. data/spec/fortnox/api/repositories/examples/save.rb +32 -18
  49. data/spec/fortnox/api/repositories/examples/save_with_nested_model.rb +0 -5
  50. data/spec/fortnox/api/repositories/examples/save_with_specially_named_attribute.rb +1 -4
  51. data/spec/fortnox/api/repositories/examples/search.rb +4 -7
  52. data/spec/fortnox/api/repositories/invoice_spec.rb +64 -15
  53. data/spec/fortnox/api/repositories/order_spec.rb +11 -9
  54. data/spec/fortnox/api/repositories/project_spec.rb +7 -6
  55. data/spec/fortnox/api/repositories/terms_of_payment_spec.rb +9 -7
  56. data/spec/fortnox/api/repositories/unit_spec.rb +13 -11
  57. data/spec/fortnox/api/types/country_spec.rb +1 -1
  58. data/spec/fortnox/api/types/email_spec.rb +2 -2
  59. data/spec/fortnox/api/types/examples/document_row.rb +3 -3
  60. data/spec/fortnox/api/types/examples/enum.rb +4 -4
  61. data/spec/fortnox/api/types/examples/types.rb +1 -3
  62. data/spec/fortnox/api/types/housework_types_spec.rb +54 -61
  63. data/spec/fortnox/api/types/model_spec.rb +3 -27
  64. data/spec/fortnox/api/types/order_row_spec.rb +2 -2
  65. data/spec/fortnox/api/types/required_spec.rb +6 -11
  66. data/spec/fortnox/api/types/sales_account_spec.rb +57 -0
  67. data/spec/fortnox/api_spec.rb +19 -124
  68. data/spec/spec_helper.rb +0 -14
  69. data/spec/support/helpers/configuration_helper.rb +30 -3
  70. data/spec/support/helpers.rb +1 -1
  71. data/spec/support/matchers/type/attribute_matcher.rb +2 -2
  72. data/spec/support/matchers/type/have_nullable_date_matcher.rb +6 -4
  73. data/spec/support/matchers/type/have_nullable_matcher.rb +1 -1
  74. data/spec/support/matchers/type/have_nullable_string_matcher.rb +5 -5
  75. data/spec/support/matchers/type/require_attribute_matcher.rb +5 -5
  76. data/spec/support/matchers/type/type_matcher.rb +1 -1
  77. data/spec/support/vcr_setup.rb +16 -0
  78. data/spec/vcr_cassettes/articles/all.yml +16 -43
  79. data/spec/vcr_cassettes/articles/find_by_hash_failure.yml +10 -12
  80. data/spec/vcr_cassettes/articles/find_failure.yml +10 -12
  81. data/spec/vcr_cassettes/articles/find_id_1.yml +13 -14
  82. data/spec/vcr_cassettes/articles/find_new.yml +14 -16
  83. data/spec/vcr_cassettes/articles/multi_param_find_by_hash.yml +13 -15
  84. data/spec/vcr_cassettes/articles/save_new.yml +13 -15
  85. data/spec/vcr_cassettes/articles/save_old.yml +14 -16
  86. data/spec/vcr_cassettes/articles/save_with_specially_named_attribute.yml +13 -15
  87. data/spec/vcr_cassettes/articles/search_by_name.yml +16 -15
  88. data/spec/vcr_cassettes/articles/search_miss.yml +10 -12
  89. data/spec/vcr_cassettes/articles/search_with_special_char.yml +10 -12
  90. data/spec/vcr_cassettes/articles/single_param_find_by_hash.yml +13 -27
  91. data/spec/vcr_cassettes/authentication/expired_token.yml +54 -0
  92. data/spec/vcr_cassettes/authentication/invalid_authorization.yml +57 -0
  93. data/spec/vcr_cassettes/authentication/invalid_refresh_token.yml +58 -0
  94. data/spec/vcr_cassettes/authentication/valid_request.yml +63 -0
  95. data/spec/vcr_cassettes/customers/all.yml +20 -127
  96. data/spec/vcr_cassettes/customers/find_by_hash_failure.yml +10 -12
  97. data/spec/vcr_cassettes/customers/find_failure.yml +10 -12
  98. data/spec/vcr_cassettes/customers/find_id_1.yml +14 -15
  99. data/spec/vcr_cassettes/customers/find_new.yml +13 -15
  100. data/spec/vcr_cassettes/customers/find_with_sales_account.yml +63 -0
  101. data/spec/vcr_cassettes/customers/multi_param_find_by_hash.yml +13 -15
  102. data/spec/vcr_cassettes/customers/save_new.yml +12 -14
  103. data/spec/vcr_cassettes/customers/save_new_with_country_code_SE.yml +12 -14
  104. data/spec/vcr_cassettes/customers/save_new_with_sales_account.yml +63 -0
  105. data/spec/vcr_cassettes/customers/save_old.yml +13 -15
  106. data/spec/vcr_cassettes/customers/save_with_specially_named_attribute.yml +12 -14
  107. data/spec/vcr_cassettes/customers/search_by_name.yml +13 -45
  108. data/spec/vcr_cassettes/customers/search_miss.yml +10 -12
  109. data/spec/vcr_cassettes/customers/search_with_special_char.yml +10 -12
  110. data/spec/vcr_cassettes/customers/single_param_find_by_hash.yml +14 -16
  111. data/spec/vcr_cassettes/invoices/all.yml +47 -112
  112. data/spec/vcr_cassettes/invoices/filter_hit.yml +14 -18
  113. data/spec/vcr_cassettes/invoices/filter_invalid.yml +10 -12
  114. data/spec/vcr_cassettes/invoices/find_by_hash_failure.yml +10 -12
  115. data/spec/vcr_cassettes/invoices/find_failure.yml +10 -12
  116. data/spec/vcr_cassettes/invoices/find_id_1.yml +15 -16
  117. data/spec/vcr_cassettes/invoices/find_new.yml +16 -18
  118. data/spec/vcr_cassettes/invoices/multi_param_find_by_hash.yml +13 -15
  119. data/spec/vcr_cassettes/invoices/row_description_limit.yml +65 -0
  120. data/spec/vcr_cassettes/invoices/save_new.yml +14 -16
  121. data/spec/vcr_cassettes/invoices/save_new_with_comments.yml +14 -16
  122. data/spec/vcr_cassettes/invoices/save_new_with_country.yml +14 -15
  123. data/spec/vcr_cassettes/invoices/save_new_with_country_GB.yml +15 -16
  124. data/spec/vcr_cassettes/invoices/save_new_with_country_Norge.yml +14 -15
  125. data/spec/vcr_cassettes/invoices/save_new_with_country_Norway.yml +14 -15
  126. data/spec/vcr_cassettes/invoices/save_new_with_country_Sverige.yml +14 -15
  127. data/spec/vcr_cassettes/invoices/save_new_with_country_VA.yml +15 -16
  128. data/spec/vcr_cassettes/invoices/save_new_with_country_VI.yml +15 -16
  129. data/spec/vcr_cassettes/invoices/save_new_with_country_empty_string.yml +14 -15
  130. data/spec/vcr_cassettes/invoices/save_new_with_country_nil.yml +14 -15
  131. data/spec/vcr_cassettes/invoices/save_new_with_unsaved_parent.yml +65 -0
  132. data/spec/vcr_cassettes/invoices/save_old.yml +16 -18
  133. data/spec/vcr_cassettes/invoices/save_old_with_empty_comments.yml +16 -18
  134. data/spec/vcr_cassettes/invoices/save_old_with_empty_country.yml +16 -17
  135. data/spec/vcr_cassettes/invoices/save_old_with_nil_comments.yml +16 -18
  136. data/spec/vcr_cassettes/invoices/save_old_with_nil_country.yml +16 -17
  137. data/spec/vcr_cassettes/invoices/save_with_nested_model.yml +15 -16
  138. data/spec/vcr_cassettes/invoices/save_with_specially_named_attribute.yml +14 -15
  139. data/spec/vcr_cassettes/invoices/search_by_name.yml +13 -21
  140. data/spec/vcr_cassettes/invoices/search_miss.yml +10 -12
  141. data/spec/vcr_cassettes/invoices/search_with_special_char.yml +10 -12
  142. data/spec/vcr_cassettes/invoices/single_param_find_by_hash.yml +14 -16
  143. data/spec/vcr_cassettes/orders/all.yml +19 -113
  144. data/spec/vcr_cassettes/orders/filter_hit.yml +14 -20
  145. data/spec/vcr_cassettes/orders/filter_invalid.yml +10 -12
  146. data/spec/vcr_cassettes/orders/find_by_hash_failure.yml +10 -12
  147. data/spec/vcr_cassettes/orders/find_failure.yml +10 -12
  148. data/spec/vcr_cassettes/orders/find_id_1.yml +17 -17
  149. data/spec/vcr_cassettes/orders/find_new.yml +16 -18
  150. data/spec/vcr_cassettes/orders/housework_invalid_tax_reduction_type.yml +11 -13
  151. data/spec/vcr_cassettes/orders/housework_othercoses_invalid.yml +11 -13
  152. data/spec/vcr_cassettes/orders/housework_type_babysitting.yml +15 -16
  153. data/spec/vcr_cassettes/orders/housework_type_cleaning.yml +15 -16
  154. data/spec/vcr_cassettes/orders/housework_type_construction.yml +15 -16
  155. data/spec/vcr_cassettes/orders/housework_type_cooking.yml +11 -13
  156. data/spec/vcr_cassettes/orders/housework_type_electricity.yml +15 -16
  157. data/spec/vcr_cassettes/orders/housework_type_gardening.yml +15 -16
  158. data/spec/vcr_cassettes/orders/housework_type_glassmetalwork.yml +15 -16
  159. data/spec/vcr_cassettes/orders/housework_type_grounddrainagework.yml +15 -16
  160. data/spec/vcr_cassettes/orders/housework_type_hvac.yml +15 -16
  161. data/spec/vcr_cassettes/orders/housework_type_itservices.yml +15 -16
  162. data/spec/vcr_cassettes/orders/housework_type_majorappliancerepair.yml +15 -16
  163. data/spec/vcr_cassettes/orders/housework_type_masonry.yml +15 -16
  164. data/spec/vcr_cassettes/orders/housework_type_movingservices.yml +15 -16
  165. data/spec/vcr_cassettes/orders/housework_type_othercare.yml +15 -16
  166. data/spec/vcr_cassettes/orders/housework_type_othercosts.yml +15 -16
  167. data/spec/vcr_cassettes/orders/housework_type_paintingwallpapering.yml +15 -16
  168. data/spec/vcr_cassettes/orders/housework_type_snowplowing.yml +15 -16
  169. data/spec/vcr_cassettes/orders/housework_type_textileclothing.yml +15 -16
  170. data/spec/vcr_cassettes/orders/housework_type_tutoring.yml +11 -13
  171. data/spec/vcr_cassettes/orders/multi_param_find_by_hash.yml +13 -15
  172. data/spec/vcr_cassettes/orders/save_new.yml +16 -18
  173. data/spec/vcr_cassettes/orders/save_old.yml +16 -18
  174. data/spec/vcr_cassettes/orders/save_with_nested_model.yml +15 -16
  175. data/spec/vcr_cassettes/orders/search_by_name.yml +13 -17
  176. data/spec/vcr_cassettes/orders/search_miss.yml +10 -12
  177. data/spec/vcr_cassettes/orders/search_with_special_char.yml +10 -12
  178. data/spec/vcr_cassettes/orders/single_param_find_by_hash.yml +14 -16
  179. data/spec/vcr_cassettes/projects/all.yml +14 -37
  180. data/spec/vcr_cassettes/projects/find_by_hash_failure.yml +10 -12
  181. data/spec/vcr_cassettes/projects/find_failure.yml +10 -12
  182. data/spec/vcr_cassettes/projects/find_id_1.yml +13 -15
  183. data/spec/vcr_cassettes/projects/find_new.yml +14 -16
  184. data/spec/vcr_cassettes/projects/multi_param_find_by_hash.yml +15 -16
  185. data/spec/vcr_cassettes/projects/save_new.yml +13 -15
  186. data/spec/vcr_cassettes/projects/save_old.yml +14 -16
  187. data/spec/vcr_cassettes/projects/single_param_find_by_hash.yml +12 -14
  188. data/spec/vcr_cassettes/termsofpayments/all.yml +16 -23
  189. data/spec/vcr_cassettes/termsofpayments/find_failure.yml +10 -12
  190. data/spec/vcr_cassettes/termsofpayments/find_id_1.yml +13 -16
  191. data/spec/vcr_cassettes/termsofpayments/find_new.yml +12 -14
  192. data/spec/vcr_cassettes/termsofpayments/save_new.yml +12 -14
  193. data/spec/vcr_cassettes/termsofpayments/save_old.yml +12 -14
  194. data/spec/vcr_cassettes/units/all.yml +13 -24
  195. data/spec/vcr_cassettes/units/find_failure.yml +10 -12
  196. data/spec/vcr_cassettes/units/find_id_1.yml +13 -15
  197. data/spec/vcr_cassettes/units/find_new.yml +12 -14
  198. data/spec/vcr_cassettes/units/save_new.yml +12 -14
  199. data/spec/vcr_cassettes/units/save_old.yml +12 -14
  200. data/spec/vcr_cassettes/units/save_with_specially_named_attribute.yml +12 -14
  201. metadata +39 -230
  202. data/lib/fortnox/api/circular_queue.rb +0 -39
  203. data/spec/fortnox/api/circular_queue_spec.rb +0 -52
  204. data/spec/support/helpers/when_performing_helper.rb +0 -7
  205. data/temp.txt +0 -1
@@ -22,8 +22,8 @@ module Fortnox
22
22
  # DeliveredQuantity Delivered quantity. 14 digits
23
23
  attribute :delivered_quantity, Types::Sized::Float[-9_999_999_999_999.9, 9_999_999_999_999.9]
24
24
 
25
- # Description Description Row description. 50 characters
26
- attribute :description, Types::Sized::String[50]
25
+ # Description Description Row description.
26
+ attribute :description, Types::Sized::String[255]
27
27
 
28
28
  # Discount amount. 12 digits (for amount) / 5 digits (for percent)
29
29
  # TODO(hannes): Verify that we can send in more than 5 digits through
@@ -45,7 +45,7 @@ module Fortnox
45
45
  attribute :housework_type, Types::HouseworkType
46
46
 
47
47
  # Price Price per unit. 12 digits
48
- attribute :price, Types::Sized::Float[0.0, 99_999_999_999.9]
48
+ attribute :price, Types::Sized::Float[-9_999_999_999, 99_999_999_999.9]
49
49
 
50
50
  # Project Code of the project for the row.
51
51
  attribute :project, Types::Nullable::String
@@ -7,6 +7,7 @@ module Fortnox
7
7
  def self.sized(size)
8
8
  lambda do |value|
9
9
  return nil if value == ''
10
+
10
11
  value.to_s.upcase[0...size] unless value.nil?
11
12
  end
12
13
  end
@@ -14,14 +15,16 @@ module Fortnox
14
15
  def self.default
15
16
  lambda do |value|
16
17
  return nil if value == ''
17
- value.to_s.upcase unless value.nil?
18
+
19
+ value&.to_s&.upcase
18
20
  end
19
21
  end
20
22
 
21
23
  def self.lower_case
22
24
  lambda do |value|
23
25
  return nil if value == ''
24
- value.to_s.downcase unless value.nil?
26
+
27
+ value&.to_s&.downcase
25
28
  end
26
29
  end
27
30
  end
@@ -33,16 +36,29 @@ module Fortnox
33
36
  'AMOUNT', 'PERCENT'
34
37
  )
35
38
  HOUSEWORK_TYPES = {
36
- rot: %w[
37
- CONSTRUCTION ELECTRICITY GLASSMETALWORK GROUNDDRAINAGEWORK
38
- MASONRY PAINTINGWALLPAPERING HVAC OTHERCOSTS
39
+ rot: [
40
+ 'CONSTRUCTION',
41
+ 'ELECTRICITY',
42
+ 'GLASSMETALWORK',
43
+ 'GROUNDDRAINAGEWORK',
44
+ 'HVAC',
45
+ 'MASONRY',
46
+ 'OTHERCOSTS',
47
+ 'PAINTINGWALLPAPERING'
39
48
  ],
40
- rut: %w[
41
- MAJORAPPLIANCEREPAIR MOVINGSERVICES ITSERVICES CLEANING
42
- TEXTILECLOTHING SNOWPLOWING GARDENING BABYSITTING OTHERCARE
43
- OTHERCOSTS
49
+ rut: [
50
+ 'BABYSITTING',
51
+ 'CLEANING',
52
+ 'GARDENING',
53
+ 'ITSERVICES',
54
+ 'MAJORAPPLIANCEREPAIR',
55
+ 'MOVINGSERVICES',
56
+ 'OTHERCARE',
57
+ 'OTHERCOSTS',
58
+ 'SNOWPLOWING',
59
+ 'TEXTILECLOTHING'
44
60
  ],
45
- legacy_rut: %w[COOKING TUTORING]
61
+ legacy_rut: ['COOKING', 'TUTORING']
46
62
  }.freeze
47
63
 
48
64
  # TODO: RUT to be added:
@@ -85,7 +101,7 @@ module Fortnox
85
101
  'SEVAT', 'SEREVERSEDVAT', 'EUREVERSEDVAT', 'EUVAT', 'EXPORT'
86
102
  )
87
103
  DefaultDeliveryTypeValues = Types::Strict::String.enum(
88
- 'PRINT', 'EMAIL', 'PRINTSERVICE'
104
+ 'PRINT', 'EMAIL', 'PRINTSERVICE', 'ELECTRONICINVOICE'
89
105
  )
90
106
  ProjectStatusTypes = Types::Strict::String.enum(
91
107
  'NOTSTARTED', 'ONGOING', 'COMPLETED'
@@ -5,7 +5,6 @@ module Fortnox
5
5
  module Types
6
6
  class Model < Dry::Struct
7
7
  transform_types(&:omittable)
8
- transform_keys(&:to_sym)
9
8
 
10
9
  def initialize(input_attributes)
11
10
  if (missing_key = first_missing_required_key(input_attributes))
@@ -19,9 +18,7 @@ module Fortnox
19
18
  private
20
19
 
21
20
  def missing_keys(attributes)
22
- non_nil_attributes = attributes.reject { |_, value| value.nil? }
23
-
24
- attribute_keys = non_nil_attributes.keys
21
+ attribute_keys = attributes.compact.keys
25
22
  schema_keys = self.class.schema.keys.map(&:name)
26
23
 
27
24
  schema_keys - attribute_keys
@@ -7,7 +7,7 @@ module Fortnox
7
7
  module String
8
8
  def self.[](size)
9
9
  Types::Strict::String.constrained(max_size: size).optional.constructor do |value|
10
- value.to_s unless value.nil?
10
+ value&.to_s
11
11
  end
12
12
  end
13
13
  end
@@ -23,7 +23,7 @@ module Fortnox
23
23
  module Float
24
24
  def self.[](low, high)
25
25
  Types::Strict::Float.constrained(gteq: low, lteq: high).optional.constructor do |value|
26
- value.to_f unless value.nil?
26
+ value&.to_f
27
27
  end
28
28
  end
29
29
  end
@@ -38,6 +38,7 @@ module Fortnox
38
38
  .optional
39
39
  .constructor do |value|
40
40
  next nil if value.nil? || value == ''
41
+
41
42
  value
42
43
  end
43
44
 
@@ -92,7 +93,7 @@ module Fortnox
92
93
  Email = Strict::String
93
94
  .constrained(max_size: 1024, format: /^$|\A[[[:alnum:]]+-_.]+@[\w+-_.]+\.[a-z]+\z/i)
94
95
  .optional
95
- .constructor { |v| v.to_s.downcase unless v.nil? }
96
+ .constructor { |v| v&.to_s&.downcase }
96
97
 
97
98
  HouseworkType = Strict::String
98
99
  .constrained(included_in: HouseworkTypes.values)
@@ -119,6 +120,18 @@ module Fortnox
119
120
  .optional
120
121
  .constructor(EnumConstructors.lower_case)
121
122
 
123
+ # Some Fortnox endpoints returns a String and some returns an Integer...
124
+ # The documentation says it should be a string, so let's keep it as a string.
125
+ SalesAccount = Strict::String
126
+ .constrained(format: /^[0-9]{4}$/)
127
+ .optional
128
+ .constructor do |value|
129
+ next nil if value == '' || value.nil?
130
+ next value.to_s if value.is_a?(::Integer)
131
+
132
+ value
133
+ end
134
+
122
135
  require_relative 'types/model'
123
136
  require_relative 'types/default_delivery_types'
124
137
  require_relative 'types/default_templates'
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Fortnox
4
4
  module API
5
- VERSION = '0.8.2'
5
+ VERSION = '0.9.0'
6
6
  end
7
7
  end
data/lib/fortnox/api.rb CHANGED
@@ -5,7 +5,6 @@ require 'dry-configurable'
5
5
  require 'dry-container'
6
6
  require 'logger'
7
7
 
8
- require_relative 'api/circular_queue'
9
8
  require_relative 'api/version'
10
9
 
11
10
  module Fortnox
@@ -14,10 +13,7 @@ module Fortnox
14
13
 
15
14
  DEFAULT_CONFIGURATION = {
16
15
  base_url: 'https://api.fortnox.se/3/',
17
- client_secret: nil,
18
- token_store: {},
19
- access_token: nil,
20
- access_tokens: nil,
16
+ token_url: 'https://apps.fortnox.se/oauth-v1/token',
21
17
  debugging: false,
22
18
  logger: lambda {
23
19
  logger = Logger.new($stdout)
@@ -27,23 +23,18 @@ module Fortnox
27
23
  }.freeze
28
24
 
29
25
  setting :base_url, default: DEFAULT_CONFIGURATION[:base_url]
30
- setting :client_secret, default: DEFAULT_CONFIGURATION[:client_secret]
31
- setting :token_store, default: DEFAULT_CONFIGURATION[:token_store]
32
- setting :access_token, default: DEFAULT_CONFIGURATION[:access_token], constructor: (proc do |value|
33
- next if value.nil? # nil is a valid unassigned value
34
- invalid_access_token_format!(value) unless value.is_a?(String)
35
- config.token_store = { default: value }
36
- value
37
- end)
38
- setting :access_tokens, default: DEFAULT_CONFIGURATION[:access_tokens], constructor: (proc do |value|
39
- next if value.nil? # nil is a valid unassigned value
40
- invalid_access_tokens_format!(value) unless value.is_a?(Hash) || value.is_a?(Array)
41
- config.token_store = value.is_a?(Hash) ? value : { default: value }
42
- value
43
- end)
26
+ setting :token_url, default: DEFAULT_CONFIGURATION[:token_url]
44
27
  setting :debugging, default: DEFAULT_CONFIGURATION[:debugging], reader: true
45
28
  setting :logger, default: DEFAULT_CONFIGURATION[:logger], reader: true
46
29
 
30
+ def self.access_token=(token)
31
+ Thread.current[:access_token] = token
32
+ end
33
+
34
+ def self.access_token
35
+ Thread.current[:access_token]
36
+ end
37
+
47
38
  class Exception < StandardError
48
39
  end
49
40
 
@@ -59,21 +50,10 @@ module Fortnox
59
50
  class MissingConfiguration < Fortnox::API::Exception
60
51
  end
61
52
 
62
- Registry = Dry::Container.new
63
-
64
- def self.invalid_access_token_format!(value)
65
- raise ArgumentError,
66
- 'expected a String, but '\
67
- "#{value.inspect} is a(n) #{value.class}"
53
+ class MissingAccessToken < Fortnox::API::Exception
68
54
  end
69
- private_class_method :invalid_access_token_format!
70
55
 
71
- def self.invalid_access_tokens_format!(value)
72
- raise ArgumentError,
73
- 'expected a Hash or an Array, but '\
74
- "#{value.inspect} is a(n) #{value.class}"
75
- end
76
- private_class_method :invalid_access_tokens_format!
56
+ Registry = Dry::Container.new
77
57
  end
78
58
  end
79
59
 
@@ -6,6 +6,8 @@ require 'fortnox/api/mappers/base/canonical_name_sym'
6
6
  describe Fortnox::API::Mapper::CanonicalNameSym do
7
7
  describe '.canonical_name_sym' do
8
8
  context 'with simple class' do
9
+ subject { TestClass.canonical_name_sym }
10
+
9
11
  before do
10
12
  test_class = Class.new do
11
13
  extend Fortnox::API::Mapper::CanonicalNameSym
@@ -14,12 +16,12 @@ describe Fortnox::API::Mapper::CanonicalNameSym do
14
16
  stub_const('TestClass', test_class)
15
17
  end
16
18
 
17
- subject { TestClass.canonical_name_sym }
18
-
19
19
  it { is_expected.to eq(:testclass) }
20
20
  end
21
21
 
22
22
  context 'when class included in module' do
23
+ subject { Something::Test.canonical_name_sym }
24
+
23
25
  before do
24
26
  test_class = Class.new do
25
27
  extend Fortnox::API::Mapper::CanonicalNameSym
@@ -28,8 +30,6 @@ describe Fortnox::API::Mapper::CanonicalNameSym do
28
30
  stub_const('Something::Test', test_class)
29
31
  end
30
32
 
31
- subject { Something::Test.canonical_name_sym }
32
-
33
33
  it { is_expected.to eq(:test) }
34
34
  end
35
35
  end
@@ -8,14 +8,12 @@ describe Fortnox::API::Mapper::FromJSON do
8
8
  include_context 'with JSON conversion'
9
9
 
10
10
  before do
11
- module Test
12
- class BaseMapper
13
- include Fortnox::API::Mapper::FromJSON
14
- end
11
+ Test::BaseMapper.class_eval do
12
+ include Fortnox::API::Mapper::FromJSON # rubocop:disable RSpec/DescribedClass
15
13
  end
16
14
 
17
- register_mapper(:categories, Test::CategoryMapper)
18
- register_mapper(:designer, Test::ProductDesignerMapper)
15
+ add_to_registry(:categories, Test::CategoryMapper)
16
+ add_to_registry(:designer, Test::ProductDesignerMapper)
19
17
  end
20
18
 
21
19
  let(:mapper) { Test::ProductMapper.new }
@@ -26,10 +24,10 @@ describe Fortnox::API::Mapper::FromJSON do
26
24
  {
27
25
  'Product' => {
28
26
  '@url': 'someurl@example.com',
29
- 'Name': 'Ford Mustang',
30
- 'VAT': 30_000,
31
- 'Categories': [{ 'Name' => 'Cars', 'ID' => '1' }, { 'Name' => 'Fast cars', 'ID' => '2' }],
32
- 'Designer': { 'Name' => 'John Najjar', 'ID' => '23' }
27
+ Name: 'Ford Mustang',
28
+ VAT: 30_000,
29
+ Categories: [{ Name: 'Cars', ID: '1' }, { Name: 'Fast cars', ID: '2' }],
30
+ Designer: { Name: 'John Najjar', ID: '23' }
33
31
  }
34
32
  }
35
33
  end
@@ -47,7 +45,7 @@ describe Fortnox::API::Mapper::FromJSON do
47
45
  # expect( entity_hash[:url] ).to eq 'someurl@example.com'
48
46
  # end
49
47
 
50
- context 'with nested models' do
48
+ describe 'nested models' do
51
49
  let(:expected_nested_model_hash) do
52
50
  [{ name: 'Cars', id: '1' }, { name: 'Fast cars', id: '2' }]
53
51
  end
@@ -57,7 +55,7 @@ describe Fortnox::API::Mapper::FromJSON do
57
55
  end
58
56
  end
59
57
 
60
- context 'with nested model' do
58
+ describe 'nested model' do
61
59
  let(:expected_nested_model_hash) { { name: 'John Najjar', id: '23' } }
62
60
 
63
61
  specify 'is converted correctly' do
@@ -5,73 +5,64 @@ require 'fortnox/api/mappers/base/to_json'
5
5
  require 'fortnox/api/mappers/contexts/json_conversion'
6
6
 
7
7
  describe Fortnox::API::Mapper::ToJSON do
8
- # TODO: Following error is risen sometimes when all specs are run:
9
- # "NoMethodError: undefined method `call\' for Test::ProductMapper:Class`"
10
- it 'should be tested when random error is fixed!'
8
+ include_context 'with JSON conversion'
11
9
 
12
- describe 'wrap_entity_json_hash' do
13
- it 'should be tested'
10
+ before do
11
+ Test::BaseMapper.class_eval do
12
+ include Fortnox::API::Mapper::ToJSON # rubocop:disable RSpec/DescribedClass
13
+ end
14
14
  end
15
15
 
16
- # include_context 'with JSON conversion'
16
+ let(:mapper) { Test::ProductMapper.new }
17
17
 
18
- # before do
19
- # module Test
20
- # class BaseMapper
21
- # include Fortnox::API::Mapper::ToJSON
22
- # end
23
- # end
24
- # end
18
+ describe '#entity_to_hash' do
19
+ let(:product) do
20
+ category1 = Test::Category.new(name: 'Cars', id: '1')
21
+ category2 = Test::Category.new(name: 'Fast Cars', id: '2')
22
+ product_designer = Test::ProductDesigner.new(name: 'John Najjar', id: '23')
25
23
 
26
- # let( :mapper ){ Test::ProductMapper.new }
24
+ Test::Product.new(url: 'someurl@example.com',
25
+ name: 'Ford Mustang',
26
+ vat: 30_000.0,
27
+ categories: [category1, category2],
28
+ designer: product_designer)
29
+ end
30
+ let(:keys_to_filter) { [:url] }
31
+ let(:returned_hash) { mapper.entity_to_hash(product, keys_to_filter) }
32
+ let(:inner_hash) { returned_hash['Product'] }
27
33
 
28
- # describe 'entity_to_hash' do
29
- # let( :category1 ){ Test::Category.new( name: 'Cars', id: '1' ) }
30
- # let( :category2 ){ Test::Category.new( name: 'Fast Cars', id: '2' ) }
31
- # let( :product_designer ){ Test::ProductDesigner.new( name: 'John Najjar', id: '23' ) }
32
- # let( :product ) do
33
- # Test::Product.new( url: 'someurl@example.com',
34
- # name: 'Ford Mustang',
35
- # vat: 30000.0 ,
36
- # categories: [category1, category2],
37
- # designer: product_designer )
38
- # end
39
- # let( :keys_to_filter ){ [:url] }
40
- # let( :returned_hash ){ mapper.entity_to_hash( product, keys_to_filter ) }
41
- # let( :inner_hash ){ returned_hash['Product'] }
34
+ it 'includes JSON entity wrapper' do
35
+ expect(returned_hash).to have_key('Product')
36
+ end
42
37
 
43
- # it 'includes JSON entity wrapper' do
44
- # expect( returned_hash ).to have_key( 'Product' )
45
- # end
38
+ describe 'keys without mapping' do
39
+ specify 'converts correctly' do
40
+ expect(inner_hash['Name']).to eq('Ford Mustang')
41
+ end
42
+ end
46
43
 
47
- # context 'with keys without mapping' do
48
- # specify 'converts correctly' do
49
- # expect( inner_hash['Name'] ).to eq( 'Ford Mustang' )
50
- # end
51
- # end
44
+ describe 'keys to filter' do
45
+ specify 'filteres out those keys' do
46
+ expect(inner_hash).not_to have_key('@url')
47
+ end
48
+ end
52
49
 
53
- # context 'with keys to filter' do
54
- # specify 'filteres out those keys' do
55
- # expect( inner_hash ).not_to have_key( '@url' )
56
- # end
57
- # end
50
+ describe 'nested models' do
51
+ let(:expected_nested_model_hash) do
52
+ [{ 'Name' => 'Cars', 'ID' => '1' }, { 'Name' => 'Fast Cars', 'ID' => '2' }]
53
+ end
58
54
 
59
- # context 'with nested models' do
60
- # let( :expected_nested_model_hash ) do
61
- # [{"Name" => "Cars", "ID" => "1"}, {"Name" => "Fast Cars", "ID" => "2"}]
62
- # end
55
+ specify 'are converted correctly' do
56
+ expect(inner_hash['Categories']).to eq(expected_nested_model_hash)
57
+ end
58
+ end
63
59
 
64
- # specify 'are converted correctly' do
65
- # expect( inner_hash['Categories'] ).to eq( expected_nested_model_hash )
66
- # end
67
- # end
60
+ describe 'nested model' do
61
+ let(:expected_nested_model_hash) { { 'Name' => 'John Najjar', 'ID' => '23' } }
68
62
 
69
- # context 'with nested model' do
70
- # let( :expected_nested_model_hash ){ { 'Name' => 'John Najjar', 'ID' => '23' } }
71
-
72
- # specify 'is converted correctly' do
73
- # expect( inner_hash['Designer'] ).to eq( expected_nested_model_hash )
74
- # end
75
- # end
76
- # end
63
+ specify 'is converted correctly' do
64
+ expect(inner_hash['Designer']).to eq(expected_nested_model_hash)
65
+ end
66
+ end
67
+ end
77
68
  end
@@ -99,6 +99,7 @@ describe Fortnox::API::Mapper::Base do
99
99
  let(:value) { true }
100
100
  end
101
101
  end
102
+
102
103
  describe 'falseclass' do
103
104
  include_examples 'identity mapper', :falseclass do
104
105
  let(:value) { false }
@@ -126,17 +127,15 @@ describe Fortnox::API::Mapper::Base do
126
127
 
127
128
  describe 'special cases' do
128
129
  context 'with SE' do
129
- subject { mapper.call('SE') }
130
-
131
130
  it 'translates code to country name in Swedish' do
132
- is_expected.to eq('Sverige')
131
+ expect(mapper.call('SE')).to eq('Sverige')
133
132
  end
134
133
  end
135
134
 
136
135
  context 'with nil value' do
137
136
  subject { mapper.call(nil) }
138
137
 
139
- it { is_expected.to eq(nil) }
138
+ it { is_expected.to be_nil }
140
139
  end
141
140
 
142
141
  context 'with empty string' do
@@ -146,10 +145,8 @@ describe Fortnox::API::Mapper::Base do
146
145
  end
147
146
 
148
147
  context 'with nonsense' do
149
- subject { -> { mapper.call('nonsense') } }
150
-
151
148
  it 'is not supported (since input is sanitised) and therefore blows up' do
152
- raise_error(NoMethodError)
149
+ expect { mapper.call('nonsense') }.to raise_error(NoMethodError)
153
150
  end
154
151
  end
155
152
  end
@@ -2,56 +2,61 @@
2
2
 
3
3
  require 'fortnox/api'
4
4
  require 'fortnox/api/mappers'
5
+ require 'dry/container/stub'
5
6
 
6
7
  shared_context 'with JSON conversion' do
7
- before do
8
- module Test
9
- class BaseMapper
10
- end
11
-
12
- class CategoryMapper < BaseMapper
13
- KEY_MAP = { id: 'ID' }.freeze
14
- end
15
-
16
- class ProductDesignerMapper < BaseMapper
17
- KEY_MAP = { id: 'ID' }.freeze
18
- end
19
-
20
- class ProductMapper < BaseMapper
21
- KEY_MAP = {
22
- vat: 'VAT',
23
- url: '@url' # TODO: How to handle url attribute?
24
- }.freeze
25
- JSON_ENTITY_WRAPPER = 'Product'
26
- JSON_COLLECTION_WRAPPER = 'Products'
27
- end
8
+ include Helpers::Configuration
28
9
 
29
- class Category < Fortnox::API::Model::Base
10
+ before do
11
+ stub_const('Test::BaseMapper', Class.new)
12
+
13
+ stub_const('Test::CategoryMapper', Class.new(Test::BaseMapper))
14
+ stub_const('Test::CategoryMapper::KEY_MAP', { id: 'ID' }.freeze)
15
+
16
+ stub_const('Test::ProductDesignerMapper', Class.new(Test::BaseMapper))
17
+ stub_const('Test::ProductDesignerMapper::KEY_MAP', { id: 'ID' }.freeze)
18
+
19
+ stub_const('Test::ProductMapper', Class.new(Test::BaseMapper))
20
+ stub_const(
21
+ 'Test::ProductMapper::KEY_MAP',
22
+ {
23
+ vat: 'VAT',
24
+ url: '@url' # TODO: How to handle url attribute?
25
+ }.freeze
26
+ )
27
+ stub_const('Test::ProductMapper::JSON_ENTITY_WRAPPER', 'Product')
28
+ stub_const('Test::ProductMapper::JSON_COLLECTION_WRAPPER', 'Products')
29
+
30
+ stub_const(
31
+ 'Test::Category',
32
+ Class.new(Fortnox::API::Model::Base) do
30
33
  attribute :name, 'strict.string'
31
34
  attribute :id, 'strict.string'
32
35
  end
36
+ )
33
37
 
34
- class ProductDesigner < Fortnox::API::Model::Base
38
+ stub_const(
39
+ 'Test::ProductDesigner',
40
+ Class.new(Fortnox::API::Model::Base) do
35
41
  attribute :name, 'strict.string'
36
42
  attribute :id, 'strict.string'
37
43
  end
44
+ )
38
45
 
39
- class Product < Fortnox::API::Model::Base
46
+ stub_const(
47
+ 'Test::Product',
48
+ Class.new(Fortnox::API::Model::Base) do
40
49
  attribute :url, 'strict.string'
41
50
  attribute :name, 'strict.string'
42
51
  attribute :vat, 'strict.float'
43
52
  attribute :categories, Dry::Types['coercible.array'].of(Test::Category)
44
53
  attribute :designer, Test::ProductDesigner
45
54
  end
46
- end
47
-
48
- def register_mapper(mapper_sym, mapper)
49
- return if Fortnox::API::Registry.key? mapper_sym
50
- Fortnox::API::Registry.register(mapper_sym, mapper)
51
- end
55
+ )
52
56
 
53
- register_mapper(:category, Test::CategoryMapper)
54
- register_mapper(:productdesigner, Test::ProductDesignerMapper)
55
- register_mapper(:product, Test::ProductMapper)
57
+ Fortnox::API::Registry.enable_stubs!
58
+ add_to_registry(:category, Test::CategoryMapper)
59
+ add_to_registry(:productdesigner, Test::ProductDesignerMapper)
60
+ add_to_registry(:product, Test::ProductMapper)
56
61
  end
57
62
  end
@@ -8,7 +8,6 @@ require 'fortnox/api/mappers/examples/mapper'
8
8
 
9
9
  module Fortnox
10
10
  module API
11
- # Shhh Rubocop, we don't need a comment here ... Really
12
11
  module Mapper
13
12
  describe Unit do
14
13
  context 'when mapping model' do
@@ -17,13 +16,13 @@ module Fortnox
17
16
  let(:model_hash) { { code: 'lbs', description: 'Pounds' } }
18
17
 
19
18
  describe '#entity_to_hash' do
20
- subject { Unit.new.entity_to_hash(model, {}) }
19
+ subject { described_class.new.entity_to_hash(model, {}) }
21
20
 
22
21
  it { is_expected.to eq(serialised_model_hash) }
23
22
  end
24
23
 
25
24
  describe '#wrapped_json_hash_to_entity_hash' do
26
- subject { Unit.new.wrapped_json_hash_to_entity_hash(serialised_model_hash) }
25
+ subject { described_class.new.wrapped_json_hash_to_entity_hash(serialised_model_hash) }
27
26
 
28
27
  it { is_expected.to eq(model_hash) }
29
28
  end
@@ -46,7 +45,7 @@ module Fortnox
46
45
  end
47
46
 
48
47
  describe '#wrapped_json_collection_to_entities_hash' do
49
- subject { Unit.new.wrapped_json_collection_to_entities_hash(serialised_collection_hash) }
48
+ subject { described_class.new.wrapped_json_collection_to_entities_hash(serialised_collection_hash) }
50
49
 
51
50
  it { is_expected.to eq(collection_hash) }
52
51
  end