easybill-rest-client 2.0.2
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.
- checksums.yaml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +152 -0
- data/README.md +8 -0
- data/Rakefile +4 -0
- data/config.json +5 -0
- data/easybill-rest-client.gemspec +29 -0
- data/lib/easybill-rest-client.rb +15 -0
- data/lib/easybill_rest_client/.document_address.rb.swp +0 -0
- data/lib/easybill_rest_client/.document_item.rb.swp +0 -0
- data/lib/easybill_rest_client/.document_payment.rb.swp +0 -0
- data/lib/easybill_rest_client/.document_recurring_options.rb.swp +0 -0
- data/lib/easybill_rest_client/.easybill_error.rb.swp +0 -0
- data/lib/easybill_rest_client/.log_formatter.rb.swp +0 -0
- data/lib/easybill_rest_client/.request_builder.rb.swp +0 -0
- data/lib/easybill_rest_client/.too_many_requests_error.rb.swp +0 -0
- data/lib/easybill_rest_client/api_client.rb +61 -0
- data/lib/easybill_rest_client/client.rb +39 -0
- data/lib/easybill_rest_client/customer.rb +70 -0
- data/lib/easybill_rest_client/customer_api.rb +15 -0
- data/lib/easybill_rest_client/document.rb +53 -0
- data/lib/easybill_rest_client/document_address.rb +19 -0
- data/lib/easybill_rest_client/document_api.rb +31 -0
- data/lib/easybill_rest_client/document_item.rb +34 -0
- data/lib/easybill_rest_client/document_payment.rb +15 -0
- data/lib/easybill_rest_client/document_payment_api.rb +15 -0
- data/lib/easybill_rest_client/document_recurring_options.rb +23 -0
- data/lib/easybill_rest_client/document_service_date.rb +12 -0
- data/lib/easybill_rest_client/generic_api.rb +39 -0
- data/lib/easybill_rest_client/pdf.rb +21 -0
- data/lib/easybill_rest_client/post_box.rb +24 -0
- data/lib/easybill_rest_client/post_box_api.rb +15 -0
- data/lib/easybill_rest_client/request.rb +107 -0
- data/lib/easybill_rest_client/request_logger.rb +24 -0
- data/lib/easybill_rest_client/response.rb +36 -0
- data/lib/easybill_rest_client/retry_on.rb +46 -0
- data/lib/easybill_rest_client/version.rb +5 -0
- data/log/.gitkeep +0 -0
- data/log/test.log +5723 -0
- data/script/.generate.swo +0 -0
- data/spec/.spec_helper.rb.swp +0 -0
- data/spec/fixtures/vcr/EasybillRestClient_CustomerApi/_customers_get/.returns_customers.yml.swo +0 -0
- data/spec/fixtures/vcr/EasybillRestClient_CustomerApi/_customers_get/.returns_customers.yml.swp +0 -0
- data/spec/fixtures/vcr/EasybillRestClient_CustomerApi/a_list_of_customers/gets_all_customers.yml +84 -0
- data/spec/fixtures/vcr/EasybillRestClient_CustomerApi/a_list_of_customers/gets_customers_matching_a_filter.yml +72 -0
- data/spec/fixtures/vcr/EasybillRestClient_CustomerApi/creates_updates_and_deletes_customer.yml +377 -0
- data/spec/fixtures/vcr/EasybillRestClient_Document/_all/returns_documents.yml +44 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentApi/_cancel/cancels_a_document.yml +212 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentApi/_create/creates_a_document.yml +53 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentApi/_delete/deletes_a_document.yml +174 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentApi/_find/returns_a_document.yml +57 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentApi/_find_all/paid_and_unpaid_documents_exist/returns_only_unpaid_documents.yml +795 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentApi/_find_all/returns_documents.yml +58 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentApi/_find_all/returns_documents_by_number.yml +58 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentApi/_finish/marks_a_drafted_document_as_finished.yml +212 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentApi/_get_pdf/returns_a_PDF.yml +3889 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentApi/_send_email/passes_on_additional_params.yml +63 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentApi/_send_email/sends_an_email.yml +63 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentApi/_update/updates_a_document.yml +107 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentPaymentApi/_create/creates_a_document_payment.yml +195 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentPaymentApi/_delete/deletes_a_document_payment.yml +168 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentPaymentApi/_document_payments_get/returns_a_document_s_payments.yml +460 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentPaymentApi/_document_payments_post/creates_a_document_payment.yml +47 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentPaymentApi/_find/returns_a_document_payment.yml +66 -0
- data/spec/fixtures/vcr/EasybillRestClient_DocumentPaymentApi/_find_all/returns_document_payments.yml +66 -0
- data/spec/fixtures/vcr/EasybillRestClient_PostBoxApi/_delete/deletes_a_post_box.yml +174 -0
- data/spec/fixtures/vcr/EasybillRestClient_PostBoxApi/_find/returns_a_post_box.yml +72 -0
- data/spec/fixtures/vcr/EasybillRestClient_PostBoxApi/_find_all/returns_post_boxes.yml +73 -0
- data/spec/fixtures/vcr/EasybillRestClient_PostBoxApi/_post_boxes_get/returns_post_boxes.yml +52 -0
- data/spec/integration/.customer_api_spec.rb.swn +0 -0
- data/spec/integration/.customer_api_spec.rb.swo +0 -0
- data/spec/integration/customer_api_spec.rb +47 -0
- data/spec/integration/easybill_rest_client/document_api_spec.rb +129 -0
- data/spec/integration/easybill_rest_client/document_payment_api_spec.rb +55 -0
- data/spec/integration/easybill_rest_client/post_box_api_spec.rb +31 -0
- data/spec/lib/easybill_rest_client/.api_client_spec.rb.swp +0 -0
- data/spec/lib/easybill_rest_client/easybill_rest_api/.generic_api_spec.rb.swp +0 -0
- data/spec/lib/easybill_rest_client/generic_api_spec.rb +55 -0
- data/spec/lib/easybill_rest_client/pdf_spec.rb +29 -0
- data/spec/lib/easybill_rest_client/request_logger_spec.rb +14 -0
- data/spec/lib/easybill_rest_client/request_spec.rb +32 -0
- data/spec/lib/easybill_rest_client/retry_on_spec.rb +37 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/support/.setup_api_client.rb.swo +0 -0
- data/spec/support/setup_client.rb +12 -0
- data/swagger.json +3964 -0
- metadata +257 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EasybillRestClient
|
|
4
|
+
class GenericApi
|
|
5
|
+
def initialize(api_client)
|
|
6
|
+
@api_client = api_client
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def find(id)
|
|
10
|
+
build(api_client.request(:get, "/#{resource_name}/#{id}"))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def find_all(params = {})
|
|
14
|
+
api_client.request_collection(:get, "/#{resource_name}", params).map { |d| build(d) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def save(entity)
|
|
18
|
+
entity.id ? update(entity) : create(entity)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def create(entity)
|
|
22
|
+
build(api_client.request(:post, "/#{resource_name}", entity.attributes))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def update(entity)
|
|
26
|
+
build(api_client.request(:put, "/#{resource_name}/#{entity.id}", entity.attributes))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def delete(id)
|
|
30
|
+
api_client.request(:delete, "/#{resource_name}/#{id}")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def build(params)
|
|
34
|
+
resource_class.new(params)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
attr_reader :api_client
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EasybillRestClient
|
|
4
|
+
class Pdf
|
|
5
|
+
attr_reader :content
|
|
6
|
+
attr_writer :filename
|
|
7
|
+
|
|
8
|
+
def initialize(filename:, content:)
|
|
9
|
+
@filename = filename
|
|
10
|
+
@content = content
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def empty?
|
|
14
|
+
content.empty?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def filename
|
|
18
|
+
@filename[/\.pdf\Z/i] ? @filename : "#{@filename}.pdf"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EasybillRestClient
|
|
4
|
+
class PostBox
|
|
5
|
+
include Virtus.model
|
|
6
|
+
values do
|
|
7
|
+
attribute :id, Integer
|
|
8
|
+
end
|
|
9
|
+
attribute :cc, String
|
|
10
|
+
attribute :created_at, DateTime
|
|
11
|
+
attribute :date, Date
|
|
12
|
+
attribute :document_id, Integer
|
|
13
|
+
attribute :from, String
|
|
14
|
+
attribute :message, String
|
|
15
|
+
attribute :processed_at, DateTime
|
|
16
|
+
attribute :send_by_self, Boolean
|
|
17
|
+
attribute :send_with_attachment, Boolean
|
|
18
|
+
attribute :status_info, String
|
|
19
|
+
attribute :status, String
|
|
20
|
+
attribute :subject, String
|
|
21
|
+
attribute :to, String
|
|
22
|
+
attribute :type, String
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'easybill_rest_client/request_logger'
|
|
4
|
+
require 'easybill_rest_client/retry_on'
|
|
5
|
+
|
|
6
|
+
module EasybillRestClient
|
|
7
|
+
class Request
|
|
8
|
+
BASE_URL = 'https://api.easybill.de/rest/v1'
|
|
9
|
+
USERNAME = 'rest-api@easybill.de'
|
|
10
|
+
SUPPORTED_METHODS = %i[get post put delete].freeze
|
|
11
|
+
OPEN_TIMEOUT = 30
|
|
12
|
+
|
|
13
|
+
def initialize(opts = {})
|
|
14
|
+
unless SUPPORTED_METHODS.include?(opts.fetch(:method).to_sym)
|
|
15
|
+
raise ArgumentError, "Unsupported HTTP method '#{method}'"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@api_key = opts.fetch(:api_key)
|
|
19
|
+
@method = opts.fetch(:method)
|
|
20
|
+
@endpoint = opts.fetch(:endpoint)
|
|
21
|
+
@params = opts.fetch(:params)
|
|
22
|
+
@logger = opts.fetch(:logger)
|
|
23
|
+
@tries = opts.fetch(:tries)
|
|
24
|
+
@retry_cool_off_time = opts.fetch(:retry_cool_off_time)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def run
|
|
28
|
+
request_logger.info("#{method.to_s.upcase} #{endpoint} #{request_details}")
|
|
29
|
+
retry_on(TooManyRequests) do
|
|
30
|
+
retry_on(Net::OpenTimeout) do
|
|
31
|
+
response = perform_request
|
|
32
|
+
raise TooManyRequests if response.is_a?(Net::HTTPTooManyRequests)
|
|
33
|
+
response
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def request_details
|
|
39
|
+
body_allowed? ? request.body : uri.query
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
attr_reader :api_key, :method, :endpoint, :params, :logger, :tries, :retry_cool_off_time
|
|
45
|
+
|
|
46
|
+
def perform_request
|
|
47
|
+
http_opts = { use_ssl: uri.scheme == 'https', open_timeout: OPEN_TIMEOUT }
|
|
48
|
+
Net::HTTP.start(uri.host, uri.port, http_opts) do |http|
|
|
49
|
+
http.request(request)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def uri
|
|
54
|
+
return @uri if @uri
|
|
55
|
+
@uri = URI.parse(BASE_URL)
|
|
56
|
+
@uri.path << endpoint
|
|
57
|
+
@uri.query = URI.encode_www_form(query_params) unless body_allowed?
|
|
58
|
+
@uri
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def query_params
|
|
62
|
+
params.map { |key, value| [key, translate_query_value(value)] }.to_h
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def translate_query_value(value)
|
|
66
|
+
case value
|
|
67
|
+
when Array
|
|
68
|
+
value.join(',')
|
|
69
|
+
when nil
|
|
70
|
+
'null'
|
|
71
|
+
else
|
|
72
|
+
value
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def request
|
|
77
|
+
return @request if @request
|
|
78
|
+
@request = request_class.new(uri)
|
|
79
|
+
@request.body = params.reject { |_k, v| v.nil? }.to_json if body_allowed?
|
|
80
|
+
@request.basic_auth(USERNAME, api_key)
|
|
81
|
+
@request
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def request_class
|
|
85
|
+
Net::HTTP.const_get(method.to_s.capitalize)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def body_allowed?
|
|
89
|
+
request_class::REQUEST_HAS_BODY
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def retry_on(exception, &block)
|
|
93
|
+
@retry_on ||= RetryOn.new(request_logger, tries, retry_cool_off_time)
|
|
94
|
+
@retry_on.retry_on(exception, &block)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def request_logger
|
|
98
|
+
@request_logger ||= RequestLogger.new(logger: logger, request_id: request_id)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def request_id
|
|
102
|
+
@request_id ||= SecureRandom.hex(3)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
class TooManyRequests < StandardError; end
|
|
107
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EasybillRestClient
|
|
4
|
+
class RequestLogger
|
|
5
|
+
def initialize(logger:, request_id:)
|
|
6
|
+
@logger = logger
|
|
7
|
+
@request_id = request_id
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
%i[info warn error].each do |method|
|
|
11
|
+
define_method(method) do |msg|
|
|
12
|
+
logger.public_send(method, format_message(msg))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
attr_reader :request_id, :logger
|
|
19
|
+
|
|
20
|
+
def format_message(msg)
|
|
21
|
+
"[easybill-rest-client] RequestId=#{request_id} #{msg}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EasybillRestClient
|
|
4
|
+
class Response
|
|
5
|
+
def initialize(response)
|
|
6
|
+
@response = response
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def body
|
|
10
|
+
body = extract_response_body(response)
|
|
11
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
12
|
+
raise ApiError, body.inspect
|
|
13
|
+
end
|
|
14
|
+
body && !body.empty? ? body : nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
attr_reader :response
|
|
20
|
+
|
|
21
|
+
def extract_response_body(response)
|
|
22
|
+
return unless response.class.body_permitted?
|
|
23
|
+
case response.content_type
|
|
24
|
+
when 'application/json'
|
|
25
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
26
|
+
when 'application/pdf'
|
|
27
|
+
/\Wfilename="(?<filename>.*?)"/ =~ response.fetch('content-disposition')
|
|
28
|
+
Pdf.new(filename: filename, content: response.body)
|
|
29
|
+
else
|
|
30
|
+
response.body
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class ApiError < RuntimeError; end
|
|
36
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'retryable'
|
|
4
|
+
|
|
5
|
+
module EasybillRestClient
|
|
6
|
+
class RetryOn
|
|
7
|
+
def initialize(logger, tries, retry_cool_off_time)
|
|
8
|
+
@logger = logger
|
|
9
|
+
@tries = tries
|
|
10
|
+
@retry_cool_off_time = retry_cool_off_time
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def retry_on(klass, &block)
|
|
14
|
+
Retryable.retryable(retryable_opts(klass), &block)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
attr_reader :logger, :tries, :retry_cool_off_time
|
|
20
|
+
|
|
21
|
+
def retryable_opts(klass)
|
|
22
|
+
{
|
|
23
|
+
EasybillRestClient::TooManyRequests => {
|
|
24
|
+
tries: tries,
|
|
25
|
+
sleep: retry_cool_off_time,
|
|
26
|
+
on: klass,
|
|
27
|
+
exception_cb: method(:log_too_many_requests)
|
|
28
|
+
},
|
|
29
|
+
Net::OpenTimeout => {
|
|
30
|
+
tries: tries,
|
|
31
|
+
sleep: 0,
|
|
32
|
+
on: klass,
|
|
33
|
+
exception_cb: method(:log_open_timeout)
|
|
34
|
+
}
|
|
35
|
+
}.fetch(klass)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def log_open_timeout(_exception)
|
|
39
|
+
logger.warn("Unable to open connection after #{Request::OPEN_TIMEOUT}s, retrying...")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def log_too_many_requests(_exception)
|
|
43
|
+
logger.warn('Too many requests!')
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/log/.gitkeep
ADDED
|
File without changes
|