rev-api 2.2.0 → 2.2.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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -1
  3. data/.gitignore +21 -21
  4. data/.ruby-gemset +1 -1
  5. data/.ruby-version +1 -1
  6. data/.travis.yml +8 -8
  7. data/Gemfile +3 -3
  8. data/LICENSE +191 -191
  9. data/README.md +132 -132
  10. data/Rakefile +13 -13
  11. data/examples/cli.rb +270 -270
  12. data/lib/rev-api.rb +25 -25
  13. data/lib/rev-api/api.rb +326 -326
  14. data/lib/rev-api/api_serializable.rb +30 -30
  15. data/lib/rev-api/exceptions.rb +97 -100
  16. data/lib/rev-api/http_client.rb +97 -97
  17. data/lib/rev-api/models/order.rb +130 -138
  18. data/lib/rev-api/models/order_request.rb +194 -222
  19. data/lib/rev-api/version.rb +3 -3
  20. data/rev-api.gemspec +34 -34
  21. data/spec/fixtures/api_cassettes/cancel_order.yml +38 -38
  22. data/spec/fixtures/api_cassettes/cancel_order_not_allowed.yml +40 -40
  23. data/spec/fixtures/api_cassettes/get_attachment_content.yml +399 -399
  24. data/spec/fixtures/api_cassettes/get_attachment_content_as_pdf.yml +399 -399
  25. data/spec/fixtures/api_cassettes/get_attachment_content_as_text.yml +65 -65
  26. data/spec/fixtures/api_cassettes/get_attachment_content_as_youtube_transcript.yml +66 -66
  27. data/spec/fixtures/api_cassettes/get_attachment_content_unacceptable_representation.yml +42 -42
  28. data/spec/fixtures/api_cassettes/get_attachment_content_with_invalid_id.yml +42 -42
  29. data/spec/fixtures/api_cassettes/get_attachment_metadata.yml +42 -42
  30. data/spec/fixtures/api_cassettes/get_attachment_with_invalid_id.yml +40 -40
  31. data/spec/fixtures/api_cassettes/get_orders.yml +122 -122
  32. data/spec/fixtures/api_cassettes/get_orders_with_clientRef.yml +41 -41
  33. data/spec/fixtures/api_cassettes/get_tc_order.yml +44 -44
  34. data/spec/fixtures/api_cassettes/get_third_page_of_orders.yml +52 -58
  35. data/spec/fixtures/api_cassettes/link_input.yml +44 -44
  36. data/spec/fixtures/api_cassettes/link_input_with_all_attributes.yml +44 -44
  37. data/spec/fixtures/api_cassettes/link_input_with_spaces_in_filename.yml +45 -45
  38. data/spec/fixtures/api_cassettes/not_found_order.yml +42 -42
  39. data/spec/fixtures/api_cassettes/submit_cp_order.yml +45 -45
  40. data/spec/fixtures/api_cassettes/submit_su_order.yml +45 -45
  41. data/spec/fixtures/api_cassettes/submit_tc_order_with_account_balance.yml +45 -45
  42. data/spec/fixtures/api_cassettes/submit_tc_order_with_invalid_request.yml +45 -45
  43. data/spec/fixtures/api_cassettes/submit_tc_order_without_specifying_payment.yml +45 -45
  44. data/spec/fixtures/api_cassettes/unauthorized.yml +42 -42
  45. data/spec/fixtures/api_cassettes/upload_input.yml +90 -90
  46. data/spec/fixtures/api_cassettes/upload_input_with_invalid_content_type.yml +91 -91
  47. data/spec/lib/rev/api_spec.rb +24 -24
  48. data/spec/lib/rev/cancel_order_spec.rb +24 -24
  49. data/spec/lib/rev/exceptions_spec.rb +8 -8
  50. data/spec/lib/rev/get_attachment_content_spec.rb +79 -79
  51. data/spec/lib/rev/get_attachment_metadata_spec.rb +33 -33
  52. data/spec/lib/rev/get_order_spec.rb +52 -68
  53. data/spec/lib/rev/get_orders_spec.rb +62 -62
  54. data/spec/lib/rev/http_client_spec.rb +32 -32
  55. data/spec/lib/rev/models/order_request_spec.rb +0 -7
  56. data/spec/lib/rev/models/order_spec.rb +58 -58
  57. data/spec/lib/rev/post_inputs_spec.rb +94 -94
  58. data/spec/lib/rev/post_order_spec.rb +163 -195
  59. data/spec/spec_helper.rb +49 -49
  60. metadata +38 -41
  61. data/spec/fixtures/api_cassettes/get_tr_order.yml +0 -44
  62. data/spec/fixtures/api_cassettes/submit_tr_order.yml +0 -44
@@ -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
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
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
- # translation, 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
- # finished translation or a source file for an order.
115
- # For transcript and translation 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
- # finished translation or a source file for an order.
150
- # For transcript and translation 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
- # or translation 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