rev-api 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +20 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +3 -0
  6. data/Gemfile.lock +42 -0
  7. data/LICENSE +191 -0
  8. data/README.md +124 -0
  9. data/Rakefile +14 -0
  10. data/examples/cli.rb +200 -0
  11. data/lib/rev-api.rb +26 -0
  12. data/lib/rev-api/api.rb +311 -0
  13. data/lib/rev-api/api_serializable.rb +30 -0
  14. data/lib/rev-api/exceptions.rb +108 -0
  15. data/lib/rev-api/http_client.rb +97 -0
  16. data/lib/rev-api/models/order.rb +113 -0
  17. data/lib/rev-api/models/order_request.rb +183 -0
  18. data/lib/rev-api/version.rb +3 -0
  19. data/rev-api.gemspec +33 -0
  20. data/spec/fixtures/api_cassettes/cancel_order.yml +38 -0
  21. data/spec/fixtures/api_cassettes/cancel_order_not_allowed.yml +40 -0
  22. data/spec/fixtures/api_cassettes/get_attachment_content.yml +399 -0
  23. data/spec/fixtures/api_cassettes/get_attachment_content_as_pdf.yml +399 -0
  24. data/spec/fixtures/api_cassettes/get_attachment_content_as_text.yml +65 -0
  25. data/spec/fixtures/api_cassettes/get_attachment_content_as_youtube_transcript.yml +66 -0
  26. data/spec/fixtures/api_cassettes/get_attachment_content_unacceptable_representation.yml +42 -0
  27. data/spec/fixtures/api_cassettes/get_attachment_content_with_invalid_id.yml +42 -0
  28. data/spec/fixtures/api_cassettes/get_attachment_metadata.yml +42 -0
  29. data/spec/fixtures/api_cassettes/get_attachment_with_invalid_id.yml +40 -0
  30. data/spec/fixtures/api_cassettes/get_orders.yml +122 -0
  31. data/spec/fixtures/api_cassettes/get_tc_order.yml +44 -0
  32. data/spec/fixtures/api_cassettes/get_third_page_of_orders.yml +58 -0
  33. data/spec/fixtures/api_cassettes/get_tr_order.yml +44 -0
  34. data/spec/fixtures/api_cassettes/link_input.yml +44 -0
  35. data/spec/fixtures/api_cassettes/link_input_with_all_attributes.yml +44 -0
  36. data/spec/fixtures/api_cassettes/not_found_order.yml +42 -0
  37. data/spec/fixtures/api_cassettes/submit_tc_order_with_account_balance.yml +45 -0
  38. data/spec/fixtures/api_cassettes/submit_tc_order_with_cc_and_all_attributes.yml +46 -0
  39. data/spec/fixtures/api_cassettes/submit_tc_order_with_invalid_request.yml +45 -0
  40. data/spec/fixtures/api_cassettes/submit_tc_order_with_saved_cc.yml +45 -0
  41. data/spec/fixtures/api_cassettes/submit_tr_order.yml +44 -0
  42. data/spec/fixtures/api_cassettes/unauthorized.yml +42 -0
  43. data/spec/fixtures/api_cassettes/upload_input.yml +130 -0
  44. data/spec/fixtures/api_cassettes/upload_input_with_invalid_content_type.yml +131 -0
  45. data/spec/fixtures/sourcedocument.png +0 -0
  46. data/spec/lib/rev/api_spec.rb +24 -0
  47. data/spec/lib/rev/cancel_order_spec.rb +25 -0
  48. data/spec/lib/rev/get_attachment_content_spec.rb +79 -0
  49. data/spec/lib/rev/get_attachment_metadata_spec.rb +33 -0
  50. data/spec/lib/rev/get_order_spec.rb +68 -0
  51. data/spec/lib/rev/get_orders_spec.rb +39 -0
  52. data/spec/lib/rev/http_client_spec.rb +32 -0
  53. data/spec/lib/rev/post_inputs_spec.rb +75 -0
  54. data/spec/lib/rev/post_order_spec.rb +207 -0
  55. data/spec/spec_helper.rb +31 -0
  56. metadata +248 -0
