fortnox-api 0.2.0 → 0.3.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +1 -1
  4. data/fortnox-api.gemspec +3 -1
  5. data/lib/fortnox/api/mappers.rb +1 -0
  6. data/lib/fortnox/api/mappers/project.rb +15 -0
  7. data/lib/fortnox/api/models.rb +1 -0
  8. data/lib/fortnox/api/models/base.rb +1 -1
  9. data/lib/fortnox/api/models/customer.rb +0 -0
  10. data/lib/fortnox/api/models/project.rb +40 -0
  11. data/lib/fortnox/api/repositories.rb +1 -0
  12. data/lib/fortnox/api/repositories/base.rb +1 -1
  13. data/lib/fortnox/api/repositories/base/loaders.rb +13 -10
  14. data/lib/fortnox/api/repositories/base/savers.rb +3 -3
  15. data/lib/fortnox/api/repositories/project.rb +15 -0
  16. data/lib/fortnox/api/types.rb +2 -0
  17. data/lib/fortnox/api/types/enums.rb +3 -0
  18. data/lib/fortnox/api/version.rb +1 -1
  19. data/spec/fortnox/api/mappers/project_spec.rb +10 -0
  20. data/spec/fortnox/api/models/customer_spec.rb +1 -3
  21. data/spec/fortnox/api/models/examples/document_base.rb +2 -2
  22. data/spec/fortnox/api/models/examples/model.rb +11 -5
  23. data/spec/fortnox/api/models/invoice_spec.rb +1 -4
  24. data/spec/fortnox/api/models/order_spec.rb +0 -3
  25. data/spec/fortnox/api/models/project_spec.rb +7 -0
  26. data/spec/fortnox/api/repositories/customer_spec.rb +8 -1
  27. data/spec/fortnox/api/repositories/examples/find.rb +65 -10
  28. data/spec/fortnox/api/repositories/examples/search.rb +11 -0
  29. data/spec/fortnox/api/repositories/invoice_spec.rb +11 -1
  30. data/spec/fortnox/api/repositories/order_spec.rb +11 -1
  31. data/spec/fortnox/api/repositories/project_spec.rb +29 -0
  32. data/spec/fortnox/api/types/enums_spec.rb +1 -0
  33. data/spec/vcr_cassettes/customers/find_by_hash_failure.yml +45 -0
  34. data/spec/vcr_cassettes/customers/find_failure.yml +45 -0
  35. data/spec/vcr_cassettes/customers/multi_param_find_by_hash.yml +46 -0
  36. data/spec/vcr_cassettes/customers/search_with_special_char.yml +45 -0
  37. data/spec/vcr_cassettes/customers/single_param_find_by_hash.yml +47 -0
  38. data/spec/vcr_cassettes/invoices/find_by_hash_failure.yml +45 -0
  39. data/spec/vcr_cassettes/invoices/find_failure.yml +45 -0
  40. data/spec/vcr_cassettes/invoices/multi_param_find_by_hash.yml +46 -0
  41. data/spec/vcr_cassettes/invoices/search_with_special_char.yml +45 -0
  42. data/spec/vcr_cassettes/invoices/single_param_find_by_hash.yml +47 -0
  43. data/spec/vcr_cassettes/orders/find_by_hash_failure.yml +45 -0
  44. data/spec/vcr_cassettes/orders/find_failure.yml +45 -0
  45. data/spec/vcr_cassettes/orders/multi_param_find_by_hash.yml +46 -0
  46. data/spec/vcr_cassettes/orders/search_with_special_char.yml +45 -0
  47. data/spec/vcr_cassettes/orders/single_param_find_by_hash.yml +47 -0
  48. data/spec/vcr_cassettes/projects/all.yml +52 -0
  49. data/spec/vcr_cassettes/projects/find_by_hash_failure.yml +45 -0
  50. data/spec/vcr_cassettes/projects/find_failure.yml +45 -0
  51. data/spec/vcr_cassettes/projects/find_id_1.yml +46 -0
  52. data/spec/vcr_cassettes/projects/find_new.yml +46 -0
  53. data/spec/vcr_cassettes/projects/multi_param_find_by_hash.yml +46 -0
  54. data/spec/vcr_cassettes/projects/save_new.yml +45 -0
  55. data/spec/vcr_cassettes/projects/save_old.yml +46 -0
  56. data/spec/vcr_cassettes/projects/single_param_find_by_hash.yml +46 -0
  57. metadata +66 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4e14de06afe9e870c90a72663071002f8acb19e5
