billomat 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,14 +2,12 @@
2
2
 
3
3
  module Billomat
4
4
  module Actions
5
- ##
6
- # Completes an invoice by calling the /complete path on a resource
5
+ # Completes an invoice by calling the /complete path on a resource.
7
6
  class Complete
8
- ##
9
- # Returns a Complete object
7
+ # Returns a Complete object.
10
8
  #
11
- # @param [String] invoice_id The ID of the invoice
12
- # @param [Hash] opts The options for this request
9
+ # @param invoice_id [String] the ID of the invoice
10
+ # @param opts [Hash] the options for this request
13
11
  # @return [Billomat::Actions::Complete]
14
12
  #
15
13
  # @example
@@ -19,8 +17,7 @@ module Billomat
19
17
  @opts = opts
20
18
  end
21
19
 
22
- ##
23
- # Calls the gateway
20
+ # Calls the gateway.
24
21
  #
25
22
  # @return [TrueClass]
26
23
  def call
@@ -29,15 +26,14 @@ module Billomat
29
26
  true
30
27
  end
31
28
 
32
- ##
33
- # The given options have to be wrapped
29
+ # The given options have to be wrapped.
34
30
  #
35
- # @return [Hash] The payload for the complete request
31
+ # @return [Hash] the payload for the complete request
36
32
  def wrapped_data
37
33
  { complete: @opts }
38
34
  end
39
35
 
40
- # @return [String] The complete path with the invoice_id
36
+ # @return [String] the complete path with the invoice_id
41
37
  def path
42
38
  "/invoices/#{@invoice_id}/complete"
43
39
  end
@@ -2,25 +2,22 @@
2
2
 
3
3
  module Billomat
4
4
  module Actions
5
- ##
6
5
  # This actions sends an invoice email.
7
6
  # Recipients must be passed like this:
8
- # { recipients: { to: 'bob@example.org' } }
7
+ # { recipients: { to: 'bob@example.org' } }
9
8
  #
10
9
  # @example
11
10
  # Billomat::Actions::Email.new('1235', { recipiens: { to: 'a@b.org' } })
12
11
  class Email
13
- # @param [String] invoice_id The invoice ID
14
- # @param [Hash] opts The options for this action
15
- #
12
+ # @param invoice_id [String] the invoice ID
13
+ # @param opts [Hash] the options for this action
16
14
  # @return [Billomat::Actions::Email]
17
15
  def initialize(invoice_id, opts = {})
18
16
  @invoice_id = invoice_id
19
17
  @opts = opts
20
18
  end
21
19
 
22
- ##
23
- # Calls the gateway
20
+ # Calls the gateway.
24
21
  #
25
22
  # @return [TrueClass]
26
23
  def call
@@ -29,15 +26,14 @@ module Billomat
29
26
  true
30
27
  end
31
28
 
32
- ##
33
29
  # Wraps the options
34
30
  #
35
- # @return [Hash] The wrapped email options
31
+ # @return [Hash] the wrapped email options
36
32
  def wrapped_data
37
33
  { email: @opts }
38
34
  end
39
35
 
40
- # @return [String] The path for the email action
36
+ # @return [String] the path for the email action
41
37
  def path
42
38
  "/invoices/#{@invoice_id}/email"
43
39
  end
@@ -2,21 +2,18 @@
2
2
 
3
3
  module Billomat
4
4
  module Actions
5
- ##
6
5
  # This class allows to download the invoice as a pdf.
7
6
  # The PDF comes in a base64 encoded string in the response body.
8
7
  class Pdf
9
- # @param [String] invoice_id The invoice ID
10
- # @param [Hash] opts The options for this action
11
- #
8
+ # @param invoice_id [String] the invoice ID
9
+ # @param opts [Hash] the options for this action
12
10
  # @return [Billomat::Actions::Pdf]
13
11
  def initialize(invoice_id, opts = {})
14
12
  @invoice_id = invoice_id
15
13
  @opts = opts
16
14
  end
17
15
 
18
- ##
19
- # Calls the gateway
16
+ # Calls the gateway.
20
17
  #
21
18
  # @return [TrueClass]
22
19
  def call
@@ -24,10 +21,7 @@ module Billomat
24
21
  resp['pdf']
25
22
  end
26
23
 
