rev-api 2.2.1 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +22 -21
  3. data/.ruby-gemset +1 -1
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +8 -8
  6. data/Gemfile +3 -3
  7. data/LICENSE +191 -191
  8. data/README.md +131 -132
  9. data/Rakefile +13 -13
  10. data/examples/cli.rb +270 -270
  11. data/lib/rev-api.rb +26 -26
  12. data/lib/rev-api/api.rb +326 -326
  13. data/lib/rev-api/api_serializable.rb +30 -30
  14. data/lib/rev-api/exceptions.rb +97 -97
  15. data/lib/rev-api/http_client.rb +97 -97
  16. data/lib/rev-api/models/order.rb +129 -130
  17. data/lib/rev-api/models/order_request.rb +292 -194
  18. data/lib/rev-api/version.rb +3 -3
  19. data/rev-api.gemspec +33 -34
  20. data/spec/fixtures/api_cassettes/cancel_order.yml +38 -38
  21. data/spec/fixtures/api_cassettes/cancel_order_not_allowed.yml +40 -40
  22. data/spec/fixtures/api_cassettes/get_attachment_content.yml +399 -399
  23. data/spec/fixtures/api_cassettes/get_attachment_content_as_pdf.yml +399 -399
  24. data/spec/fixtures/api_cassettes/get_attachment_content_as_text.yml +65 -65
  25. data/spec/fixtures/api_cassettes/get_attachment_content_as_youtube_transcript.yml +66 -66
  26. data/spec/fixtures/api_cassettes/get_attachment_content_unacceptable_representation.yml +42 -42
  27. data/spec/fixtures/api_cassettes/get_attachment_content_with_invalid_id.yml +42 -42
  28. data/spec/fixtures/api_cassettes/get_attachment_metadata.yml +42 -42
  29. data/spec/fixtures/api_cassettes/get_attachment_with_invalid_id.yml +40 -40
  30. data/spec/fixtures/api_cassettes/get_orders.yml +122 -122
  31. data/spec/fixtures/api_cassettes/get_orders_with_clientRef.yml +41 -41
  32. data/spec/fixtures/api_cassettes/get_tc_order.yml +44 -44
  33. data/spec/fixtures/api_cassettes/get_third_page_of_orders.yml +52 -52
  34. data/spec/fixtures/api_cassettes/link_input.yml +44 -44
  35. data/spec/fixtures/api_cassettes/link_input_with_all_attributes.yml +44 -44
  36. data/spec/fixtures/api_cassettes/link_input_with_spaces_in_filename.yml +45 -45
  37. data/spec/fixtures/api_cassettes/not_found_order.yml +42 -42
  38. data/spec/fixtures/api_cassettes/submit_cp_order.yml +44 -45
  39. data/spec/fixtures/api_cassettes/submit_su_order.yml +44 -45
  40. data/spec/fixtures/api_cassettes/submit_tc_order_with_account_balance.yml +44 -45
  41. data/spec/fixtures/api_cassettes/submit_tc_order_with_invalid_request.yml +45 -45
  42. data/spec/fixtures/api_cassettes/submit_tc_order_without_specifying_payment.yml +44 -45
  43. data/spec/fixtures/api_cassettes/unauthorized.yml +42 -42
  44. data/spec/fixtures/api_cassettes/upload_input.yml +90 -90
  45. data/spec/fixtures/api_cassettes/upload_input_with_invalid_content_type.yml +91 -91
  46. data/spec/lib/rev/api_spec.rb +30 -24
  47. data/spec/lib/rev/cancel_order_spec.rb +24 -24
  48. data/spec/lib/rev/exceptions_spec.rb +8 -8
  49. data/spec/lib/rev/get_attachment_content_spec.rb +79 -79
  50. data/spec/lib/rev/get_attachment_metadata_spec.rb +33 -33
  51. data/spec/lib/rev/get_order_spec.rb +52 -52
  52. data/spec/lib/rev/get_orders_spec.rb +62 -62
  53. data/spec/lib/rev/http_client_spec.rb +32 -32
  54. data/spec/lib/rev/models/order_request_spec.rb +122 -11
  55. data/spec/lib/rev/models/order_spec.rb +58 -58
  56. data/spec/lib/rev/post_inputs_spec.rb +94 -94
  57. data/spec/lib/rev/post_order_spec.rb +163 -163
  58. data/spec/spec_helper.rb +47 -49
  59. data/spec/test_helpers.rb +5 -0
  60. metadata +54 -69
  61. data/.coveralls.yml +0 -2