4
- data.tar.gz: 4dc0b9ee89a977e576736102451ef54efa1fcdbf
3
+ metadata.gz: 124c4cde46325d41cffa7a3bf092375697cb6b1d
4
+ data.tar.gz: cb91dde384a1c59e4304f29dc9dbdd9ea0c64d4f
5
5
  SHA512:
6
- metadata.gz: 64e08b1aa313475699bb3e6f9ce31f19bab00ab9104df525dc71dd05bf3c6b6503a9a0405931cb4e92b5d3eba1b1f4143e4b334d207d9f4451a0ad5d78bbae08
7
- data.tar.gz: 171f54a4cca2ec7567e357c7141df4b689e0ccf4622e42ebdac04411a6109b90cd200a4052e056ffa7c03c6699084cce94678514afa2ce223bea34a6c18ced23
6
+ metadata.gz: 8f7bc215cb29128bb0c8385c3769f9aa3576006d69ac84d275b8df4a8799583148e73b77a788538ed4d0fee45e700b7144b803b0a3dca4a2cfe430da1206b0de
7
+ data.tar.gz: d962b0e1dbb2e05900846866f69a585100b59ac993002dfb1bcef7d89dc8887ecb0e6eaa993be1355971d6d7e09cc4ae67b6881b2609b0a3abe260839aae5436
data/.gitignore CHANGED
@@ -6,3 +6,6 @@ Gemfile.lock
6
6
 
7
7
  # Vim swap files
8
8
  *.swp
9
+
10
+ # rbenv Ruby version file
11
+ .ruby-version
@@ -2,7 +2,7 @@ rvm:
2
2
  - "2.2.5"
3
3
  - "2.3.0"
4
4
  - "2.3.1"
5
- - "2.4.0-preview2"
5
+ # - "2.4.0" TODO: Ruby 2.4 not supported yet. See issue #93.
6
6
  addons:
7
7
  code_climate:
8
8
  repo_token: 397c19af63ca3467cff6fd640b0460b691f687118332b5989f530d28f82adcb8
@@ -18,7 +18,9 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(spec)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "httparty", "~> 0.13"
21
+ spec.required_ruby_version = ['> 2.2', '< 2.4'] # TODO: Gem breaks with Ruby 2.4 or higher. See issue #93.
22
+
23
+ spec.add_dependency "httparty", "~> 0.14.0" # TODO: Temporary lockdown. See issue #103 for more info.
22
24
  spec.add_dependency "dotenv", "~> 2.0"
23
25
  spec.add_dependency "dry-struct", "~> 0.1"
24
26
  spec.add_dependency "dry-types", "~> 0.8"
@@ -47,3 +47,4 @@ require 'fortnox/api/mappers/invoice_row'
47
47
  require 'fortnox/api/mappers/invoice'
48
48
  require 'fortnox/api/mappers/order_row'
49
49
  require 'fortnox/api/mappers/order'
50
+ require 'fortnox/api/mappers/project'
@@ -0,0 +1,15 @@
1
+ require "fortnox/api/mappers/base"
2
+
3
+ module Fortnox
4
+ module API
5
+ module Mapper
6
+ class Project < Fortnox::API::Mapper::Base
7
+ KEY_MAP = {}.freeze
8
+ JSON_ENTITY_WRAPPER = 'Project'.freeze
9
+ JSON_COLLECTION_WRAPPER = 'Projects'.freeze
10
+ end
11
+
12
+ Registry.register( Project.canonical_name_sym, Project )
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,4 @@
1
1
  require "fortnox/api/models/customer"
