octobat 2.0.17 → 2.0.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/History.txt +6 -0
- data/VERSION +1 -1
- data/lib/octobat.rb +9 -1
- data/lib/octobat/file_link.rb +41 -0
- data/lib/octobat/file_upload.rb +59 -0
- data/lib/octobat/multipart_encoder.rb +131 -0
- data/lib/octobat/reporting/report_run.rb +12 -0
- data/lib/octobat/reporting/report_type.rb +12 -0
- data/lib/octobat/util.rb +5 -1
- data/lib/octobat/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 346d7f40d07e9514915f5752d92de705bd6d3875
|
4
|
+
data.tar.gz: c0e09511acf91497cbe1d3dac2e30073e5480cce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ad71a3790764557809ccc97b339d1492efd6ee5524e24bb88ee63e478a09db741ce70e9eaa1fb4918e33df6f78b06cfc51b3b446a0fe8e1b59ff9cdc55feaae
|
7
|
+
data.tar.gz: 533fde0cda76b7581715078d0d6c8eefb3a0aa9cbeaa0b35ffb209bd1370b3e87a969c62c4edbff3a27f24f3ed7d0d062d1ecf2623970ca46329fb2457a2aa60
|
data/Gemfile.lock
CHANGED
data/History.txt
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
=== 2.0.18 2020-05-25
|
2
|
+
* 3 major enhancements:
|
3
|
+
* Add support for multipart encoder (file upload)
|
4
|
+
* Add support for File API endpoints
|
5
|
+
* Add support for Reporting API endpoints
|
6
|
+
|
1
7
|
=== 2.0.17 2020-04-27
|
2
8
|
* 1 minor enhancement:
|
3
9
|
* Update Order API endpoints
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.0.
|
1
|
+
2.0.18
|
data/lib/octobat.rb
CHANGED
@@ -17,6 +17,7 @@ require 'octobat/api_operations/delete'
|
|
17
17
|
require 'octobat/api_operations/list'
|
18
18
|
|
19
19
|
# Resources
|
20
|
+
require 'octobat/multipart_encoder'
|
20
21
|
require 'octobat/util'
|
21
22
|
require 'octobat/octobat_object'
|
22
23
|
require 'octobat/api_resource'
|
@@ -51,6 +52,12 @@ require 'octobat/exports_setting'
|
|
51
52
|
require 'octobat/emails_setting'
|
52
53
|
require 'octobat/tax_id'
|
53
54
|
|
55
|
+
require 'octobat/file_upload'
|
56
|
+
require 'octobat/file_link'
|
57
|
+
require 'octobat/reporting/report_type'
|
58
|
+
require 'octobat/reporting/report_run'
|
59
|
+
|
60
|
+
|
54
61
|
# Errors
|
55
62
|
require 'octobat/errors/octobat_error'
|
56
63
|
require 'octobat/errors/octobat_lib_error'
|
@@ -63,6 +70,7 @@ module Octobat
|
|
63
70
|
#DEFAULT_CA_BUNDLE_PATH = File.dirname(__FILE__) + '/data/ca-certificates.crt'
|
64
71
|
@api_base = 'https://apiv2.octobat.com'
|
65
72
|
#@api_base = 'http://api.octobat.local:3052'
|
73
|
+
@uploads_base = "https://files.octobat.com"
|
66
74
|
|
67
75
|
@max_network_retries = 0
|
68
76
|
@max_network_retry_delay = 2
|
@@ -75,7 +83,7 @@ module Octobat
|
|
75
83
|
|
76
84
|
|
77
85
|
class << self
|
78
|
-
attr_accessor :api_key, :api_base, :verify_ssl_certs, :api_version
|
86
|
+
attr_accessor :api_key, :api_base, :verify_ssl_certs, :api_version, :uploads_base
|
79
87
|
attr_reader :max_network_retry_delay, :initial_network_retry_delay
|
80
88
|
end
|
81
89
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Octobat
|
2
|
+
class FileLink < APIResource
|
3
|
+
include Octobat::APIOperations::Create
|
4
|
+
extend Octobat::APIOperations::List
|
5
|
+
|
6
|
+
def self.create(params = {}, opts = {})
|
7
|
+
api_key, headers = Util.parse_opts(opts)
|
8
|
+
response, api_key = Octobat.request(:post, self.url, api_key, params, headers, Octobat.uploads_base)
|
9
|
+
Util.convert_to_octobat_object(response, api_key)
|
10
|
+
end
|
11
|
+
|
12
|
+
def refresh
|
13
|
+
response, api_key = Octobat.request(:get, url, @api_key, @retrieve_options, @headers, Octobat.uploads_base)
|
14
|
+
refresh_from(response, api_key)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.list(filters={}, opts={})
|
18
|
+
set_parent_resource(filters)
|
19
|
+
api_key, headers = Util.parse_opts(opts)
|
20
|
+
|
21
|
+
api_key ||= @api_key
|
22
|
+
|
23
|
+
f = filters.select{|request_filter| !@parent_resource.has_key?(request_filter)}
|
24
|
+
|
25
|
+
response, api_key = Octobat.request(:get, url, api_key, f, headers, Octobat.uploads_base)
|
26
|
+
obj = ListObject.construct_from(response, api_key)
|
27
|
+
|
28
|
+
obj.filters = filters.dup
|
29
|
+
obj.cursors[:ending_before] = obj.filters.delete(:ending_before)
|
30
|
+
obj.cursors[:starting_after] = obj.filters.delete(:starting_after)
|
31
|
+
|
32
|
+
obj.filters.delete(:expand)
|
33
|
+
obj.parent_resource = @parent_resource
|
34
|
+
|
35
|
+
obj
|
36
|
+
end
|
37
|
+
|
38
|
+
self.singleton_class.send(:alias_method, :all, :list)
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Octobat
|
2
|
+
class FileUpload < APIResource
|
3
|
+
include Octobat::APIOperations::Create
|
4
|
+
extend Octobat::APIOperations::List
|
5
|
+
|
6
|
+
def self.url
|
7
|
+
"/files"
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def self.create(params = {}, opts = {})
|
12
|
+
if params[:attachment] && !params[:attachment].is_a?(String)
|
13
|
+
unless params[:attachment].respond_to?(:read)
|
14
|
+
raise ArgumentError, "attachment must respond to `#read`"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
api_key, headers = Util.parse_opts(opts)
|
19
|
+
headers = headers.merge(content_type: MultipartEncoder::MULTIPART_FORM_DATA)
|
20
|
+
|
21
|
+
response, api_key = Octobat.request(:post, self.url, api_key, params, headers, Octobat.uploads_base)
|
22
|
+
Util.convert_to_octobat_object(response, api_key)
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def refresh
|
27
|
+
response, api_key = Octobat.request(:get, url, @api_key, @retrieve_options, @headers, Octobat.uploads_base)
|
28
|
+
refresh_from(response, api_key)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def self.list(filters={}, opts={})
|
33
|
+
set_parent_resource(filters)
|
34
|
+
api_key, headers = Util.parse_opts(opts)
|
35
|
+
|
36
|
+
api_key ||= @api_key
|
37
|
+
|
38
|
+
f = filters.select{|request_filter| !@parent_resource.has_key?(request_filter)}
|
39
|
+
|
40
|
+
response, api_key = Octobat.request(:get, url, api_key, f, headers, Octobat.uploads_base)
|
41
|
+
obj = ListObject.construct_from(response, api_key)
|
42
|
+
|
43
|
+
obj.filters = filters.dup
|
44
|
+
obj.cursors[:ending_before] = obj.filters.delete(:ending_before)
|
45
|
+
obj.cursors[:starting_after] = obj.filters.delete(:starting_after)
|
46
|
+
|
47
|
+
obj.filters.delete(:expand)
|
48
|
+
obj.parent_resource = @parent_resource
|
49
|
+
|
50
|
+
obj
|
51
|
+
end
|
52
|
+
|
53
|
+
self.singleton_class.send(:alias_method, :all, :list)
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
require "tempfile"
|
5
|
+
|
6
|
+
module Octobat
|
7
|
+
# Encodes parameters into a `multipart/form-data` payload as described by RFC
|
8
|
+
# 2388:
|
9
|
+
#
|
10
|
+
# https://tools.ietf.org/html/rfc2388
|
11
|
+
#
|
12
|
+
# This is most useful for transferring file-like objects.
|
13
|
+
#
|
14
|
+
# Parameters should be added with `#encode`. When ready, use `#body` to get
|
15
|
+
# the encoded result and `#content_type` to get the value that should be
|
16
|
+
# placed in the `Content-Type` header of a subsequent request (which includes
|
17
|
+
# a boundary value).
|
18
|
+
class MultipartEncoder
|
19
|
+
MULTIPART_FORM_DATA = "multipart/form-data"
|
20
|
+
|
21
|
+
# A shortcut for encoding a single set of parameters and finalizing a
|
22
|
+
# result.
|
23
|
+
#
|
24
|
+
# Returns an encoded body and the value that should be set in the content
|
25
|
+
# type header of a subsequent request.
|
26
|
+
def self.encode(params)
|
27
|
+
encoder = MultipartEncoder.new
|
28
|
+
encoder.encode(params)
|
29
|
+
encoder.close
|
30
|
+
[encoder.body, encoder.content_type]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Gets the object's randomly generated boundary string.
|
34
|
+
attr_reader :boundary
|
35
|
+
|
36
|
+
# Initializes a new multipart encoder.
|
37
|
+
def initialize
|
38
|
+
# Kind of weird, but required by Rubocop because the unary plus operator
|
39
|
+
# is considered faster than `Octobat.new`.
|
40
|
+
@body = +""
|
41
|
+
|
42
|
+
# Chose the same number of random bytes that Go uses in its standard
|
43
|
+
# library implementation. Easily enough entropy to ensure that it won't
|
44
|
+
# be present in a file we're sending.
|
45
|
+
@boundary = SecureRandom.hex(30)
|
46
|
+
|
47
|
+
@closed = false
|
48
|
+
@first_field = true
|
49
|
+
end
|
50
|
+
|
51
|
+
# Gets the encoded body. `#close` must be called first.
|
52
|
+
def body
|
53
|
+
raise "object must be closed before getting body" unless @closed
|
54
|
+
|
55
|
+
@body
|
56
|
+
end
|
57
|
+
|
58
|
+
# Finalizes the object by writing the final boundary.
|
59
|
+
def close
|
60
|
+
raise "object already closed" if @closed
|
61
|
+
|
62
|
+
@body << "\r\n"
|
63
|
+
@body << "--#{@boundary}--"
|
64
|
+
|
65
|
+
@closed = true
|
66
|
+
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
# Gets the value including boundary that should be put into a multipart
|
71
|
+
# request's `Content-Type`.
|
72
|
+
def content_type
|
73
|
+
"#{MULTIPART_FORM_DATA}; boundary=#{@boundary}"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Encodes a set of parameters to the body.
|
77
|
+
#
|
78
|
+
# Note that parameters are expected to be a hash, but a "flat" hash such
|
79
|
+
# that complex substructures like hashes and arrays have already been
|
80
|
+
# appropriately Octobat-encoded. Pass a complex structure through
|
81
|
+
# `Util.flatten_params` first before handing it off to this method.
|
82
|
+
def encode(params)
|
83
|
+
raise "no more parameters can be written to closed object" if @closed
|
84
|
+
|
85
|
+
params.each do |name, val|
|
86
|
+
if val.is_a?(::File) || val.is_a?(::Tempfile)
|
87
|
+
write_field(name, val.read, filename: ::File.basename(val.path))
|
88
|
+
elsif val.respond_to?(:read)
|
89
|
+
write_field(name, val.read, filename: "blob")
|
90
|
+
else
|
91
|
+
write_field(name, val, filename: nil)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
# private
|
100
|
+
#
|
101
|
+
|
102
|
+
# Escapes double quotes so that the given value can be used in a
|
103
|
+
# double-quoted string and replaces any linebreak characters with spaces.
|
104
|
+
private def escape(str)
|
105
|
+
str.gsub('"', "%22").tr("\n", " ").tr("\r", " ")
|
106
|
+
end
|
107
|
+
|
108
|
+
private def write_field(name, data, filename:)
|
109
|
+
if !@first_field
|
110
|
+
@body << "\r\n"
|
111
|
+
else
|
112
|
+
@first_field = false
|
113
|
+
end
|
114
|
+
|
115
|
+
@body << "--#{@boundary}\r\n"
|
116
|
+
|
117
|
+
if filename
|
118
|
+
@body << %(Content-Disposition: form-data) +
|
119
|
+
%(; name="#{escape(name.to_s)}") +
|
120
|
+
%(; filename="#{escape(filename)}"\r\n)
|
121
|
+
@body << %(Content-Type: application/octet-stream\r\n)
|
122
|
+
else
|
123
|
+
@body << %(Content-Disposition: form-data) +
|
124
|
+
%(; name="#{escape(name.to_s)}"\r\n)
|
125
|
+
end
|
126
|
+
|
127
|
+
@body << "\r\n"
|
128
|
+
@body << data.to_s
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/lib/octobat/util.rb
CHANGED
@@ -65,7 +65,11 @@ module Octobat
|
|
65
65
|
'document_email_template' => DocumentEmailTemplate,
|
66
66
|
'exports_setting' => ExportsSetting,
|
67
67
|
'document' => Document,
|
68
|
-
'emails_setting' => EmailsSetting
|
68
|
+
'emails_setting' => EmailsSetting,
|
69
|
+
'file' => FileUpload,
|
70
|
+
'file_link' => FileLink,
|
71
|
+
'reporting.report_type' => Reporting::ReportType,
|
72
|
+
'reporting.report_run' => Reporting::ReportRun
|
69
73
|
}
|
70
74
|
end
|
71
75
|
|
data/lib/octobat/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: octobat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.18
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gaultier Laperche
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-05-
|
11
|
+
date: 2020-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|
@@ -79,10 +79,13 @@ files:
|
|
79
79
|
- lib/octobat/errors/octobat_error.rb
|
80
80
|
- lib/octobat/errors/octobat_lib_error.rb
|
81
81
|
- lib/octobat/exports_setting.rb
|
82
|
+
- lib/octobat/file_link.rb
|
83
|
+
- lib/octobat/file_upload.rb
|
82
84
|
- lib/octobat/invoice.rb
|
83
85
|
- lib/octobat/invoice_numbering_sequence.rb
|
84
86
|
- lib/octobat/item.rb
|
85
87
|
- lib/octobat/list_object.rb
|
88
|
+
- lib/octobat/multipart_encoder.rb
|
86
89
|
- lib/octobat/octobat_object.rb
|
87
90
|
- lib/octobat/order.rb
|
88
91
|
- lib/octobat/payment_recipient.rb
|
@@ -91,6 +94,8 @@ files:
|
|
91
94
|
- lib/octobat/payout.rb
|
92
95
|
- lib/octobat/product.rb
|
93
96
|
- lib/octobat/proforma_invoice.rb
|
97
|
+
- lib/octobat/reporting/report_run.rb
|
98
|
+
- lib/octobat/reporting/report_type.rb
|
94
99
|
- lib/octobat/singleton_api_resource.rb
|
95
100
|
- lib/octobat/tax_evidence.rb
|
96
101
|
- lib/octobat/tax_evidence_request.rb
|