@@ -1,26 +1,26 @@
1
- require 'httparty'
2
-
3
- # These three are the only classes that should be accessed directly
4
- require 'rev-api/api'
5
-
6
- module Rev
7
- class << self
8
- # Alias for Rev::Api.new
9
- #
10
- # @return [Rev::Api]
11
- def new(client_api_key, user_api_key, host)
12
- Rev::Api.new(client_api_key, user_api_key, host)
13
- end
14
-
15
- # Delegate to Rev::Api
16
- #
17
- def method_missing(method, *args, &block)
18
- return super unless new.respond_to?(method)
19
- new.send(method, *args, &block)
20
- end
21
-
22
- def respond_to?(method, include_private = false)
23
- new.respond_to?(method, include_private) || super(method, include_private)
24
- end
25
- end
26
- end
1
+ require 'httparty'
2
+
3
+ # These three are the only classes that should be accessed directly
4
+ require 'rev-api/api'
5
+
6
+ module Rev
7
+ class << self
8
+ # Alias for Rev::Api.new
9
+ #
10
+ # @return [Rev::Api]
11
+ def new(client_api_key, user_api_key, host = Rev::Api::PRODUCTION_HOST)
12
+ Rev::Api.new(client_api_key, user_api_key, host)
13
+ end
14
+
15
+ # Delegate to Rev::Api
16
+ #
17
+ def method_missing(method, *args, &block)
18
+ return super unless new.respond_to?(method)
19
+ new.send(method, *args, &block)
20
+ end
21
+
22
+ def respond_to?(method, include_private = false)
23
+ new.respond_to?(method, include_private) || super(method, include_private)
24
+ end
25
+ end
26
+ end
@@ -1,326 +1,326 @@
1
- require 'rev-api/version'
2
- require 'rev-api/http_client'
3
- require 'rev-api/exceptions'
4
- require 'json'
5
-
6
- # automatically include business logic objects
7
- Dir[File.dirname(__FILE__) + '/models/*.rb'].each do |file|
8
- require file
9
- end
10
-
11
- # Rev API Ruby SDK
12
- module Rev
13
- # Main point of interaction with API.
14
- # Wraps common REST operations, returning plain objects.
15
- # Internally utilizes JSON resource representation.
16
- class Api
17
-
18
- # Production host. Used by default for new Rev::Api client
19
- PRODUCTION_HOST = 'www.rev.com'
20
-
21
- # Sandbox domain - pass 'Rev::Api::SANDBOX_HOST' as third param
22
- # into Rev::Api ctor
23
- SANDBOX_HOST = 'api-sandbox.rev.com'
24
-
25
- # @note https://www.rev.com/api/security
26
- # @param client_api_key [String] secret key specific to each partner that wishes to use the Rev API
27
- # @param user_api_key [String] secret key specific to a Rev user, which identifies the user account under whose privileges the requested operation executes
28
- # @param host [String] use {Rev::Api::PRODUCTION_HOST} or {Rev::Api::SANDBOX_HOST}. Production is default value
29
- # @return [HttpClient] client obj
30
- def initialize(client_api_key, user_api_key, host = PRODUCTION_HOST)
31
- @client = HttpClient.new(client_api_key, user_api_key, host)
32
- end
33
-
34
- # Loads single page of existing orders for current client
35
- #
36
- # @note https://www.rev.com/api/ordersget
37
- # @param page [Int, nil] 0-based page number, defaults to 0
38
- # @return [OrdersListPage] paged result containing 'orders'
39
- def get_orders_page(page = 0)
40
- response = @client.get("/orders?page=#{page.to_i}")
41
- Api.verify_get_response(response)
42
- OrdersListPage.new(Api.parse(response))
43
- end
44
-
45
- # Loads all orders for current client. Works by calling get_orders_page multiple times.
46
- # Use with caution if your order list might be large.
47
- #
48
- # @note https://www.rev.com/api/ordersget
49
- # @return [Array of Order] list of orders
50
- def get_all_orders
51
- orders = []
52
- page = 0
53
- loop do
54
- orders_page = self.get_orders_page page
55
- page += 1
56
- orders.push *orders_page.orders
57
- break if (page * orders_page.results_per_page >= orders_page.total_count)
58
- end
59
- orders
60
- end
61
-
62
- # Loads orders whose associated reference ID is the given client_ref
63
- #
64
- # @note https://www.rev.com/api/ordersget
65
- # @param client_ref [String, nil] client reference (required)
66
- # @param page [Int, nil] 0-based page number, defaults to 0
67
- # @return [OrdersListPage] paged result containing 'orders' list
68
- # @raise [ArgumentError] client_ref is nil
69
- def get_orders_by_client_ref(client_ref, page = 0)
70
- raise ArgumentError if client_ref.nil?
71
-
72
- response = @client.get("/orders?clientRef=#{URI.escape(client_ref)}&page=#{page.to_i}")
73
- Api.verify_get_response(response)
74
- OrdersListPage.new(Api.parse(response))
75
- end
76
-
77
- # Returns Order given an order number.
78
- #
79
- # @note https://www.rev.com/api/ordersgetone
80
- # @param number [String] order number, like 'TCXXXXXXXX'
81
- # @return [Order] order obj
82
- def get_order(number)
83
- response = @client.get("/orders/#{number}")
84
- Api.verify_get_response(response)
85
- Order.new(Api.parse(response))
86
- end
87
-
88
- # Cancel an order by number. If cancellation is not allowed, Rev::Api::BadRequestError is raised.
89
- #
90
- # @note https://www.rev.com/api/orderscancel
91
- # @param number [String] order number
92
- # @return [Boolean] true on success, raised Exception from Rev::Api namespace otherwise
93
- def cancel_order(number)
94
- data = { :order_num => number }
95
- response = @client.post("/orders/#{number}/cancel", data)
96
- Api.verify_post_response(response)
97
- end
98
-
99
- # Get metadata about an order attachment.
100
- # Use this method to retrieve information about an order attachment (either transcript,
101
- # caption or source file).
102
- #
103
- # @note https://www.rev.com/api/attachmentsget
104
- # @param id [String] attachment id, as returned in info about an order
105
- # @return [Attachment] attachment object
106
- def get_attachment_metadata(id)
107
- response = @client.get("/attachments/#{id}")
108
- Api.verify_get_response(response)
109
- Attachment.new(Api.parse(response))
110
- end
111
-
112
- # Get the raw data for the attachment with given id.
113
- # Download the contents of an attachment. Use this method to download either a finished transcript,
114
- # or a source file for an order.
115
- # For transcript attachments, you may request to get the contents in a specific
116
- # representation, specified via a mime-type.
117
- #
118
- # See {Rev::Order::Attachment::REPRESENTATIONS} hash, which contains symbols for currently supported mime types.
119
- # The authoritative list is in the API documentation at https://www.rev.com/api/attachmentsgetcontent
120
- #
121
- # If a block is given, the response is passed to the block directly, to allow progressive reading of the data.
122
- # In this case, the block must itself check for error responses, using Api.verify_get_response.
123
- # If no block is given, the full response is returned. In that case, if the response is an error, an appropriate
124
- # error is raised.
125
- #
126
- # @param id [String] attachment id
127
- # @param mime_type [String, nil] mime-type for the desired format in which the content should be retrieved.
128
- # @yieldparam resp [Net::HTTP::Response] the response, ready to be read
129
- # @return [Net::HTTP::Response] the response containing raw data
130
- def get_attachment_content(id, mime_type = nil, &block)
131
- headers = {}
132
-
133
- unless mime_type.nil?
134
- headers['Accept'] = mime_type
135
- headers['Accept-Charset'] = 'utf-8' if mime_type.start_with? 'text/'
136
- end
137
-
138
- if block_given?
139
- @client.get_binary("/attachments/#{id}/content", headers, &block)
140
- else
141
- response = @client.get_binary("/attachments/#{id}/content", headers)
142
- Api.verify_get_response(response)
143
- response
144
- end
145
- end
146
-
147
- # Get the raw data for the attachment with given id.
148
- # Download the contents of an attachment and save it into a file. Use this method to download either a finished transcript,
149
- # or a source file for an order.
150
- # For transcript attachments, you may request to get the contents in a specific
151
- # representation, specified via a mime-type.
152
- #
153
- # See {Rev::Order::Attachment::REPRESENTATIONS} hash, which contains symbols for currently supported mime types.
154
- # The authoritative list is in the API documentation at https://www.rev.com/api/attachmentsgetcontent
155
- #
156
- # @param id [String] attachment id
157
- # @param path [String, nil] path to file into which the content is to be saved.
158
- # @param mime_type [String, nil] mime-type for the desired format in which the content should be retrieved.
159
- # @return [String] filepath content has been saved to. Might raise standard IO exception if file creation files
160
- def save_attachment_content(id, path, mime_type = nil)
161
- headers = {}
162
-
163
- unless mime_type.nil?
164
- headers['Accept'] = mime_type
165
- headers['Accept-Charset'] = 'utf-8' if mime_type.start_with? 'text/'
166
- end
167
-
168
- # same simple approach as box-api does for now: return response.body as-is if path for saving is nil
169
- File.open(path, 'wb') do |file|
170
- response = @client.get_binary("/attachments/#{id}/content", headers) do |resp|
171
- resp.read_body do |segment|
172
- file.write(segment)
173
- end
174
- end
175
- Api.verify_get_response(response)
176
- end
177
-
178
- # we don't handle IO-related exceptions
179
- path
180
- end
181
-
182
- # Get the content of the attachment with given id as a string. Use this method to grab the contents of a finished transcript
183
- # as a string. This method should generally not be used for source attachments, as those are typically
184
- # binary files like MP3s, which cannot be converted to a string.
185
- #
186
- # May raise Rev::Api::NotAcceptableError if the attachment cannot be converted into a text representation.
187
- #
188
- # @param id [String] attachment id
189
- # @return [String] the content of the attachment as a string
190
- def get_attachment_content_as_string(id)
191
- response = self.get_attachment_content(id, Attachment::REPRESENTATIONS[:txt])
192
- response.body
193
- end
194
-
195
- # Submit a new order using {Rev::OrderRequest}.
196
- # @note https://www.rev.com/api/ordersposttranscription - for full information
197
- #
198
- # @param order_request [OrderRequest] object specifying payment, inputs, options and notification info.
199
- # inputs must previously be uploaded using upload_input or create_input_from_link
200
- # @return [String] order number for the new order
201
- # Raises {Rev::BadRequestError} on failure (.code attr exposes API error code -
202
- # see {Rev::OrderRequestError}).
203
- def submit_order(order_request)
204
- response = @client.post("/orders", order_request.to_json, { 'Content-Type' => 'application/json' })
205
- Api.verify_post_response(response)
206
-
207
- new_order_uri = response.headers['Location']
208
- return new_order_uri.split('/')[-1]
209
- end
210
-
211
- # Upload given local file directly as source input for order.
212
- # @note https://www.rev.com/api/inputspost
213
- #
214
- # @param path [String] mandatory, path to local file (relative or absolute) to upload
215
- # @param content_type [String] mandatory, content-type of the file you're uploading
216
- # @return [String] URI identifying newly uploaded media. This URI can be used to identify the input
217
- # when constructing a OrderRequest object to submit an order.
218
- # {Rev::BadRequestError} is raised on failure (.code attr exposes API error code -
219
- # see {Rev::InputRequestError}).
220
- def upload_input(path, content_type)
221
- filename = Pathname.new(path).basename
222
- headers = {
223
- 'Content-Disposition' => "attachment; filename=\"#{filename}\"",
224
- 'Content-Type' => content_type
225
- }
226
-
227
- File.open(path) do |data|
228
- response = @client.post_binary("/inputs", data, headers)
229
- Api.verify_post_response(response)
230
-
231
- headers = HTTParty::Response::Headers.new(response.to_hash)
232
- return headers['Location']
233
- end
234
- end
235
-
236
- # Request creation of a source input based on an external URL which the server will attempt to download.
237
- # @note https://www.rev.com/api/inputspost
238
- #
239
- # @param url [String] mandatory, URL where the media can be retrieved. Must be publicly accessible.
240
- # HTTPS urls are ok as long as the site in question has a valid certificate
241
- # @param filename [String, nil] optional, the filename for the media. If not specified, we will
242
- # determine it from the URL
243
- # @param content_type [String, nil] optional, the content type of the media to be retrieved.
244
- # If not specified, we will try to determine it from the server response
245
- # @return [String] URI identifying newly uploaded media. This URI can be used to identify the input
246
- # when constructing a OrderRequest object to submit an order.
247
- # {Rev::BadRequestError} is raised on failure (.code attr exposes API error code -
248
- # see {Rev::InputRequestError}).
249
- def create_input_from_link(url, filename = nil, content_type = nil)
250
- request = { :url => url }
251
- request[:filename] = filename unless filename.nil?
252
- request[:content_type] = content_type unless content_type.nil?
253
-
254
- response = @client.post("/inputs", request.to_json, { 'Content-Type' => 'application/json' })
255
- Api.verify_post_response(response)
256
-
257
- response.headers['Location']
258
- end
259
-
260
- private
261
- # Below are utility helper methods for handling response
262
- class << self
263
-
264
- # Parse given response's body JSON into Hash, so that it might be
265
- # easily mapped onto business logic object.
266
- #
267
- # @param response [Response] HTTParty response obj
268
- # @return [Hash] hash of values parsed from JSON
269
- def parse(response)
270
- JSON.load response.body.to_s
271
- end
272
-
273
- # Raises exception if response is not considered as success
274
- #
275
- # @param response [HTTPParty::Response] HTTParty response obj. Net::HTTPResponse represented by .response
276
- # @return [Boolean] true if response is considered as successful
277
- def verify_get_response(response)
278
- # HTTP response codes are handled here and propagated up to the caller, since caller should be able
279
- # to handle all types of errors the same - using exceptions
280
- unless response.response.instance_of? Net::HTTPOK
281
- Api.handle_error(response)
282
- end
283
-
284
- true
285
- end
286
-
287
- # (see #verify_get_response)
288
- def verify_post_response(response)
289
- # see https://www.rev.com/api/errorhandling
290
- unless response.response.instance_of?(Net::HTTPCreated) || response.response.instance_of?(Net::HTTPNoContent)
291
- Api.handle_error(response)
292
- end
293
-
294
- true
295
- end
296
-
297
- # Given a response, raises a corresponding Exception.
298
- # Full response is given for the sake of BadRequest reporting,
299
- # which usually contains validation errors.
300
- #
301
- # @param response [Response] containing failing status to look for
302
- def handle_error(response)
303
- case response.response
304
- when Net::HTTPBadRequest
305
- # Bad request - response contains error code and details. Usually means failed validation
306
- body = JSON.load response.body.to_s
307
- msg = "API responded with code #{body['code']}: #{body['message']}"
308
- msg += " Details: #{body['detail'].to_s}" if body['detail']
309
- raise BadRequestError.new msg, body['code']
310
- when Net::HTTPUnauthorized
311
- raise NotAuthorizedError
312
- when Net::HTTPForbidden
313
- raise ForbiddenError
314
- when Net::HTTPNotFound
315
- raise NotFoundError
316
- when Net::HTTPNotAcceptable
317
- raise NotAcceptableError
318
- when Net::HTTPServerError
319
- raise ServerError, "Status code: #{response.code}"
320
- else
321
- raise UnknownError
322
- end
323
- end
324
- end
325
- end
326
- end
1
+ require 'rev-api/version'
2
+ require 'rev-api/http_client'
3
+ require 'rev-api/exceptions'
4
+ require 'json'
5
+
6
+ # automatically include business logic objects
7
+ Dir[File.dirname(__FILE__) + '/models/*.rb'].each do |file|
8
+ require file
9
+ end
10
+
11
+ # Rev API Ruby SDK
12
+ module Rev
13
+ # Main point of interaction with API.
14
+ # Wraps common REST operations, returning plain objects.
15
+ # Internally utilizes JSON resource representation.
16
+ class Api
17
+
18
+ # Production host. Used by default for new Rev::Api client
19
+ PRODUCTION_HOST = 'www.rev.com'
20
+
21
+ # Sandbox domain - pass 'Rev::Api::SANDBOX_HOST' as third param
22
+ # into Rev::Api ctor
23
+ SANDBOX_HOST = 'api-sandbox.rev.com'
24
+
25
+ # @note https://www.rev.com/api/security
26
+ # @param client_api_key [String] secret key specific to each partner that wishes to use the Rev API
27
+ # @param user_api_key [String] secret key specific to a Rev user, which identifies the user account under whose privileges the requested operation executes
28
+ # @param host [String] use {Rev::Api::PRODUCTION_HOST} or {Rev::Api::SANDBOX_HOST}. Production is default value
29
+ # @return [HttpClient] client obj
30
+ def initialize(client_api_key, user_api_key, host = PRODUCTION_HOST)
31
+ @client = HttpClient.new(client_api_key, user_api_key, host)
32
+ end
33
+
34
+ # Loads single page of existing orders for current client
35
+ #
36
+ # @note https://www.rev.com/api/ordersget
37
+ # @param page [Int, nil] 0-based page number, defaults to 0
38
+ # @return [OrdersListPage] paged result containing 'orders'
39
+ def get_orders_page(page = 0)
40
+ response = @client.get("/orders?page=#{page.to_i}")
41
+ Api.verify_get_response(response)
42
+ OrdersListPage.new(Api.parse(response))
43
+ end
44
+
45
+ # Loads all orders for current client. Works by calling get_orders_page multiple times.
46
+ # Use with caution if your order list might be large.
47
+ #
48
+ # @note https://www.rev.com/api/ordersget
49
+ # @return [Array of Order] list of orders
50
+ def get_all_orders
51
+ orders = []
52
+ page = 0
53
+ loop do
54
+ orders_page = self.get_orders_page page
55
+ page += 1
56
+ orders.push *orders_page.orders
57
+ break if (page * orders_page.results_per_page >= orders_page.total_count)
58
+ end
59
+ orders
60
+ end
61
+
62
+ # Loads orders whose associated reference ID is the given client_ref
63
+ #
64
+ # @note https://www.rev.com/api/ordersget
65
+ # @param client_ref [String, nil] client reference (required)
66
+ # @param page [Int, nil] 0-based page number, defaults to 0
67
+ # @return [OrdersListPage] paged result containing 'orders' list
68
+ # @raise [ArgumentError] client_ref is nil
69
+ def get_orders_by_client_ref(client_ref, page = 0)
70
+ raise ArgumentError if client_ref.nil?
71
+
72
+ response = @client.get("/orders?clientRef=#{URI.escape(client_ref)}&page=#{page.to_i}")
73
+ Api.verify_get_response(response)
74
+ OrdersListPage.new(Api.parse(response))
75
+ end
76
+
77
+ # Returns Order given an order number.
78
+ #
79
+ # @note https://www.rev.com/api/ordersgetone
80
+ # @param number [String] order number, like 'TCXXXXXXXX'
81
+ # @return [Order] order obj
82
+ def get_order(number)
83
+ response = @client.get("/orders/#{number}")
84
+ Api.verify_get_response(response)
85
+ Order.new(Api.parse(response))
86
+ end
87
+
88
+ # Cancel an order by number. If cancellation is not allowed, Rev::Api::BadRequestError is raised.
89
+ #
90
+ # @note https://www.rev.com/api/orderscancel
91
+ # @param number [String] order number
92
+ # @return [Boolean] true on success, raised Exception from Rev::Api namespace otherwise
93
+ def cancel_order(number)
94
+ data = { :order_num => number }
95
+ response = @client.post("/orders/#{number}/cancel", data)
96
+ Api.verify_post_response(response)
97
+ end
98
+
99
+ # Get metadata about an order attachment.
100
+ # Use this method to retrieve information about an order attachment (either transcript,
101
+ # caption or source file).
102
+ #
103
+ # @note https://www.rev.com/api/attachmentsget
104
+ # @param id [String] attachment id, as returned in info about an order
105
+ # @return [Attachment] attachment object
106
+ def get_attachment_metadata(id)
107
+ response = @client.get("/attachments/#{id}")
108
+ Api.verify_get_response(response)
109
+ Attachment.new(Api.parse(response))
110
+ end
111
+
112
+ # Get the raw data for the attachment with given id.
113
+ # Download the contents of an attachment. Use this method to download either a finished transcript,
114
+ # or a source file for an order.
115
+ # For transcript attachments, you may request to get the contents in a specific
116
+ # representation, specified via a mime-type.
117
+ #
118
+ # See {Rev::Order::Attachment::REPRESENTATIONS} hash, which contains symbols for currently supported mime types.
119
+ # The authoritative list is in the API documentation at https://www.rev.com/api/attachmentsgetcontent
120
+ #
121
+ # If a block is given, the response is passed to the block directly, to allow progressive reading of the data.
122
+ # In this case, the block must itself check for error responses, using Api.verify_get_response.
123
+ # If no block is given, the full response is returned. In that case, if the response is an error, an appropriate
124
+ # error is raised.
125
+ #
126
+ # @param id [String] attachment id
127
+ # @param mime_type [String, nil] mime-type for the desired format in which the content should be retrieved.
128
+ # @yieldparam resp [Net::HTTP::Response] the response, ready to be read
129
+ # @return [Net::HTTP::Response] the response containing raw data
130
+ def get_attachment_content(id, mime_type = nil, &block)
131
+ headers = {}
132
+
133
+ unless mime_type.nil?
134
+ headers['Accept'] = mime_type
135
+ headers['Accept-Charset'] = 'utf-8' if mime_type.start_with? 'text/'
136
+ end
137
+
138
+ if block_given?
139
+ @client.get_binary("/attachments/#{id}/content", headers, &block)
140
+ else
141
+ response = @client.get_binary("/attachments/#{id}/content", headers)
142
+ Api.verify_get_response(response)
143
+ response
144
+ end
145
+ end
146
+
147
+ # Get the raw data for the attachment with given id.
148
+ # Download the contents of an attachment and save it into a file. Use this method to download either a finished transcript,
149
+ # or a source file for an order.
150
+ # For transcript attachments, you may request to get the contents in a specific
151
+ # representation, specified via a mime-type.
152
+ #
153
+ # See {Rev::Order::Attachment::REPRESENTATIONS} hash, which contains symbols for currently supported mime types.
154
+ # The authoritative list is in the API documentation at https://www.rev.com/api/attachmentsgetcontent
155
+ #
156
+ # @param id [String] attachment id
157
+ # @param path [String, nil] path to file into which the content is to be saved.
158
+ # @param mime_type [String, nil] mime-type for the desired format in which the content should be retrieved.
159
+ # @return [String] filepath content has been saved to. Might raise standard IO exception if file creation files
160
+ def save_attachment_content(id, path, mime_type = nil)
161
+ headers = {}
162
+
163
+ unless mime_type.nil?
164
+ headers['Accept'] = mime_type
165
+ headers['Accept-Charset'] = 'utf-8' if mime_type.start_with? 'text/'
166
+ end
167
+
168
+ # same simple approach as box-api does for now: return response.body as-is if path for saving is nil
169
+ File.open(path, 'wb') do |file|
170
+ response = @client.get_binary("/attachments/#{id}/content", headers) do |resp|
171
+ resp.read_body do |segment|
172
+ file.write(segment)
173
+ end
174
+ end
175
+ Api.verify_get_response(response)
176
+ end
177
+
178
+ # we don't handle IO-related exceptions
179
+ path
180
+ end
181
+
182
+ # Get the content of the attachment with given id as a string. Use this method to grab the contents of a finished transcript
183
+ # as a string. This method should generally not be used for source attachments, as those are typically
184
+ # binary files like MP3s, which cannot be converted to a string.
185
+ #
186
+ # May raise Rev::Api::NotAcceptableError if the attachment cannot be converted into a text representation.
187
+ #
188
+ # @param id [String] attachment id
189
+ # @return [String] the content of the attachment as a string
190
+ def get_attachment_content_as_string(id)
191
+ response = self.get_attachment_content(id, Attachment::REPRESENTATIONS[:txt])
192
+ response.body
193
+ end
194
+
195
+ # Submit a new order using {Rev::OrderRequest}.
196
+ # @note https://www.rev.com/api/ordersposttranscription - for full information
197
+ #
198
+ # @param order_request [OrderRequest] object specifying payment, inputs, options and notification info.
199
+ # inputs must previously be uploaded using upload_input or create_input_from_link
200
+ # @return [String] order number for the new order
201
+ # Raises {Rev::BadRequestError} on failure (.code attr exposes API error code -
202
+ # see {Rev::OrderRequestError}).
203
+ def submit_order(order_request)
204
+ response = @client.post("/orders", order_request.to_json, { 'Content-Type' => 'application/json' })
205
+ Api.verify_post_response(response)
206
+
207
+ new_order_uri = response.headers['Location']
208
+ return new_order_uri.split('/')[-1]
209
+ end
210
+
211
+ # Upload given local file directly as source input for order.
212
+ # @note https://www.rev.com/api/inputspost
213
+ #
214
+ # @param path [String] mandatory, path to local file (relative or absolute) to upload
215
+ # @param content_type [String] mandatory, content-type of the file you're uploading
216
+ # @return [String] URI identifying newly uploaded media. This URI can be used to identify the input
217
+ # when constructing a OrderRequest object to submit an order.
218
+ # {Rev::BadRequestError} is raised on failure (.code attr exposes API error code -
219
+ # see {Rev::InputRequestError}).
220
+ def upload_input(path, content_type)
221
+ filename = Pathname.new(path).basename
222
+ headers = {
223
+ 'Content-Disposition' => "attachment; filename=\"#{filename}\"",
224
+ 'Content-Type' => content_type
225
+ }
226
+
227
+ File.open(path) do |data|
228
+ response = @client.post_binary("/inputs", data, headers)
229
+ Api.verify_post_response(response)
230
+
231
+ headers = HTTParty::Response::Headers.new(response.to_hash)
232
+ return headers['Location']
233
+ end
234
+ end
235
+
236
+ # Request creation of a source input based on an external URL which the server will attempt to download.
237
+ # @note https://www.rev.com/api/inputspost
238
+ #
239
+ # @param url [String] mandatory, URL where the media can be retrieved. Must be publicly accessible.
240
+ # HTTPS urls are ok as long as the site in question has a valid certificate
241
+ # @param filename [String, nil] optional, the filename for the media. If not specified, we will
242
+ # determine it from the URL
243
+ # @param content_type [String, nil] optional, the content type of the media to be retrieved.
244
+ # If not specified, we will try to determine it from the server response
245
+ # @return [String] URI identifying newly uploaded media. This URI can be used to identify the input
246
+ # when constructing a OrderRequest object to submit an order.
247
+ # {Rev::BadRequestError} is raised on failure (.code attr exposes API error code -
248
+ # see {Rev::InputRequestError}).
249
+ def create_input_from_link(url, filename = nil, content_type = nil)
250
+ request = { :url => url }
251
+ request[:filename] = filename unless filename.nil?
252
+ request[:content_type] = content_type unless content_type.nil?
253
+
254
+ response = @client.post("/inputs", request.to_json, { 'Content-Type' => 'application/json' })
255
+ Api.verify_post_response(response)
256
+
257
+ response.headers['Location']
258
+ end
259
+
260
+ private
261
+ # Below are utility helper methods for handling response
262
+ class << self
263
+
264
+ # Parse given response's body JSON into Hash, so that it might be
265
+ # easily mapped onto business logic object.
266
+ #
267
+ # @param response [Response] HTTParty response obj
268
+ # @return [Hash] hash of values parsed from JSON
269
+ def parse(response)
270
+ JSON.load response.body.to_s
271
+ end
272
+
273
+ # Raises exception if response is not considered as success
274
+ #
275
+ # @param response [HTTPParty::Response] HTTParty response obj. Net::HTTPResponse represented by .response
276
+ # @return [Boolean] true if response is considered as successful
277
+ def verify_get_response(response)
278
+ # HTTP response codes are handled here and propagated up to the caller, since caller should be able
279
+ # to handle all types of errors the same - using exceptions
280
+ unless response.response.instance_of? Net::HTTPOK
281
+ Api.handle_error(response)
282
+ end
283
+
284
+ true
285
+ end
286
+
287
+ # (see #verify_get_response)
288
+ def verify_post_response(response)
289
+ # see https://www.rev.com/api/errorhandling
290
+ unless response.response.instance_of?(Net::HTTPCreated) || response.response.instance_of?(Net::HTTPNoContent)
291
+ Api.handle_error(response)
292
+ end
293
+
294
+ true
295
+ end
296
+
297
+ # Given a response, raises a corresponding Exception.
298
+ # Full response is given for the sake of BadRequest reporting,
299
+ # which usually contains validation errors.
300
+ #
301
+ # @param response [Response] containing failing status to look for
302
+ def handle_error(response)
303
+ case response.response
304
+ when Net::HTTPBadRequest
305
+ # Bad request - response contains error code and details. Usually means failed validation
306
+ body = JSON.load response.body.to_s
307
+ msg = "API responded with code #{body['code']}: #{body['message']}"
308
+ msg += " Details: #{body['detail'].to_s}" if body['detail']
309
+ raise BadRequestError.new msg, body['code']
310
+ when Net::HTTPUnauthorized
311
+ raise NotAuthorizedError
312
+ when Net::HTTPForbidden
313
+ raise ForbiddenError
314
+ when Net::HTTPNotFound
315
+ raise NotFoundError
316
+ when Net::HTTPNotAcceptable
317
+ raise NotAcceptableError
318
+ when Net::HTTPServerError
319
+ raise ServerError, "Status code: #{response.code}"
320
+ else
321
+ raise UnknownError
322
+ end
323
+ end
324
+ end
325
+ end
326
+ end