2
2
  require "fortnox/api/models/invoice"
3
3
  require "fortnox/api/models/order"
4
+ require "fortnox/api/models/project"
@@ -79,7 +79,7 @@ module Fortnox
79
79
  private_class_method
80
80
 
81
81
  # dry-types filter anything that isn't specified as an attribute on the
82
- # class that is being instansiated. This wrapper preserves the meta
82
+ # class that is being instantiated. This wrapper preserves the meta
83
83
  # properties we need to track object state during that initilisation and
84
84
  # sets them on the object after dry-types is done with it.
85
85
  def self.preserve_meta_properties( hash )
File without changes
@@ -0,0 +1,40 @@
1
+ require "fortnox/api/types"
2
+ require "fortnox/api/models/base"
3
+
4
+ module Fortnox
5
+ module API
6
+ module Model
7
+ class Project < Fortnox::API::Model::Base
8
+ UNIQUE_ID = :project_number
9
+ STUB = { description: '' }.freeze
10
+
11
+ # Url Direct URL to the record
12
+ attribute :url, Types::Nullable::String.with( read_only: true )
13
+
14
+ # Comments Comments on project. 512 characters
15
+ attribute :comments, Types::Sized::String[ 512 ]
16
+
17
+ # ContactPerson ContactPerson for project. 50 characters
18
+ attribute :contact_person, Types::Sized::String[ 50 ]
19
+
20
+ # Description Description of the project. 50 characters
21
+ attribute :description, Types::Sized::String[ 50 ]
22
+
23
+ # EndDate End date of the project.
24
+ attribute :end_date, Types::Nullable::Date
25
+
26
+ # ProjectLeader Projectleader. 50 characters
27
+ attribute :project_leader, Types::Sized::String[ 50 ]
28
+
29
+ # ProjectNumber Projectnumber. 20 characters
30
+ attribute :project_number, Types::Sized::String[ 20 ]
31
+
32
+ # Status Status of the project
33
+ attribute :status, Types::ProjectStatusType
34
+
35
+ # StartDate Start date of the project
36
+ attribute :start_date, Types::Nullable::Date
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,4 @@
1
1
  require "fortnox/api/repositories/customer"
2
2
  require "fortnox/api/repositories/invoice"
3
3
  require "fortnox/api/repositories/order"
4
+ require "fortnox/api/repositories/project"
@@ -21,7 +21,7 @@ module Fortnox
21
21
 
22
22
  private
23
23
 
24
- def instansiate( hash )
24
+ def instantiate( hash )
25
25
  hash[ :new ] = false
26
26
  hash[ :unsaved ] = false
27
27
  self.class::MODEL.new( hash )
@@ -7,18 +7,20 @@ module Fortnox
7
7
 
8
8
  def all()
9
9
  response_hash = get( self.class::URI )
10
- instansiate_collection_response( response_hash )
10
+ instantiate_collection_response( response_hash )
11
11
  end
12
12
 
13
13
  def only( filter )
14
14
  response_hash = get( "#{ self.class::URI }?filter=#{ filter }" )
15
- instansiate_collection_response( response_hash )
15
+ instantiate_collection_response( response_hash )
16
16
  end
17
17
 
18
18
  def search( hash )
19
19
  attribute, value = hash.first
20
- response_hash = get( "#{ self.class::URI }?#{ attribute }=#{ value }" )
21
- instansiate_collection_response( response_hash )
20
+ uri_encoded_value = URI.encode(value)
21
+ uri = "#{ self.class::URI }?#{ attribute }=#{ uri_encoded_value }".freeze
22
+ response_hash = get( uri )
23
+ instantiate_collection_response( response_hash )
22
24
  end
23
25
 
24
26
  def find( id_or_hash )
@@ -33,12 +35,13 @@ module Fortnox
33
35
 
34
36
  def find_one_by( id )
35
37
  response_hash = get( "#{ self.class::URI }#{ id }" )