27
- ##
28
- # Wraps the options
29
- #
30
- # @return [Hash] The wrapped email options
24
+ # @return [String] the path for the PDF action
31
25
  def path
32
26
  "/invoices/#{@invoice_id}/pdf"
33
27
  end
@@ -2,21 +2,18 @@
2
2
 
3
3
  module Billomat
4
4
  module Actions
5
- ##
6
5
  # This actions uncancels an canceld invoice.
7
6
  #
8
7
  # @example
9
8
  # Billomat::Actions::Uncancel.new('1235')
10
9
  class Uncancel
11
- # @param [String] invoice_id The invoice ID
12
- #
10
+ # @param invoice_id [String] the invoice ID
13
11
  # @return [Billomat::Actions::Uncancel]
14
12
  def initialize(invoice_id)
15
13
  @invoice_id = invoice_id
16
14
  end
17
15
 
18
- ##
19
- # Calls the gateway
16
+ # Calls the gateway.
20
17
  #
21
18
  # @return [TrueClass]
22
19
  def call
@@ -25,7 +22,7 @@ module Billomat
25
22
  true
26
23
  end
27
24
 
28
- # @return [String] The path for the uncancel action
25
+ # @return [String] the path for the uncancel action
29
26
  def path
30
27
  "/invoices/#{@invoice_id}/uncancel"
31
28
  end
@@ -7,7 +7,6 @@ require 'billomat/actions/cancel'
7
7
  require 'billomat/actions/uncancel'
8
8
 
9
9
  module Billomat
10
- ##
11
10
  # Actions are API calls that do not directly represent a resource.
12
11
  # They are mostly non-RESTful actions that are called on a resources.
13
12
  module Actions; end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Billomat
4
+ # The +billomat+ gem configuration.
4
5
  class Configuration
5
6
  attr_accessor :api_key, :subdomain, :timeout, :app_id, :app_secret
6
7
  end
@@ -4,16 +4,32 @@ require 'rest-client'
4
4
  require 'json'
5
5
 
6
6
  module Billomat
7
- ##
8
- # Raised if something goes wrong during an API call
9
- class GatewayError < StandardError; end
7
+ # Raised if something goes wrong during an API call.
8
+ class GatewayError < StandardError
9
+ attr_reader :original_exception
10
10
 
11
- ##
12
- # This class can be used by the gem to communicate with the API
11
+ # Create a new GatewayError from a RestClient exception.
12
+ #
13
+ # @param original_exception [RestClient::Exception] the original exception
14
+ def initialize(original_exception)
15
+ # Extract the error from the response if it is present. If no error
16
+ # could be extracted, use the whole body
17
+ body = original_exception.response&.body
18
+ response_error = JSON.parse(body).dig('errors', 'error') || body if body
19
+
20
+ # Use the default message and append our detailed error
21
+ message = original_exception.default_message
22
+ message += " ('#{response_error}')" if response_error
23
+
24
+ super(message)
25
+ @original_exception = original_exception
26
+ end
27
+ end
28
+
29
+ # This class can be used by the gem to communicate with the API.
13
30
  class Gateway
14
31
  attr_reader :method, :path, :body
15
32
 
16
- ##
17
33
  # Creates a new Gateway
18
34
  #
19
35
  # @param [Symbol] method The HTTP verb
@@ -29,29 +45,33 @@ module Billomat
29
45
  @body = body
30
46
  end
31
47
 
32
- ##
33
- # Executes the API call
34
- # @return [Hash] The response body
48
+ # Executes the API call and parse the response.
49
+ #
50
+ # @return [Hash] the response body
35
51
  def run
36
52
  resp = response
37
53
 
38
- raise GatewayError, resp.body if resp.code > 299
39
54
  return nil if resp.body.empty?
40
55
 
41
56
  JSON.parse(resp.body)
57
+ rescue RestClient::Exception => e
58
+ raise GatewayError, e
42
59
  end
43
60
 
61
+ # Executes the API call and return the response.
62
+ #
63
+ # @return [RestClient::Response] the API response
44
64
  def response
45
65
  RestClient::Request.execute(
46
- method: method,
47
- url: url,
48
- timeout: timeout,
49
- headers: headers,
50
- payload: body.to_json
66
+ method: method,
67
+ url: url,
68
+ timeout: timeout,
69
+ headers: headers,
70
+ payload: body.to_json
51
71
  )
