docraptor 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 520005260c4eb699f6ac0c8b223def5e9624e57d
4
+ data.tar.gz: 6a976eea99f53053665f0f4995b690e606c3ecc0
5
+ SHA512:
6
+ metadata.gz: eaa8cd06bd4ef88d656e7342941684c38513edeb6c33851aec486b18815c829edd436df1fea2d35adf491cd4a5afb51e04418bc10dbc6caea9b0c990a5c5a75a
7
+ data.tar.gz: 109acdcb676b246f7124371d8a5b4dcf1bb5bcf2dc2d223862a7135951c546f24ee49e150f9e564f6ab66f11ef216f3ba8c157fd3404e0166bbde90d6d785f9d
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler/setup'
2
+ require 'bundler/gem_tasks'
data/docraptor.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "docraptor/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "docraptor"
7
+ s.version = DocRaptor::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Elijah Miller", "Matthew Gordon"]
10
+ s.email = ["elijah@expectedbehavior.com", "matt@expectedbehavior.com"]
11
+ s.homepage = "https://docraptor.com"
12
+ s.summary = %q{A wrapper for the DocRaptor HTML to PDF/XLS service.}
13
+ s.description = %q{A native client library for the DocRaptor HTML to PDF/XLS service.}
14
+ s.license = "MIT"
15
+
16
+ s.add_runtime_dependency 'typhoeus', '~> 0.2', '>= 0.2.1'
17
+ s.add_runtime_dependency 'json', '~> 1.4', '>= 1.4.6'
18
+
19
+ s.add_development_dependency 'rake', '~> 10.4', '>= 10.4.2'
20
+ s.add_development_dependency 'rspec', '~> 3.2', '>= 3.2.0'
21
+ s.add_development_dependency 'vcr', '~> 2.9', '>= 2.9.3'
22
+ s.add_development_dependency 'webmock', '~> 1.6', '>= 1.6.2'
23
+ s.add_development_dependency 'autotest', '~> 4.4', '>= 4.4.6'
24
+ s.add_development_dependency 'autotest-rails-pure', '~> 4.1', '>= 4.1.2'
25
+ s.add_development_dependency 'autotest-growl', '~> 0.2', '>= 0.2.16'
26
+ s.add_development_dependency 'autotest-fsevent', '~> 0.2', '>= 0.2.10'
27
+
28
+ s.files = `find *`.split("\n").uniq.sort.select{|f| !f.empty? }
29
+ s.test_files = `find spec/*`.split("\n")
30
+ s.executables = []
31
+ s.require_paths = ["lib"]
32
+ end
data/lib/docraptor.rb ADDED
@@ -0,0 +1,33 @@
1
+ # Common files
2
+ require 'docraptor/api_client'
3
+ require 'docraptor/api_error'
4
+ require 'docraptor/version'
5
+ require 'docraptor/configuration'
6
+
7
+ # Models
8
+ require 'docraptor/models/base_object'
9
+ require 'docraptor/models/doc'
10
+ require 'docraptor/models/prince_options'
11
+ require 'docraptor/models/async_doc'
12
+ require 'docraptor/models/async_doc_status'
13
+
14
+ # APIs
15
+ require 'docraptor/api/client_api'
16
+
17
+ module DocRaptor
18
+ class << self
19
+ # Configure sdk using block.
20
+ # DocRaptor.configure do |config|
21
+ # config.username = "xxx"
22
+ # config.password = "xxx"
23
+ # end
24
+ # If no block given, return the configuration singleton instance.
25
+ def configure
26
+ if block_given?
27
+ yield Configuration.instance
28
+ else
29
+ Configuration.instance
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,219 @@
1
+ require "uri"
2
+
3
+ module DocRaptor
4
+ class ClientApi
5
+ attr_accessor :api_client
6
+
7
+ def initialize(api_client = nil)
8
+ @api_client = api_client || Configuration.api_client
9
+ end
10
+
11
+ #
12
+ # Creates a document asynchronously.\nYou must use a callback url or the the returned status id and the status api to find out when it completes. Then use the download api to get the document.
13
+ # @param doc The document to be created.
14
+ # @param [Hash] opts the optional parameters
15
+ # @return [AsyncDoc]
16
+ def async_docs_post(doc, opts = {})
17
+ if Configuration.debugging
18
+ Configuration.logger.debug "Calling API: ClientApi#async_docs_post ..."
19
+ end
20
+
21
+ # verify the required parameter 'doc' is set
22
+ fail "Missing the required parameter 'doc' when calling async_docs_post" if doc.nil?
23
+
24
+ # resource path
25
+ path = "/async_docs".sub('{format}','json')
26
+
27
+ # query parameters
28
+ query_params = {}
29
+
30
+ # header parameters
31
+ header_params = {}
32
+
33
+ # HTTP header 'Accept' (if needed)
34
+ _header_accept = ['application/json', 'application/xml', 'application/pdf', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
35
+ _header_accept_result = @api_client.select_header_accept(_header_accept) and header_params['Accept'] = _header_accept_result
36
+
37
+ # HTTP header 'Content-Type'
38
+ _header_content_type = []
39
+ header_params['Content-Type'] = @api_client.select_header_content_type(_header_content_type)
40
+
41
+ # form parameters
42
+ form_params = {}
43
+
44
+ # http body (model)
45
+ post_body = @api_client.object_to_http_body(doc)
46
+
47
+
48
+ auth_names = ['basicAuth']
49
+ result = @api_client.call_api(:POST, path,
50
+ :header_params => header_params,
51
+ :query_params => query_params,
52
+ :form_params => form_params,
53
+ :body => post_body,
54
+ :auth_names => auth_names,
55
+ :return_type => 'AsyncDoc')
56
+ if Configuration.debugging
57
+ Configuration.logger.debug "API called: ClientApi#async_docs_post. Result: #{result.inspect}"
58
+ end
59
+ return result
60
+ end
61
+
62
+ #
63
+ # Creates a document synchronously.
64
+ # @param doc The document to be created.
65
+ # @param [Hash] opts the optional parameters
66
+ # @return [File]
67
+ def docs_post(doc, opts = {})
68
+ if Configuration.debugging
69
+ Configuration.logger.debug "Calling API: ClientApi#docs_post ..."
70
+ end
71
+
72
+ # verify the required parameter 'doc' is set
73
+ fail "Missing the required parameter 'doc' when calling docs_post" if doc.nil?
74
+
75
+ # resource path
76
+ path = "/docs".sub('{format}','json')
77
+
78
+ # query parameters
79
+ query_params = {}
80
+
81
+ # header parameters
82
+ header_params = {}
83
+
84
+ # HTTP header 'Accept' (if needed)
85
+ _header_accept = ['application/json', 'application/xml', 'application/pdf', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
86
+ _header_accept_result = @api_client.select_header_accept(_header_accept) and header_params['Accept'] = _header_accept_result
87
+
88
+ # HTTP header 'Content-Type'
89
+ _header_content_type = []
90
+ header_params['Content-Type'] = @api_client.select_header_content_type(_header_content_type)
91
+
92
+ # form parameters
93
+ form_params = {}
94
+
95
+ # http body (model)
96
+ post_body = @api_client.object_to_http_body(doc)
97
+
98
+
99
+ auth_names = ['basicAuth']
100
+ result = @api_client.call_api(:POST, path,
101
+ :header_params => header_params,
102
+ :query_params => query_params,
103
+ :form_params => form_params,
104
+ :body => post_body,
105
+ :auth_names => auth_names,
106
+ :return_type => 'File')
107
+ if Configuration.debugging
108
+ Configuration.logger.debug "API called: ClientApi#docs_post. Result: #{result.inspect}"
109
+ end
110
+ return result
111
+ end
112
+
113
+ #
114
+ # Downloads a document.
115
+ # @param id The download_id returned from status request or a callback.
116
+ # @param [Hash] opts the optional parameters
117
+ # @return [File]
118
+ def download_id_get(id, opts = {})
119
+ if Configuration.debugging
120
+ Configuration.logger.debug "Calling API: ClientApi#download_id_get ..."
121
+ end
122
+
123
+ # verify the required parameter 'id' is set
124
+ fail "Missing the required parameter 'id' when calling download_id_get" if id.nil?
125
+
126
+ # resource path
127
+ path = "/download/{id}".sub('{format}','json').sub('{' + 'id' + '}', id.to_s)
128
+
129
+ # query parameters
130
+ query_params = {}
131
+
132
+ # header parameters
133
+ header_params = {}
134
+
135
+ # HTTP header 'Accept' (if needed)
136
+ _header_accept = ['application/json', 'application/xml', 'application/pdf', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
137
+ _header_accept_result = @api_client.select_header_accept(_header_accept) and header_params['Accept'] = _header_accept_result
138
+
139
+ # HTTP header 'Content-Type'
140
+ _header_content_type = []
141
+ header_params['Content-Type'] = @api_client.select_header_content_type(_header_content_type)
142
+
143
+ # form parameters
144
+ form_params = {}
145
+
146
+ # http body (model)
147
+ post_body = nil
148
+
149
+
150
+ auth_names = ['basicAuth']
151
+ result = @api_client.call_api(:GET, path,
152
+ :header_params => header_params,
153
+ :query_params => query_params,
154
+ :form_params => form_params,
155
+ :body => post_body,
156
+ :auth_names => auth_names,
157
+ :return_type => 'File')
158
+ if Configuration.debugging
159
+ Configuration.logger.debug "API called: ClientApi#download_id_get. Result: #{result.inspect}"
160
+ end
161
+ return result
162
+ end
163
+
164
+ #
165
+ # Check on the status of an asynchronously created document.
166
+ # @param id The status_id returned when creating an asynchronous document.
167
+ # @param [Hash] opts the optional parameters
168
+ # @return [AsyncDocStatus]
169
+ def status_id_get(id, opts = {})
170
+ if Configuration.debugging
171
+ Configuration.logger.debug "Calling API: ClientApi#status_id_get ..."
172
+ end
173
+
174
+ # verify the required parameter 'id' is set
175
+ fail "Missing the required parameter 'id' when calling status_id_get" if id.nil?
176
+
177
+ # resource path
178
+ path = "/status/{id}".sub('{format}','json').sub('{' + 'id' + '}', id.to_s)
179
+
180
+ # query parameters
181
+ query_params = {}
182
+
183
+ # header parameters
184
+ header_params = {}
185
+
186
+ # HTTP header 'Accept' (if needed)
187
+ _header_accept = ['application/json', 'application/xml', 'application/pdf', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
188
+ _header_accept_result = @api_client.select_header_accept(_header_accept) and header_params['Accept'] = _header_accept_result
189
+
190
+ # HTTP header 'Content-Type'
191
+ _header_content_type = []
192
+ header_params['Content-Type'] = @api_client.select_header_content_type(_header_content_type)
193
+
194
+ # form parameters
195
+ form_params = {}
196
+
197
+ # http body (model)
198
+ post_body = nil
199
+
200
+
201
+ auth_names = ['basicAuth']
202
+ result = @api_client.call_api(:GET, path,
203
+ :header_params => header_params,
204
+ :query_params => query_params,
205
+ :form_params => form_params,
206
+ :body => post_body,
207
+ :auth_names => auth_names,
208
+ :return_type => 'AsyncDocStatus')
209
+ if Configuration.debugging
210
+ Configuration.logger.debug "API called: ClientApi#status_id_get. Result: #{result.inspect}"
211
+ end
212
+ return result
213
+ end
214
+ end
215
+ end
216
+
217
+
218
+
219
+
@@ -0,0 +1,273 @@
1
+ require 'date'
2
+ require 'json'
3
+ require 'logger'
4
+ require 'tempfile'
5
+ require 'typhoeus'
6
+ require 'uri'
7
+
8
+ module DocRaptor
9
+ class ApiClient
10
+
11
+ attr_accessor :host
12
+
13
+ # Defines the headers to be used in HTTP requests of all API calls by default.
14
+ #
15
+ # @return [Hash]
16
+ attr_accessor :default_headers
17
+
18
+ # Stores the HTTP response from the last API call using this API client.
19
+ attr_accessor :last_response
20
+
21
+ def initialize(host = nil)
22
+ @host = host || Configuration.base_url
23
+ @format = 'json'
24
+ @user_agent = "ruby-swagger-#{VERSION}"
25
+ @default_headers = {
26
+ 'Content-Type' => "application/#{@format.downcase}",
27
+ 'User-Agent' => @user_agent
28
+ }
29
+ end
30
+
31
+ def call_api(http_method, path, opts = {})
32
+ request = build_request(http_method, path, opts)
33
+ response = request.run
34
+
35
+ # record as last response
36
+ @last_response = response
37
+
38
+ if Configuration.debugging
39
+ Configuration.logger.debug "HTTP response body ~BEGIN~\n#{response.body}\n~END~\n"
40
+ end
41
+
42
+ unless response.success?
43
+ fail ApiError.new(:code => response.code,
44
+ :response_headers => response.headers,
45
+ :response_body => response.body),
46
+ response.status_message
47
+ end
48
+
49
+ if opts[:return_type]
50
+ deserialize(response, opts[:return_type])
51
+ else
52
+ nil
53
+ end
54
+ end
55
+
56
+ def build_request(http_method, path, opts = {})
57
+ url = build_request_url(path)
58
+ http_method = http_method.to_sym.downcase
59
+
60
+ header_params = @default_headers.merge(opts[:header_params] || {})
61
+ query_params = opts[:query_params] || {}
62
+ form_params = opts[:form_params] || {}
63
+
64
+
65
+ update_params_for_auth! header_params, query_params, opts[:auth_names]
66
+
67
+
68
+ req_opts = {
69
+ :method => http_method,
70
+ :headers => header_params,
71
+ :params => query_params,
72
+ :ssl_verifypeer => Configuration.verify_ssl,
73
+ :sslcert => Configuration.cert_file,
74
+ :sslkey => Configuration.key_file,
75
+ :cainfo => Configuration.ssl_ca_cert,
76
+ :verbose => Configuration.debugging
77
+ }
78
+
79
+ if [:post, :patch, :put, :delete].include?(http_method)
80
+ req_body = build_request_body(header_params, form_params, opts[:body])
81
+ req_opts.update :body => req_body
82
+ if Configuration.debugging
83
+ Configuration.logger.debug "HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n"
84
+ end
85
+ end
86
+
87
+ Typhoeus::Request.new(url, req_opts)
88
+ end
89
+
90
+ # Deserialize the response to the given return type.
91
+ #
92
+ # @param [String] return_type some examples: "User", "Array[User]", "Hash[String,Integer]"
93
+ def deserialize(response, return_type)
94
+ body = response.body
95
+ return nil if body.nil? || body.empty?
96
+
97
+ # handle file downloading - save response body into a tmp file and return the File instance
98
+ return download_file(response) if return_type == 'File'
99
+
100
+ # ensuring a default content type
101
+ content_type = response.headers['Content-Type'] || 'application/json'
102
+
103
+ unless content_type.start_with?('application/json')
104
+ fail "Content-Type is not supported: #{content_type}"
105
+ end
106
+
107
+ begin
108
+ data = JSON.parse("[#{body}]", :symbolize_names => true)[0]
109
+ rescue JSON::ParserError => e
110
+ if %w(String Date DateTime).include?(return_type)
111
+ data = body
112
+ else
113
+ raise e
114
+ end
115
+ end
116
+
117
+ convert_to_type data, return_type
118
+ end
119
+
120
+ # Convert data to the given return type.
121
+ def convert_to_type(data, return_type)
122
+ return nil if data.nil?
123
+ case return_type
124
+ when 'String'
125
+ data.to_s
126
+ when 'Integer'
127
+ data.to_i
128
+ when 'Float'
129
+ data.to_f
130
+ when 'BOOLEAN'
131
+ data == true
132
+ when 'DateTime'
133
+ # parse date time (expecting ISO 8601 format)
134
+ DateTime.parse data
135
+ when 'Date'
136
+ # parse date time (expecting ISO 8601 format)
137
+ Date.parse data
138
+ when 'Object'
139
+ # generic object, return directly
140
+ data
141
+ when /\AArray<(.+)>\z/
142
+ # e.g. Array<Pet>
143
+ sub_type = $1
144
+ data.map {|item| convert_to_type(item, sub_type) }
145
+ when /\AHash\<String, (.+)\>\z/
146
+ # e.g. Hash<String, Integer>
147
+ sub_type = $1
148
+ {}.tap do |hash|
149
+ data.each {|k, v| hash[k] = convert_to_type(v, sub_type) }
150
+ end
151
+ else
152
+ # models, e.g. Pet
153
+ DocRaptor.const_get(return_type).new.tap do |model|
154
+ model.build_from_hash data
155
+ end
156
+ end
157
+ end
158
+
159
+ # Save response body into a file in (the defined) temporary folder, using the filename
160
+ # from the "Content-Disposition" header if provided, otherwise a random filename.
161
+ #
162
+ # @see Configuration#temp_folder_path
163
+ # @return [File] the file downloaded
164
+ def download_file(response)
165
+ tmp_file = Tempfile.new '', Configuration.temp_folder_path
166
+ content_disposition = response.headers['Content-Disposition']
167
+ if content_disposition
168
+ filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
169
+ path = File.join File.dirname(tmp_file), filename
170
+ else
171
+ path = tmp_file.path
172
+ end
173
+ # close and delete temp file
174
+ tmp_file.close!
175
+
176
+ File.open(path, 'w') { |file| file.write(response.body) }
177
+ Configuration.logger.info "File written to #{path}. Please move the file to a proper "\
178
+ "folder for further processing and delete the temp afterwards"
179
+ File.new(path)
180
+ end
181
+
182
+ def build_request_url(path)
183
+ # Add leading and trailing slashes to path
184
+ path = "/#{path}".gsub(/\/+/, '/')
185
+ URI.encode(host + path)
186
+ end
187
+
188
+ def build_request_body(header_params, form_params, body)
189
+ # http form
190
+ if header_params['Content-Type'] == 'application/x-www-form-urlencoded' ||
191
+ header_params['Content-Type'] == 'multipart/form-data'
192
+ data = form_params.dup
193
+ data.each do |key, value|
194
+ data[key] = value.to_s if value && !value.is_a?(File)
195
+ end
196
+ elsif body
197
+ data = body.is_a?(String) ? body : body.to_json
198
+ else
199
+ data = nil
200
+ end
201
+ data
202
+ end
203
+
204
+ # Update hearder and query params based on authentication settings.
205
+ def update_params_for_auth!(header_params, query_params, auth_names)
206
+ Array(auth_names).each do |auth_name|
207
+ auth_setting = Configuration.auth_settings[auth_name]
208
+ next unless auth_setting
209
+ case auth_setting[:in]
210
+ when 'header' then header_params[auth_setting[:key]] = auth_setting[:value]
211
+ when 'query' then query_params[auth_setting[:key]] = auth_setting[:value]
212
+ else fail ArgumentError, 'Authentication token must be in `query` of `header`'
213
+ end
214
+ end
215
+ end
216
+
217
+ def user_agent=(user_agent)
218
+ @user_agent = user_agent
219
+ @default_headers['User-Agent'] = @user_agent
220
+ end
221
+
222
+ # Return Accept header based on an array of accepts provided.
223
+ # @param [Array] accepts array for Accept
224
+ # @return [String] the Accept header (e.g. application/json)
225
+ def select_header_accept(accepts)
226
+ if accepts.empty?
227
+ return
228
+ elsif accepts.any?{ |s| s.casecmp('application/json') == 0 }
229
+ 'application/json' # look for json data by default
230
+ else
231
+ accepts.join(',')
232
+ end
233
+ end
234
+
235
+ # Return Content-Type header based on an array of content types provided.
236
+ # @param [Array] content_types array for Content-Type
237
+ # @return [String] the Content-Type header (e.g. application/json)
238
+ def select_header_content_type(content_types)
239
+ if content_types.empty?
240
+ 'application/json' # use application/json by default
241
+ elsif content_types.any?{ |s| s.casecmp('application/json')==0 }
242
+ 'application/json' # use application/json if it's included
243
+ else
244
+ content_types[0] # otherwise, use the first one
245
+ end
246
+ end
247
+
248
+ # Convert object (array, hash, object, etc) to JSON string.
249
+ # @param [Object] model object to be converted into JSON string
250
+ # @return [String] JSON string representation of the object
251
+ def object_to_http_body(model)
252
+ return if model.nil?
253
+ _body = nil
254
+ if model.is_a?(Array)
255
+ _body = model.map{|m| object_to_hash(m) }
256
+ else
257
+ _body = object_to_hash(model)
258
+ end
259
+ _body.to_json
260
+ end
261
+
262
+ # Convert object(non-array) to hash.
263
+ # @param [Object] obj object to be converted into JSON string
264
+ # @return [String] JSON string representation of the object
265
+ def object_to_hash(obj)
266
+ if obj.respond_to?(:to_hash)
267
+ obj.to_hash
268
+ else
269
+ obj
270
+ end
271
+ end
272
+ end
273
+ end