36
- instansiate( @mapper.wrapped_json_hash_to_entity_hash( response_hash ) )
38
+ instantiate( @mapper.wrapped_json_hash_to_entity_hash( response_hash ) )
37
39
  end
38
40
 
39
- # def find_all_by( hash )
40
-
41
- # end
41
+ def find_all_by( hash )
42
+ response_hash = get( "#{ self.class::URI }?#{ to_query( hash ) }" )
43
+ instantiate_collection_response( response_hash )
44
+ end
42
45
 
43
46
  def to_query( hash )
44
47
  hash.collect do |key, value|
@@ -52,10 +55,10 @@ module Fortnox
52
55
 
53
56
  private
54
57
 
55
- def instansiate_collection_response( response_hash )
58
+ def instantiate_collection_response( response_hash )
56
59
  entities_hash = @mapper.wrapped_json_collection_to_entities_hash( response_hash )
57
60
  entities_hash.map do |entity_hash|
58
- instansiate( entity_hash )
61
+ instantiate( entity_hash )
59
62
  end
60
63
  end
61
64
 
@@ -14,7 +14,7 @@ module Fortnox
14
14
  def execute_save( entity )
15
15
  body = get_changes_on( entity ).to_json
16
16
  result = yield body
17
- instansiate_saved( result )
17
+ instantiate_saved( result )
18
18
  end
19
19
 
20
20
  def save_new( entity )
@@ -40,8 +40,8 @@ module Fortnox
40
40
  "#{ self.class::URI }#{ entity.unique_id }"
41
41
  end
42
42
 