52
72
  end
53
73
 
54
- # @return [String] The complete URL for the request
74
+ # @return [String] the complete URL for the request
55
75
  def url
56
76
  "https://#{config.subdomain}.billomat.net/api#{path}"
57
77
  end
@@ -61,18 +81,18 @@ module Billomat
61
81
  config.timeout || 5
62
82
  end
63
83
 
64
- # @return [Hash] The headers for the request.
84
+ # @return [Hash] the headers for the request
65
85
  def headers
66
86
  {
67
- 'Accept' => 'application/json',
68
- 'Content-Type' => 'application/json',
87
+ 'Accept' => 'application/json',
88
+ 'Content-Type' => 'application/json',
69
89
  'X-BillomatApiKey' => config.api_key,
70
- 'X-AppId' => config.app_id,
71
- 'X-AppSecret' => config.app_secret
90
+ 'X-AppId' => config.app_id,
91
+ 'X-AppSecret' => config.app_secret
72
92
  }.reject { |_, val| val.nil? }
73
93
  end
74
94
 
75
- # @return [Billomat::Configuration] The global gem configuration
95
+ # @return [Billomat::Configuration] the global gem configuration
76
96
  #
77
97
  # :reek:UtilityFunction because it's a shorthand
78
98
  def config
@@ -4,50 +4,49 @@ require 'ostruct'
4
4
 
5
5
  module Billomat
6
6
  module Models
7
- ##
8
7
  # This class is the base for all other models (resources).
9
8
  # It handles the communication with the gateway to talk to the API.
10
9
  class Base
11
10
  attr_accessor :data
12
11
 
13
- ##
14
- # Tries to find the resource for the given id
12
+ # Tries to find the resource for the given id.
15
13
  #
16
- # @param [String] id The resource id
17
- # @return [Billomat::Models::Base, nil] The found resource or nil
14
+ # @param id [String] the resource id
15
+ # @return [Billomat::Models::Base, nil] the found resource or nil
18
16
  def self.find(id)
19
17
  return nil if id.nil?
18
+
20
19
  resp = Billomat::Gateway.new(:get, "#{base_path}/#{id}").run
21
20
  new(resp[resource_name])
22
21
  end
23
22
 
24
- ##
25
- # Allows to query for a record
23
+ # Allows to query for a record.
26
24
  #
27
- # @param [Hash] hash The query parameters
28
- # @return [Array<Billomat::Models::Base>] The found records
25
+ # @param hash [Hash] the query parameters
26
+ # @return [Array<Billomat::Models::Base>] the found records
29
27
  def self.where(hash = {})
30
28
  Billomat::Search.new(self, hash).run
31
29
  end
32
30
 
33
31
  ##
34
- # Initializes a new model
32
+ # Initializes a new model.
35
33
  #
36
- # @param [Hash] data The attributes of the object
37
- # @return [Billomat::Models::Base] The record as an object
34
+ # @param data [Hash] the attributes of the object
35
+ # @return [Billomat::Models::Base] the record as an object
38
36
  def initialize(data = {})
39
37
  @data = OpenStruct.new(data)
40
38
  end
41
39
 
42
- ##
43
40
  # Persists the current object in the API.
44
41
  # When record is new it calls create, otherwise it saves the object.
45
42
  #
46
43
  # @return [TrueClass]
47
44
  def save
48
45
  return create if id.nil?
46
+
49
47
  update
50
48
  end
49
+ alias save! save
51
50
 
52
51
  # @return [TrueClass]
53
52
  def create
@@ -59,6 +58,7 @@ module Billomat
59
58
 
60
59
  true
61
60
  end
61
+ alias create! create
62
62
 
63
63
  # @return [TrueClass]
64
64
  def update
@@ -68,6 +68,7 @@ module Billomat
68
68
 
69
69
  true
70
70
  end
71
+ alias update! update
71
72
 
72
73
  # @return [TrueClass]
73
74
  def delete
@@ -76,52 +77,50 @@ module Billomat
76
77
 
77
78
  true
78
79
  end
80
+ alias delete! delete
79
81
 
80
- # @return [String, nil] The object's ID
82
+ # @return [String, nil] the object's ID
81
83
  def id