@@ -0,0 +1,68 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe 'GET /orders/{order_num}' do
4
+ let(:client) { Rev.new('welcome', 'AAAAAu/YjZ3phXU5FsF35yIcgiA=', 'www.revtrunk.com') }
5
+
6
+ describe 'Transcription' do
7
+ before do
8
+ VCR.insert_cassette 'get_tc_order'
9
+ end
10
+
11
+ it 'must get an order by given order number' do
12
+ client.get_order('TC0233908691').wont_be_nil
13
+
14
+ assert_requested :get, /.*\/api\/v1\/orders\/TC0233908691/, :times => 1
15
+ end
16
+
17
+ describe 'loaded order' do
18
+ let(:order) { client.get_order('TC0233908691') }
19
+
20
+ it 'must have basic attributes' do
21
+ order.order_number.must_equal 'TC0233908691'
22
+ order.price.must_equal 10.0
23
+ order.status.must_equal 'Finding Transcriptionist'
24
+ order.client_ref.must_equal 'XC123'
25
+ end
26
+
27
+ it 'must have comments' do
28
+ order.comments.size.must_equal 1
29
+ order.comments.first.by.must_equal 'Admin Admin'
30
+ order.comments.first.text.must_be_empty
31
+ order.comments.first.timestamp.day.must_equal 6
32
+ order.comments.first.timestamp.month.must_equal 9
33
+ order.comments.first.timestamp.year.must_equal 2013
34
+ end
35
+
36
+ it 'must have attachments' do
37
+ order.attachments.size.must_equal 1
38
+ order.attachments.first.kind.must_equal 'media'
39
+ end
40
+
41
+ it 'must have transcription info' do
42
+ order.transcription.total_length.must_equal 10
43
+ order.transcription.verbatim.must_equal false
44
+ order.transcription.timestamps.must_equal false
45
+ end
46
+ end
47
+ end
48
+
49
+ describe 'Translation' do
50
+ before do
51
+ VCR.insert_cassette 'get_tr_order'
52
+ end
53
+
54
+ describe 'loaded order' do
55
+ let(:order) { client.get_order('TR0116711100') }
56
+
57
+ it 'must have translation info' do
58
+ order.translation.total_word_count.must_equal 2
59
+ order.translation.source_language_code.must_equal 'cs'
60
+ order.translation.destination_language_code.must_equal 'en'
61
+ end
62
+ end
63
+ end
64
+
65
+ after do
66
+ VCR.eject_cassette
67
+ end
68
+ end
@@ -0,0 +1,39 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe 'GET /orders' do
4
+ let(:client) { Rev.new('welcome', 'AAAAAu/YjZ3phXU5FsF35yIcgiA=', 'www.revtrunk.com') }
5
+
6
+ describe 'GET /orders without page number' do
7
+ it 'must get first page of existing orders' do
8
+ VCR.insert_cassette 'get_orders'
9
+
10
+ page = client.get_orders_page
11
+
12
+ assert_requested :get, /.*\/api\/v1\/orders\?page=0/, :times => 1
13
+
14
+ page.orders.must_be_instance_of Array
15
+ page.results_per_page.must_equal 8
16
+ page.orders.size.must_equal 8
17
+ page.page.must_equal 0
18
+ page.total_count.must_equal 77
19
+ end
20
+ end
21
+
22
+ describe 'GET /orders?page={pagenum}' do
23
+ it 'must load any page' do
24
+ VCR.insert_cassette 'get_third_page_of_orders'
25
+
26
+ page = client.get_orders_page(2)
27
+
28
+ assert_requested :get, /.*\/api\/v1\/orders\?page=2/, :times => 1
29
+
30
+ page.orders.size.must_equal 8
31
+ page.page.must_equal 2
32
+ page.orders.first.order_number.must_equal 'TC0229215557'
33
+ end
34
+ end
35
+
36
+ after do
37
+ VCR.eject_cassette
38
+ end
39
+ end
@@ -0,0 +1,32 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe Rev::HttpClient do
4
+ it 'must support predefined production host' do
5
+ client = Rev::HttpClient.new('foo', 'bar', Rev::Api::PRODUCTION_HOST)
6
+ Rev::HttpClient.base_uri.must_equal 'https://www.rev.com/api/v1'
7
+ end
8
+
9
+ it 'must support predefined sandbox host' do
10
+ client = Rev::HttpClient.new('foo', 'bar', Rev::Api::SANDBOX_HOST)
11
+ Rev::HttpClient.base_uri.must_equal 'https://api-sandbox.rev.com/api/v1'
12
+ end
13
+
14
+ it 'must support custom host for development purposes' do
15
+ client = Rev::HttpClient.new('foo', 'bar', 'localhost')
16
+ Rev::HttpClient.base_uri.must_equal 'https://localhost/api/v1'
17
+ end
18
+
19
+ it 'must include authorization and User-Agent headers for any request' do
20
+ host = 'www.example.com'
21
+ stub_request(:any, host)
22
+
23
+ client = Rev::HttpClient.new('foo', 'bar', host)
24
+ client.get('/orders')
25
+
26
+ assert_requested :get, "https://#{host}/api/v1/orders", :headers => {
27
+ 'Authorization' => "Rev foo:bar",
28
+ 'User-Agent' => Rev::HttpClient::USER_AGENT
29
+ }
30
+ end
31
+ end
32
+
@@ -0,0 +1,75 @@
1
+ require_relative '../../spec_helper'
2
+ require 'base64'
3
+
4
+ describe 'POST /inputs' do
5
+ let(:client) { Rev.new('welcome', 'AAAAAu/YjZ3phXU5FsF35yIcgiA=', 'www.revtrunk.com') }
6
+
7
+ it 'must link external file with explicit content-type and file' do
8
+ VCR.insert_cassette 'link_input_with_all_attributes'
9
+
10
+ link = 'http://www.rev.com/content/img/rev/rev_logo_colored_top.png'
11
+ filename = 'sourcedocument.png'
12
+ content_type = 'image/png'
13
+ new_input_location = client.create_input_from_link(link, filename, content_type)
14
+
15
+ new_input_location.must_match 'urn:foxtranslate:inputmedia:'
16
+ expected_body = {
17
+ 'url' => link,
18
+ 'filename' => filename,
19
+ 'content_type' => content_type
20
+ }
21
+ assert_requested(:post, /.*\/inputs/, :times => 1) do |req|
22
+ req.headers['Content-Type'] == 'application/json'
23
+ actual_body = JSON.load req.body
24
+ actual_body.must_equal expected_body
25
+ end
26
+ end
27
+
28
+ it 'must link external file without content-type and filename' do
29
+ VCR.insert_cassette 'link_input'
30
+
31
+ link = 'http://www.rev.com/content/img/rev/rev_logo_colored_top.png'
32
+ new_input_location = client.create_input_from_link(link)
33
+
34
+ new_input_location.must_match 'urn:foxtranslate:inputmedia:'
35
+ expected_body = { 'url' => link }
36
+ assert_requested(:post, /.*\/inputs/, :times => 1) do |req|
37
+ req.headers['Content-Type'] == 'application/json'
38
+ actual_body = JSON.load req.body
39
+ actual_body.must_equal expected_body
40
+ end
41
+ end
42
+
43
+ it 'must upload source file directly' do
44
+ VCR.insert_cassette 'upload_input'
45
+
46
+ filename = './spec/fixtures/sourcedocument.png'
47
+ content_type = 'image/png'
48
+
49
+ new_input_location = client.upload_input(filename, content_type)
50
+
51
+ new_input_location.must_match 'urn:foxtranslate:inputmedia:'
52
+ expected_body = File.read(filename)
53
+ assert_requested(:post, /.*\/inputs/, :times => 1) do |req|
54
+ req.headers['Content-Type'] == content_type
55
+ req.headers['Content-Disposition'] == 'attachment; filename="sourcedocument.png'
56
+ req.body.must_equal expected_body
57
+ end
58
+ end
59
+
60
+ it 'must raise BadRequestError on failure' do
61
+ VCR.insert_cassette 'upload_input_with_invalid_content_type'
62
+
63
+ filename = './spec/fixtures/sourcedocument.png'
64
+ content_type = 'trololo'
65
+
66
+ action = lambda { client.upload_input(filename, content_type) }
67
+ exception = action.must_raise Rev::BadRequestError
68
+ exception.message.must_match '10001: The content-type explicitly specified in the request is not supported for input media'
69
+ exception.code.must_equal Rev::InputRequestErrorCodes::UNSUPPORTED_CONTENT_TYPE
70
+ end
71
+
72
+ after do
73
+ VCR.eject_cassette
74
+ end
75
+ end
@@ -0,0 +1,207 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe 'POST /orders' do
4
+ let(:client) { Rev.new('welcome', 'AAAAAu/YjZ3phXU5FsF35yIcgiA=', 'www.revtrunk.com') }
5
+
6
+ # some defaults we use often
7
+ let(:billing_address) { Rev::BillingAddress.new(
8
+ :street => '123 Pine Lane',
9
+ :street2 => 'Apt D',
10
+ :city => 'MyTown',
11
+ :state => 'MN',
12
+ :zip => '12345',
13
+ :country_alpha2 => 'US'
14
+ )}
15
+
16
+ let(:credit_card) { Rev::CreditCard.new(
17
+ :number => '4111111111111111',
18
+ :expiration_month => 9,
19
+ :expiration_year => 2023,
20
+ :cardholder => 'Joe Smith',
21
+ :billing_address => billing_address
22
+ )}
23
+ let(:cc_payment) { Rev::Payment.new(Rev::Payment::TYPES[:credit_card], credit_card) }
24
+ let(:saved_cc_payment) { Rev::Payment.new(Rev::Payment::TYPES[:credit_card], :saved_id => 1) }
25
+ let(:balance_payment) { Rev::Payment.new(Rev::Payment::TYPES[:balance]) }
26
+ let(:transcription_inputs) {
27
+ inputs = []
28
+ inputs << Rev::Input.new(:external_link => 'http://www.youtube.com/watch?v=UF8uR6Z6KLc')
29
+ inputs << Rev::Input.new(:audio_length => 15, :external_link => 'https://vimeo.com/7976699')
30
+ }
31
+ let(:translation_inputs) {
32
+ inputs = []
33
+ inputs << Rev::Input.new(:word_length => 1000, :uri => 'urn:foxtranslate:inputmedia:SnVwbG9hZHMvMjAxMy0wOS0xNy9lMzk4MWIzNS0wNzM1LTRlMDAtODY1NC1jNWY4ZjE4MzdlMTIvc291cmNlZG9jdW1lbnQucG5n')
34
+ }
35
+ let(:transcription_options) { Rev::TranscriptionOptions.new(transcription_inputs,
36
+ :verbatim => true, :timestamps => true) }
37
+ let(:translation_options) { Rev::TranslationOptions.new(translation_inputs,
38
+ :source_language_code => 'es', :destination_language_code => 'en') }
39
+
40
+ it 'must submit order using Credit Card including all attributes' do
41
+ VCR.insert_cassette 'submit_tc_order_with_cc_and_all_attributes'
42
+
43
+ request = Rev::OrderRequest.new(
44
+ cc_payment,
45
+ :transcription_options => transcription_options,
46
+ :client_ref => 'XB432423',
47
+ :comment => 'Please work quickly',
48
+ :notification => Rev::Notification.new('http://www.example.com', Rev::Notification::LEVELS[:detailed])
49
+ )
50
+
51
+ new_order_num = client.submit_order(request)
52
+
53
+ new_order_num.must_equal 'TC0520815415'
54
+ expected_body = {
55
+ 'payment' => {
56
+ 'type' => 'CreditCard',
57
+ 'credit_card' => {
58
+ 'number' => '4111111111111111',
59
+ 'expiration_month' => 9,
60
+ 'expiration_year' => 2023,
61
+ 'cardholder' => 'Joe Smith',
62
+ 'billing_address' => {
63
+ 'street' => '123 Pine Lane',
64
+ 'street2' => 'Apt D',
65
+ 'city' => 'MyTown',
66
+ 'state' => 'MN',
67
+ 'zip' => '12345',
68
+ 'country_alpha2' => 'US'
69
+ }
70
+ }
71
+ },
72
+ 'transcription_options' => {
73
+ 'inputs'=> [
74
+ { 'external_link' => 'http://www.youtube.com/watch?v=UF8uR6Z6KLc' },
75
+ { 'audio_length' => 15, 'external_link' => 'https://vimeo.com/7976699' }
76
+ ],
77
+ 'verbatim' => true,
78
+ 'timestamps' => true
79
+ },
80
+ 'client_ref' => 'XB432423',
81
+ 'comment' => 'Please work quickly',
82
+ 'notification' => {
83
+ 'url' => 'http://www.example.com',
84
+ 'level' => 'Detailed'
85
+ }
86
+ }
87
+ assert_requested(:post, /.*\/orders/, :times => 1) do |req|
88
+ req.headers['Content-Type'] == 'application/json'
89
+ actual_body = JSON.load req.body
90
+ actual_body.must_equal expected_body
91
+ end
92
+ end
93
+
94
+ it 'must place order using saved credit card' do
95
+ VCR.insert_cassette 'submit_tc_order_with_saved_cc'
96
+
97
+ request = Rev::OrderRequest.new(
98
+ saved_cc_payment,
99
+ :transcription_options => transcription_options
100
+ )
101
+
102
+ new_order_num = client.submit_order(request)
103
+
104
+ new_order_num.must_equal 'TC0370955571'
105
+ expected_body = {
106
+ 'payment' => {
107
+ 'type' => 'CreditCard',
108
+ 'credit_card' => {
109
+ 'saved_id' => 1
110
+ }
111
+ },
112
+ 'transcription_options' => {
113
+ 'inputs' => [
114
+ { 'external_link' => 'http://www.youtube.com/watch?v=UF8uR6Z6KLc' },
115
+ { 'external_link' => 'https://vimeo.com/7976699', 'audio_length'=>15 }
116
+ ],
117
+ 'verbatim'=>true,
118
+ 'timestamps'=>true
119
+ }
120
+ }
121
+ assert_requested(:post, /.*\/orders/, :times => 1) do |req|
122
+ req.headers['Content-Type'] == 'application/json'
123
+ actual_body = JSON.load req.body
124
+ actual_body.must_equal expected_body
125
+ end
126
+ end
127
+
128
+ it 'must place order using account balance' do
129
+ VCR.insert_cassette 'submit_tc_order_with_account_balance'
130
+
131
+ request = Rev::OrderRequest.new(
132
+ balance_payment,
133
+ :transcription_options => transcription_options
134
+ )
135
+
136
+ new_order_num = client.submit_order(request)
137
+
138
+ new_order_num.must_equal 'TC0406615008'
139
+ expected_body = {
140
+ 'payment' => {
141
+ 'type' => 'AccountBalance'
142
+ },
143
+ 'transcription_options' => {
144
+ 'inputs' => [
145
+ { 'external_link' => 'http://www.youtube.com/watch?v=UF8uR6Z6KLc' },
146
+ { 'external_link' => 'https://vimeo.com/7976699', 'audio_length' => 15 }
147
+ ],
148
+ 'verbatim' => true,
149
+ 'timestamps' => true
150
+ }
151
+ }
152
+ assert_requested(:post, /.*\/orders/, :times => 1) do |req|
153
+ req.headers['Content-Type'] == 'application/json'
154
+ actual_body = JSON.load req.body
155
+ actual_body.must_equal expected_body
156
+ end
157
+ end
158
+
159
+ it 'must raise BadRequest error in case of request validation failure' do
160
+ VCR.insert_cassette 'submit_tc_order_with_invalid_request'
161
+
162
+ # example - missing transcription options
163
+ request = Rev::OrderRequest.new(
164
+ balance_payment
165
+ )
166
+
167
+ action = lambda { client.submit_order(request) }
168
+ exception = action.must_raise Rev::BadRequestError
169
+ exception.message.must_match '10004: You must specify either translation or transcription options in an order'
170
+ exception.code.must_equal Rev::OrderRequestErrorCodes::TC_OR_TR_OPTIONS_NOT_SPECIFIED
171
+ end
172
+
173
+ it 'must submit translation order with translation options' do
174
+ VCR.insert_cassette 'submit_tr_order'
175
+
176
+ request = Rev::OrderRequest.new(
177
+ balance_payment,
178
+ :translation_options => translation_options
179
+ )
180
+
181
+ new_order_num = client.submit_order(request)
182
+
183
+ new_order_num.must_equal 'TR0235803277'
184
+ expected_body = {
185
+ 'payment' => {
186
+ 'type' => 'AccountBalance'
187
+ },
188
+ 'translation_options' => {
189
+ 'inputs'=> [
190
+ { 'word_length' => 1000, 'uri' => 'urn:foxtranslate:inputmedia:SnVwbG9hZHMvMjAxMy0wOS0xNy9lMzk4MWIzNS0wNzM1LTRlMDAtODY1NC1jNWY4ZjE4MzdlMTIvc291cmNlZG9jdW1lbnQucG5n' },
191
+ ],
192
+ 'source_language_code' => 'es',
193
+ 'destination_language_code' => 'en'
194
+ }
195
+ }
196
+ assert_requested(:post, /.*\/orders/, :times => 1) do |req|
197
+ req.headers['Content-Type'] == 'application/json'
198
+ actual_body = JSON.load req.body
199
+ actual_body.must_equal expected_body
200
+ end
201
+ end
202
+
203
+ after do
204
+ VCR.eject_cassette
205
+ end
206
+
207
+ end
@@ -0,0 +1,31 @@
1
+ require_relative '../lib/rev-api'
2
+
3
+ #dependencies
4
+ require 'minitest/autorun'
5
+ require 'webmock/minitest'
6
+ require 'vcr'
7
+ require 'turn'
8
+
9
+ module MiniTest
10
+ class Spec
11
+ class << self
12
+ def xit(desc='anonymous')
13
+ it(name) { skip 'DISABLED' }
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ Turn.config do |c|
20
+ c.format = :outline
21
+ c.trace = false
22
+ c.natural = true
23
+ end
24
+
25
+
26
+ VCR.configure do |c|
27
+ c.cassette_library_dir = 'spec/fixtures/api_cassettes'
28
+ c.default_cassette_options = { :record => :once, :allow_unused_http_interactions => false, :match_requests_on => [:method, :uri, :headers] }
29
+ c.hook_into :webmock
30
+ c.ignore_hosts 'www.example.com' # used to stub requests manually, see http_client_spec
31
+ end