43
- def instansiate_saved( wrapped_json_hash )
44
- instansiate(
43
+ def instantiate_saved( wrapped_json_hash )
44
+ instantiate(
45
45
  @mapper.wrapped_json_hash_to_entity_hash(
46
46
  wrapped_json_hash
47
47
  )
@@ -0,0 +1,15 @@
1
+ require "fortnox/api/repositories/base"
2
+ require "fortnox/api/models/project"
3
+ require "fortnox/api/mappers/project"
4
+
5
+ module Fortnox
6
+ module API
7
+ module Repository
8
+ class Project < Fortnox::API::Repository::Base
9
+ MODEL = Fortnox::API::Model::Project
10
+ MAPPER = Fortnox::API::Mapper::Project
11
+ URI = '/projects/'.freeze
12
+ end
13
+ end
14
+ end
15
+ end
@@ -32,6 +32,8 @@ module Fortnox
32
32
 
33
33
  DefaultDeliveryType = Strict::String.constrained( included_in: DefaultDeliveryTypeValues.values ).optional.constructor( EnumConstructors.default )
34
34
 
35
+ ProjectStatusType = Strict::String.constrained( included_in: ProjectStatusTypes.values ).optional.constructor( EnumConstructors.default )
36
+
35
37
  require 'fortnox/api/types/model'
36
38
  require 'fortnox/api/types/default_delivery_types'
37
39
  require 'fortnox/api/types/default_templates'
@@ -70,6 +70,9 @@ module Fortnox
70
70
  DefaultDeliveryTypeValues = Types::Strict::String.enum(
71
71
  'PRINT', 'EMAIL', 'PRINTSERVICE'
72
72
  )
73
+ ProjectStatusTypes = Types::Strict::String.enum(
74
+ 'NOTSTARTED', 'ONGOING', 'COMPLETED'
75
+ )
73
76
  end
74
77
  end
75
78
  end
@@ -1,5 +1,5 @@
1
1
  module Fortnox
2
2
  module API
3
- VERSION = "0.2.0".freeze
3
+ VERSION = "0.3.0".freeze
4
4
  end
5
5
  end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require 'fortnox/api'
3
+ require 'fortnox/api/mappers/project'
4
+ require 'fortnox/api/mappers/examples/mapper'
5
+
6
+ describe Fortnox::API::Mapper::Project do
7
+ it_behaves_like 'mapper', {}, 'Project', 'Projects' do
8
+ let(:mapper){ described_class.new }
9
+ end
10
+ end
@@ -3,7 +3,5 @@ require 'fortnox/api/models/customer'
3
3
  require 'fortnox/api/models/examples/model'
4
4
 
5
5
  describe Fortnox::API::Model::Customer, type: :model do
6
- valid_hash = { name: 'Arthur Dent' }
7
-
8
- it_behaves_like 'a model', valid_hash, :customer_number, '5'
6
+ it_behaves_like 'a model', '1'
9
7
  end
@@ -1,7 +1,7 @@
1
1
  require 'fortnox/api/models/examples/model'
2
2
 
3
- shared_examples_for 'DocumentBase Model' do |row_class, row_attribute, valid_hash, valid_row_hash: {}|
4
- it_behaves_like 'a model', valid_hash, :document_number, 1
3
+ shared_examples_for 'DocumentBase Model' do |row_class, row_attribute, valid_row_hash: {}|
4
+ it_behaves_like 'a model', 1
5
5
 
6
6
  context "when having a(n) #{ row_class }" do
7
7
  it 'returns the correct object' do
@@ -1,13 +1,19 @@
1
- shared_examples_for 'a model' do |valid_hash, unique_id_attribute, value|
1
+ shared_examples_for 'a model' do |unique_id|
2
+ let( :required_attributes ){ described_class::STUB.dup }
3
+ let( :unique_id_attribute ){ described_class::UNIQUE_ID }
4
+
2
5
  it 'can be initialized' do
3
- expect{ described_class.new(valid_hash) }.not_to raise_error
6
+ expect{ described_class.new( required_attributes ) }.not_to raise_error
4
7
  end
5
8
 
6
9
  describe '.unique_id' do
7
- let( :model ){ described_class.new( valid_hash.merge({ unique_id_attribute => value })) }
10
+ subject{ model.unique_id }
11
+
12
+ before{ expect(model.send(unique_id_attribute)).to eq unique_id }
8
13
 
9
- before{ expect(model.send(unique_id_attribute)).to eq value }
14
+ let( :attributes ){ required_attributes.merge(unique_id_attribute => unique_id) }
15
+ let( :model ){ described_class.new( attributes ) }
10
16
 
11
- it{ expect(model.unique_id).to eq value }
17
+ it{ is_expected.to eq unique_id }
12
18
  end
13
19
  end
@@ -3,10 +3,7 @@ require 'fortnox/api/models/invoice'
3
3
  require 'fortnox/api/models/examples/document_base'
4
4
 
5
5
  describe Fortnox::API::Model::Invoice, type: :model do
6
- valid_hash = { customer_number: '12345' }
7
-
8
6
  it_behaves_like 'DocumentBase Model',
9
7
  Fortnox::API::Types::InvoiceRow,
10
- :invoice_rows,
11
- valid_hash
8
+ :invoice_rows
12
9
  end
@@ -3,11 +3,8 @@ require 'fortnox/api/models/order'
3
3
  require 'fortnox/api/models/examples/document_base'
4
4
 
5
5
  describe Fortnox::API::Model::Order, type: :model do
6
- valid_hash = { customer_number: '12345' }
7
-
8
6
  it_behaves_like 'DocumentBase Model',
9
7
  Fortnox::API::Types::OrderRow,
10
8
  :order_rows,
11
- valid_hash,
12
9
  valid_row_hash: { ordered_quantity: 1.1 }
13
10
  end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+ require 'fortnox/api/models/project'
3
+ require 'fortnox/api/models/examples/model'
4
+
5
+ describe Fortnox::API::Model::Project, type: :model do
6
+ it_behaves_like 'a model', '1'
7
+ end
@@ -23,7 +23,14 @@ describe Fortnox::API::Repository::Customer, order: :defined, integration: true
23
23
  # (until 100, which is max by default).
24
24
  include_examples '.all', 100
25
25
 
26
- include_examples '.find', '1'
26
+ include_examples '.find', '1' do
27
+ let( :find_by_hash_failure ){ { city: 'Not Found' } }
28
+ let( :single_param_find_by_hash ){ { find_hash: { city: 'New York' }, matches: 2 } }
29
+
30
+ let( :multi_param_find_by_hash ) do
31
+ { find_hash: { city: 'New York', zipcode: '10001' }, matches: 1 }
32
+ end
33
+ end
27
34
 
28
35
  include_examples '.search', :name, 'Test', 23
29
36
  end
@@ -1,24 +1,79 @@
1
1
  # rubocop:disable RSpec/DescribeClass
2
2
  shared_examples_for '.find' do |searched_entity_id|
3
- describe '.find' do
4
- let( :find_id_1 ) do
3
+ describe '.find by id' do
4
+ let( :returned_object ) do
5
5
  VCR.use_cassette( "#{ vcr_dir }/find_id_1" ){ repository.find( searched_entity_id ) }
6
6
  end
7
7
 
8
- specify 'returns correct class' do
9
- expect( find_id_1.class ).to be described_class::MODEL
8
+ context 'when found' do
9
+ describe 'returned object' do
10
+ subject{ returned_object }
11
+ it{ is_expected.to be_saved }
12
+ it{ is_expected.not_to be_new }
13
+ end
14
+
15
+ describe 'class' do
16
+ subject{ returned_object.class }
17
+ it{ is_expected.to be described_class::MODEL }
18
+ end
19
+
20
+ describe 'unique id' do
21
+ subject{ returned_object.unique_id }
22
+ it{ is_expected.to eq searched_entity_id }
23
+ end
10
24
  end
11
25
 
12
- specify 'returns correct Customer' do
13
- expect( find_id_1.unique_id ).to eq( searched_entity_id )
26
+ context 'when not found' do
27
+ subject{ find_failure }
28
+
29
+ let( :not_found_id ){ '123456789' }
30
+ let( :find_failure ) do
31
+ when_performing do
32
+ VCR.use_cassette( "#{ vcr_dir }/find_failure" ){ repository.find( not_found_id ) }
33
+ end
34
+ end
35
+
36
+ it{ is_expected.to raise_error( Fortnox::API::RemoteServerError, /Kan inte hitta /) }
14
37
  end
38
+ end
39
+
40
+ describe '.find by hash' do
41
+ context 'when found' do
42
+ let( :returned_array ) do
43
+ VCR.use_cassette( "#{ vcr_dir }/#{ cassette }" ){ repository.find( find_hash ) }
44
+ end
15
45
 
16
- specify 'returned Customer is marked as saved' do
17
- expect( find_id_1 ).to be_saved
46
+ context 'with single parameter' do
47
+ let( :cassette ){ 'single_param_find_by_hash' }
48
+ let( :find_hash ){ single_param_find_by_hash[:find_hash] }
49
+
50
+ describe 'returned array size' do
51
+ subject{ returned_array.size }
52
+ it{ is_expected.to eq single_param_find_by_hash[:matches] }
53
+ end
54
+ end
55
+
56
+ context 'with multiple parameters' do
57
+ let( :cassette ){ 'multi_param_find_by_hash' }
58
+ let( :find_hash ){ multi_param_find_by_hash[:find_hash] }
59
+
60
+ describe 'returned array size' do
61
+ subject{ returned_array.size }
62
+ it{ is_expected.to eq multi_param_find_by_hash[:matches] }
63
+ end
64
+ end
18
65
  end
19
66
 
20
- specify 'returned Customer is not markes as new' do
21
- expect( find_id_1 ).not_to be_new
67
+ context 'when not found' do
68
+ subject{ find_failure }
69
+
70
+ let( :find_failure ) do
71
+ VCR.use_cassette( "#{ vcr_dir }/find_by_hash_failure" ) do
72
+ repository.find( find_by_hash_failure )
73
+ end
74
+ end
75
+
76
+ it{ is_expected.to eq [] }
22
77
  end
23
78
  end
24
79
  end