82
84
  @data['id'] || nil
83
85
  end
84
86
 
85
- ##
86
- # Wraps the data so the API accepts the request
87
+ # Wraps the data so the API accepts the request.
87
88
  #
88
89
  # @example
89
90
  # some_invoice.wrapped_data
90
91
  # #=> { "invoice" => { "id" => "12345" } }
91
92
  #
92
- # @return [Hash] The wrapped data
93
+ # @return [Hash] the wrapped data
93
94
  def wrapped_data
94
95
  { self.class.resource_name => @data.to_h }
95
96
  end
96
97
 
97
- ##
98
- # Returns the object with the right JSON structure
98
+ # Returns the object with the right JSON structure.
99
99
  #
100
- # @return [Hash] The objects data
100
+ # @return [Hash] the objects data
101
101
  def as_json(_options = nil)
102
102
  @data.to_h
103
103
  end
104
104
 
105
- ##
106
- # All values in the @data hash can be accessed like a 'normal' method
105
+ # All values in the @data hash can be accessed like a 'normal' method.
107
106
  #
108
107
  # @example
109
108
  # invoice = Billomat::Models::Invoice.new(invoice_number: '123')
110
109
  # invoice.invoice_number
111
110
  # #=> '123'
112
111
  def method_missing(method, *args, &block)
113
- return @data[method] if @data.to_h.keys.include?(method)
112
+ return @data[method] if @data.to_h.key?(method)
113
+
114
114
  super
115
115
  end
116
116
 
117
- ##
118
- # Necessary for method_missing
117
+ # Necessary for method_missing.
119
118
  #
120
119
  # @param [Symbol] method The method name
121
120
  # @param [TrueClass, FalseClass] include_privat
122
121
  # @return [TrueClass, FalseClass]
123
122
  def respond_to_missing?(method, include_privat = false)
124
- @data.to_h.keys.include?(method.to_s) || super
123
+ @data.to_h.key?(method.to_s) || super
125
124
  end
126
125
  end
127
126
  end
@@ -2,15 +2,14 @@
2
2
 
3
3
  module Billomat
4
4
  module Models
5
- ##
6
- # Representation of the client resource
5
+ # Representation of the client resource.
7
6
  class Client < Base
8
- # @return [String] The resource's base path
7
+ # @return [String] the resource's base path
9
8
  def self.base_path
10
9
  '/clients'
11
10
  end
12
11
 
13
- # @return [String] The resource's name
12
+ # @return [String] the resource's name
14
13
  def self.resource_name
15
14
  'client'
16
15
  end
@@ -2,15 +2,14 @@
2
2
 
3
3
  module Billomat
4
4
  module Models
5
- ##
6
- # Representation of the client resource
5
+ # Representation of the client resource.
7
6
  class Contact < Base
8
- # @return [String] The resource's base path
7
+ # @return [String] the resource's base path
9
8
  def self.base_path
10
9
  '/contacts'
11
10
  end
12
11
 
13
- # @return [String] The resource's name
12
+ # @return [String] the resource's name
14
13
  def self.resource_name
15
14
  'contact'
16
15
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Billomat
4
+ module Models
5
+ # Representation of the credit note resource
6
+ class CreditNote < Base
7
+ # @return [String] the resource's base path
8
+ def self.base_path
9
+ '/credit-notes'
10
+ end
11
+
12
+ # @return [String] the resource's name
13
+ def self.resource_name
14
+ 'credit-note'
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Billomat
4
+ module Models
5
+ # Representation of the credit note item resource
6
+ class CreditNoteItem < Base
7
+ # @return [String] the resource's base path
8
+ def self.base_path
9
+ '/credit-note-items'
10
+ end
11
+
12
+ # @return [String] the resource's name
13
+ def self.resource_name
14
+ 'credit-note-item'
15
+ end
16
+ end
17
+ end
18
+ end
@@ -2,33 +2,29 @@
2
2
 
3
3
  module Billomat
4
4
  module Models
5
- ##
6
5
  # Representation of the invoice resource
7
6
  class Invoice < Base
8
- # @return [String] The resource's base path
7
+ # @return [String] the resource's base path
9
8
  def self.base_path
10
9
  '/invoices'
11
10
  end
