fortnox-api 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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