12
11
 
13
- # @return [String] The resource's name
12
+ # @return [String] the resource's name
14
13
  def self.resource_name
15
14
  'invoice'
16
15
  end
17
16
 
18
- ##
19
- # Completes the invoice by calling the Complete action
17
+ # Completes the invoice by calling the Complete action.
20
18
  def complete!
21
19
  Billomat::Actions::Complete.new(id).call
22
20
  end
23
21
 
24
- ##
25
- # Cancels the invoice by calling the Cancel action
22
+ # Cancels the invoice by calling the Cancel action.
26
23
  def cancel!
27
24
  Billomat::Actions::Cancel.new(id).call
28
25
  end
29
26
 
30
- ##
31
- # Sends the invoice as an email to the given recipient
27
+ # Sends the invoice as an email to the given recipient.
32
28
  #
33
29
  # @param [String] recipient The email address of the recipient
34
30
  def send_email(recipient)
@@ -37,8 +33,7 @@ module Billomat
37
33
  Billomat::Actions::Email.new(id, email_params).call
38
34
  end
39
35
 
40
- ##
41
- # Allows to download the invoice as an PDF
36
+ # Allows to download the invoice as an PDF.
42
37
  def to_pdf
43
38
  Billomat::Actions::Pdf.new(id).call
44
39
  end
@@ -3,14 +3,14 @@
3
3
  module Billomat
4
4
  module Models
5
5
  ##
6
- # Representation of the invoice item resource
6
+ # Representation of the invoice item resource.
7
7
  class InvoiceItem < Base
8
- # @return [String] The resource's base path
8
+ # @return [String] the resource's base path
9
9
  def self.base_path
10
10
  '/invoice-items'
11
11
  end
12
12
 
13
- # @return [String] The resource's name
13
+ # @return [String] the resource's name
14
14
  def self.resource_name
15
15
  'invoice-item'
16
16
  end
@@ -2,15 +2,14 @@
2
2
 
3
3
  module Billomat
4
4
  module Models
5
- ##
6
- # Representation of the invoice payment resource
5
+ # Representation of the invoice payment resource.
7
6
  class InvoicePayment < Base
8
- # @return [String] The resource's base path
7
+ # @return [String] the resource's base path
9
8
  def self.base_path
10
9
  '/invoice-payments'
11
10
  end
12
11
 
13
- # @return [String] The resource's name
12
+ # @return [String] the resource's name
14
13
  def self.resource_name
15
14
  'invoice-payment'
16
15
  end
@@ -2,15 +2,14 @@
2
2
 
3
3
  module Billomat
4
4
  module Models
5
- ##
6
- # Representation of the tag resource
5
+ # Representation of the tag resource.
7
6
  class Tag < Base
8
- # @return [String] The resource's base path
7
+ # @return [String] the resource's base path
9
8
  def self.base_path
10
9
  '/tags'
11
10
  end
12
11
 
13
- # @return [String] The resource's name
12
+ # @return [String] the resource's name
14
13
  def self.resource_name
15
14
  'tag'
16
15
  end
@@ -2,15 +2,14 @@
2
2
 
3
3
  module Billomat
4
4
  module Models
5
- ##
6
- # Representation of the template resource
5
+ # Representation of the template resource.
7
6
  class Template < Base
8
- # @return [String] The resource's base path
7
+ # @return [String] the resource's base path
9
8
  def self.base_path
10
9
  '/templates'
11
10
  end
12
11
 
13
- # @return [String] The resource's name
12
+ # @return [String] the resource's name
14
13
  def self.resource_name
15
14
  'template'
16
15
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require 'billomat/models/base'
4
4
  require 'billomat/models/client'
5
+ require 'billomat/models/credit_note'
6
+ require 'billomat/models/credit_note_item'
5
7
  require 'billomat/models/invoice'
6
8
  require 'billomat/models/invoice_item'
7
9
  require 'billomat/models/invoice_payment'
@@ -10,7 +12,6 @@ require 'billomat/models/tag'
10
12
  require 'billomat/models/template'
11
13
 
12
14
  module Billomat
13
- ##
14
- # Models represent a resource in the API, e.g. invoices, clients
15
+ # Models represent a resource in the API, e.g. invoices, clients.
15
16
  module Models; end
16
17
  end