abbyy-cloud 0.0.5 → 0.0.6

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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -13
  3. data/.travis.yml +1 -1
  4. data/CHANGELOG.md +11 -0
  5. data/README.md +29 -0
  6. data/abbyy-cloud.gemspec +4 -2
  7. data/lib/abbyy/cloud.rb +14 -26
  8. data/lib/abbyy/cloud/connection.rb +3 -10
  9. data/lib/abbyy/cloud/exceptions/response_error.rb +1 -1
  10. data/lib/abbyy/cloud/models/cost_type.rb +14 -0
  11. data/lib/abbyy/cloud/models/currency.rb +14 -0
  12. data/lib/abbyy/cloud/models/direction.rb +0 -1
  13. data/lib/abbyy/cloud/models/discount.rb +2 -1
  14. data/lib/abbyy/cloud/models/discount_type.rb +19 -0
  15. data/lib/abbyy/cloud/models/engine.rb +0 -2
  16. data/lib/abbyy/cloud/models/error.rb +0 -1
  17. data/lib/abbyy/cloud/models/file_format.rb +19 -0
  18. data/lib/abbyy/cloud/models/file_info.rb +33 -0
  19. data/lib/abbyy/cloud/models/file_link.rb +18 -0
  20. data/lib/abbyy/cloud/models/file_read_error.rb +20 -0
  21. data/lib/abbyy/cloud/models/file_read_status.rb +14 -0
  22. data/lib/abbyy/cloud/models/file_reference.rb +11 -0
  23. data/lib/abbyy/cloud/models/full_order.rb +46 -0
  24. data/lib/abbyy/cloud/models/label.rb +14 -0
  25. data/lib/abbyy/cloud/models/ocr_mode.rb +19 -0
  26. data/lib/abbyy/cloud/models/ocr_quality.rb +18 -0
  27. data/lib/abbyy/cloud/models/ocr_settings.rb +16 -0
  28. data/lib/abbyy/cloud/models/ocr_statistics.rb +13 -0
  29. data/lib/abbyy/cloud/models/ocr_warning.rb +16 -0
  30. data/lib/abbyy/cloud/models/ocr_warning_type.rb +29 -0
  31. data/lib/abbyy/cloud/models/order_statistics.rb +15 -0
  32. data/lib/abbyy/cloud/models/payment_type.rb +14 -0
  33. data/lib/abbyy/cloud/models/price.rb +1 -1
  34. data/lib/abbyy/cloud/models/reading_status.rb +20 -0
  35. data/lib/abbyy/cloud/models/source_segment.rb +0 -1
  36. data/lib/abbyy/cloud/models/source_tag.rb +0 -1
  37. data/lib/abbyy/cloud/models/status.rb +25 -0
  38. data/lib/abbyy/cloud/models/text_statistics.rb +15 -0
  39. data/lib/abbyy/cloud/models/transfer_data.rb +0 -3
  40. data/lib/abbyy/cloud/models/translation.rb +0 -2
  41. data/lib/abbyy/cloud/models/translation_link.rb +18 -0
  42. data/lib/abbyy/cloud/models/translation_segment.rb +0 -1
  43. data/lib/abbyy/cloud/models/translation_type.rb +14 -0
  44. data/lib/abbyy/cloud/models/unit_price.rb +3 -0
  45. data/lib/abbyy/cloud/models/unit_type.rb +14 -0
  46. data/lib/abbyy/cloud/namespaces/files.rb +19 -0
  47. data/lib/abbyy/cloud/namespaces/orders.rb +17 -0
  48. data/lib/abbyy/cloud/namespaces/prices.rb +1 -1
  49. data/lib/abbyy/cloud/operations/base.rb +56 -19
  50. data/lib/abbyy/cloud/operations/download_file.rb +12 -0
  51. data/lib/abbyy/cloud/operations/engines.rb +1 -1
  52. data/lib/abbyy/cloud/operations/find_order.rb +13 -0
  53. data/lib/abbyy/cloud/operations/prices.rb +1 -1
  54. data/lib/abbyy/cloud/operations/submit_order.rb +32 -0
  55. data/lib/abbyy/cloud/operations/translate.rb +1 -1
  56. data/lib/abbyy/cloud/operations/translate_segments.rb +1 -1
  57. data/lib/abbyy/cloud/operations/upload_file.rb +22 -0
  58. data/lib/abbyy/cloud/types.rb +8 -17
  59. data/spec/feature/abbyy/files_download_spec.rb +57 -0
  60. data/spec/feature/abbyy/files_upload_spec.rb +129 -0
  61. data/spec/feature/abbyy/orders_create_spec.rb +245 -0
  62. data/spec/feature/abbyy/orders_find_spec.rb +116 -0
  63. data/spec/{abbyy → unit/abbyy}/cloud/connection_spec.rb +1 -1
  64. data/spec/{abbyy → unit/abbyy}/cloud/models/direction_spec.rb +0 -0
  65. data/spec/{abbyy → unit/abbyy}/cloud/models/discount_spec.rb +0 -0
  66. data/spec/{abbyy → unit/abbyy}/cloud/models/engine_spec.rb +0 -0
  67. data/spec/{abbyy → unit/abbyy}/cloud/models/error_spec.rb +0 -0
  68. data/spec/unit/abbyy/cloud/models/file_link_spec.rb +51 -0
  69. data/spec/unit/abbyy/cloud/models/file_reference_spec.rb +24 -0
  70. data/spec/unit/abbyy/cloud/models/full_order_spec.rb +192 -0
  71. data/spec/{abbyy → unit/abbyy}/cloud/models/locale_spec.rb +0 -0
  72. data/spec/unit/abbyy/cloud/models/order_statistics_spec.rb +15 -0
  73. data/spec/{abbyy → unit/abbyy}/cloud/models/price_spec.rb +0 -8
  74. data/spec/{abbyy → unit/abbyy}/cloud/models/source_segment_spec.rb +0 -0
  75. data/spec/{abbyy → unit/abbyy}/cloud/models/source_tag_spec.rb +0 -0
  76. data/spec/{abbyy → unit/abbyy}/cloud/models/transfer_data_spec.rb +0 -0
  77. data/spec/unit/abbyy/cloud/models/translation_link_spec.rb +66 -0
  78. data/spec/{abbyy → unit/abbyy}/cloud/models/translation_segment_spec.rb +0 -0
  79. data/spec/{abbyy → unit/abbyy}/cloud/models/translation_spec.rb +0 -0
  80. data/spec/{abbyy → unit/abbyy}/cloud/models/unit_price_spec.rb +0 -0
  81. data/spec/{abbyy → unit/abbyy}/cloud/response_error_spec.rb +0 -0
  82. data/spec/{abbyy → unit/abbyy}/cloud/settings_spec.rb +0 -0
  83. data/spec/{abbyy → unit/abbyy}/cloud_spec.rb +0 -0
  84. metadata +114 -37
@@ -21,7 +21,7 @@ class ABBYY::Cloud
21
21
  items = Operations::Prices.new(settings)
22
22
  .call(skip: skip, take: take_now, **opts)
23
23
 
24
- return items if (items.count < take_now) || take_later.zero?
24
+ return items if (items.count < take_now) || take_later&.zero?
25
25
  items + details(skip: skip_later, take: take_later, **opts)
26
26
  end
27
27
  end
@@ -6,7 +6,6 @@
6
6
  class ABBYY::Cloud
7
7
  module Operations
8
8
  class Base
9
- # Helpers to specify concrete operations
10
9
  class << self
11
10
  include Forwardable
12
11
 
@@ -15,14 +14,18 @@ class ABBYY::Cloud
15
14
  @http_method || "Post"
16
15
  end
17
16
 
18
- def path(value = nil)
19
- value ? @path = value : @path
17
+ def path(&block)
18
+ block ? @path = block : @path
20
19
  end
21
20
 
22
21
  def link(value = nil)
23
22
  value ? @link = value : @link
24
23
  end
25
24
 
25
+ def request_type(value = nil)
26
+ value ? (@request_type = value) : (@request_type || :json)
27
+ end
28
+
26
29
  def request_body(struct = nil, &block)
27
30
  provide_struct :@request_body, struct, &block
28
31
  end
@@ -31,6 +34,10 @@ class ABBYY::Cloud
31
34
  provide_struct :@request_query, struct, &block
32
35
  end
33
36
 
37
+ def response_type(value = nil)
38
+ value ? (@response_type = value) : (@response_type || :json)
39
+ end
40
+
34
41
  def response_body(struct = nil, &block)
35
42
  provide_struct :@response_body, struct, &block
36
43
  end
@@ -54,25 +61,39 @@ class ABBYY::Cloud
54
61
 
55
62
  def_delegators :settings, :connection
56
63
  def_delegators :"self.class",
57
- :link,
58
- :http_method,
59
- :path,
60
- :request_body,
61
- :request_query,
62
- :response_body
63
-
64
- def call(**data)
65
- body = prepare_request_body(data)
66
- query = prepare_request_query(data)
67
- res = connection.call(http_method, path, body: body, query: query)
68
-
69
- handle_response_body(res)
64
+ :link, :http_method, :path, :request_type, :request_body,
65
+ :request_query, :response_type, :response_body
66
+
67
+ def call(file = nil, **data)
68
+ mash = Hashie::Mash.new(data)
69
+ url = mash.instance_eval(&path)
70
+ multipart = prepare_multipart(file, **data) if file
71
+ body = prepare_request_body(multipart, data)
72
+ query = prepare_request_query(data)
73
+ headers = prepare_request_headers(multipart)
74
+ res = connection.call http_method, url, body: body,
75
+ query: query,
76
+ headers: headers
77
+ handle_response_body res
70
78
  end
71
79
 
72
80
  private
73
81
 
74
- def prepare_request_body(data)
75
- request_body[data]
82
+ def prepare_multipart(file, content_type: "plain/text", **)
83
+ name = SecureRandom.hex(10)
84
+ data = file.read if file.respond_to?(:read)
85
+ path = Pathname.new(file.path).basename if file.respond_to?(:path)
86
+ part = Part.new(name, data || file, path || "file")
87
+ part.content_type = content_type
88
+ MultipartBody.new [part]
89
+ end
90
+
91
+ def prepare_request_body(multipart, data)
92
+ case request_type
93
+ when :json then JSON(request_body[data].to_h)
94
+ when :file then multipart.to_s << "\r\n"
95
+ else data
96
+ end
76
97
  rescue => error
77
98
  raise ArgumentError.new(link, data, error.message)
78
99
  end
@@ -83,8 +104,24 @@ class ABBYY::Cloud
83
104
  raise ArgumentError.new(link, data, error.message)
84
105
  end
85
106
 
107
+ def prepare_request_headers(multipart)
108
+ headers = {}
109
+ headers["accept"] = "application/json" if response_type == :json
110
+ headers["content-type"] = \
111
+ case request_type
112
+ when :json then "application/json"
113
+ when :file then "multipart/form-data; boundary=#{multipart.boundary}"
114
+ end
115
+
116
+ headers
117
+ end
118
+
86
119
  def handle_response_body(data)
87
- response_body[data]
120
+ case response_type
121
+ when :json then response_body[JSON.parse(data)]
122
+ when :file then StringIO.new(data)
123
+ else data
124
+ end
88
125
  rescue => error
89
126
  raise TypeError.new(link, data, error.message)
90
127
  end
@@ -0,0 +1,12 @@
1
+ require_relative "base"
2
+
3
+ class ABBYY::Cloud
4
+ module Operations
5
+ class DownloadFile < Base
6
+ link "https://api.abbyy.cloud/swagger/ui/index#!/File/File_DownloadFile"
7
+ path { "v0/file/#{id}/#{token}" }
8
+ http_method "get"
9
+ response_type :file
10
+ end
11
+ end
12
+ end
@@ -6,7 +6,7 @@ class ABBYY::Cloud
6
6
  # rubocop: disable Metrics/LineLength
7
7
  link "https://api.abbyy.cloud/swagger/ui/index#!/MachineTranslation/MachineTranslation_Engines"
8
8
  # rubocop: enable Metrics/LineLength
9
- path "v0/mt/engines"
9
+ path { "v0/mt/engines" }
10
10
  http_method "get"
11
11
 
12
12
  response_body Types::Array.member(Types::Engine)
@@ -0,0 +1,13 @@
1
+ require_relative "base"
2
+
3
+ class ABBYY::Cloud
4
+ module Operations
5
+ class FindOrder < Base
6
+ link "https://api.abbyy.cloud/swagger/ui/index#!/Order/Order_GetOrder"
7
+ path { "v0/order/#{id}" }
8
+ http_method "get"
9
+
10
+ response_body Models::FullOrder
11
+ end
12
+ end
13
+ end
@@ -6,7 +6,7 @@ class ABBYY::Cloud
6
6
  # rubocop: disable Metrics/LineLength
7
7
  link "https://api.abbyy.cloud/swagger/ui/index#!/Prices/Prices_GetAccountPrices"
8
8
  # rubocop: enable Metrics/LineLength
9
- path "v0/prices/details"
9
+ path { "v0/prices/details" }
10
10
  http_method "get"
11
11
 
12
12
  request_query do
@@ -0,0 +1,32 @@
1
+ require_relative "base"
2
+
3
+ class ABBYY::Cloud
4
+ module Operations
5
+ class SubmitOrder < Base
6
+ # rubocop: disable Metrics/LineLength
7
+ link "https://api.abbyy.cloud/swagger/ui/index#!/Order/Order_SubmitOrder_0"
8
+ # rubocop: enable Metrics/LineLength
9
+ path { "v0/order" }
10
+ http_method "post"
11
+
12
+ request_body do
13
+ attribute :type, Types::TranslationType
14
+ attribute :email, Types::Strict::String.optional
15
+ attribute :contact_culture, Types::Strict::String.optional
16
+ attribute :contact_utc_offset, Types::Coercible::String.optional
17
+ attribute :mt_engine, Types::Strict::String
18
+ attribute :approval_required, Types::Form::Bool
19
+ attribute :is_manual_estimation, Types::Form::Bool
20
+ attribute :cost_type, Types::CostType
21
+ attribute :unit_type, Types::UnitType
22
+ attribute :currency, Types::Currency
23
+ attribute :from, Types::Locale
24
+ attribute :label, Types::Label.optional
25
+ attribute :to, Types::FilledArray.member(Types::Locale)
26
+ attribute :files, Types::FilledArray.member(Types::FileReference)
27
+ end
28
+
29
+ response_body Models::FullOrder
30
+ end
31
+ end
32
+ end
@@ -4,7 +4,7 @@ class ABBYY::Cloud
4
4
  module Operations
5
5
  class Translate < Base
6
6
  link "https://api.abbyy.cloud/swagger/ui/index#!/Order/Order_Translate"
7
- path "v0/order/mt/sync"
7
+ path { "v0/order/mt/sync" }
8
8
  http_method "post"
9
9
 
10
10
  request_body do
@@ -6,7 +6,7 @@ class ABBYY::Cloud
6
6
  # rubocop: disable Metrics/LineLength
7
7
  link "https://api.abbyy.cloud/swagger/ui/index#!/Order/Order_TranslateSegments"
8
8
  # rubocop: enable Metrics/LineLength
9
- path "v1/order/mt/sync"
9
+ path { "v1/order/mt/sync" }
10
10
  http_method "post"
11
11
 
12
12
  request_body do
@@ -0,0 +1,22 @@
1
+ require_relative "base"
2
+
3
+ class ABBYY::Cloud
4
+ module Operations
5
+ class UploadFile < Base
6
+ link "https://api.abbyy.cloud/swagger/ui/index#!/File/File_UploadFile"
7
+ path { "v0/file" }
8
+ http_method "post"
9
+
10
+ request_type :file
11
+
12
+ request_query do
13
+ attribute :quality, Types::OcrQuality.optional
14
+ attribute :synthesisMode, Types::OcrMode.optional
15
+ attribute :exportFormat, Types::FileFormat.optional
16
+ attribute :languages, Types::Array.member(Types::Locale).optional
17
+ end
18
+
19
+ response_body Types::Array.member(Types::FileInfo)
20
+ end
21
+ end
22
+ end
@@ -1,26 +1,17 @@
1
1
  module ABBYY::Cloud::Types
2
2
  include Dry::Types.module
3
3
 
4
- VERSIONS = [0].freeze
5
- UNIT_TYPES = %w(Chars Words Pages Documents).freeze
6
- DISCOUNT_TYPES = \
7
- %w(TMTextMatch TMTaggedTextMatch TMHalfContextMatch TMFullContextMatch)
8
- .freeze
9
-
10
4
  # Gem-specific primitive types
11
- Version = Coercible::Int.constrained(included_in: VERSIONS)
12
- UnitType = Strict::String.constrained(included_in: UNIT_TYPES)
13
- DiscountType = Strict::String.constrained(included_in: DISCOUNT_TYPES)
14
- Currency = Strict::String.constrained(format: /\A[A-Z]{3}\z/)
5
+ FilledArray = Array.constrained(min_size: 1)
15
6
 
16
- # Registers new coercible type from a struct class
17
- def self.register_type(struct, as: nil, constructor: :new)
18
- type_name = Inflecto.underscore(as || struct.name.split("::").last)
19
- definition = Dry::Types::Definition.new(struct).constructor do |value|
7
+ # Registers new coercible type
8
+ def self.register_type(klass, as: nil, constructor: :new)
9
+ type_name = Inflecto.underscore(as || klass.name.split("::").last)
10
+ definition = Dry::Types::Definition.new(klass).constructor do |value|
20
11
  case value
21
- when nil then raise
22
- when struct then value
23
- else struct.send(constructor, value)
12
+ when nil then raise
13
+ when klass then value
14
+ else klass.send(constructor, value)
24
15
  end
25
16
  end
26
17
 
@@ -0,0 +1,57 @@
1
+ RSpec.describe "files.download" do
2
+ subject { client.files.download(options) }
3
+
4
+ let(:client) { ABBYY::Cloud.new(id: "foo", token: "bar") }
5
+ let(:path) { "https://api.abbyy.cloud/v0/file/baz/qux" }
6
+ let(:options) { { id: "baz", token: "qux" } }
7
+
8
+ before do
9
+ stub_request(:get, path)
10
+ .with(basic_auth: %w(foo bar))
11
+ .to_return(status: 200, body: "Hello world!")
12
+ end
13
+
14
+ context "with valid params" do
15
+ it "sends a request to ABBYY Cloud API" do
16
+ subject
17
+ expect(a_request(:get, path)).to have_been_made
18
+ end
19
+
20
+ it "returns an IO" do
21
+ expect(subject).to be_kind_of StringIO
22
+ expect(subject.read).to eq "Hello world!"
23
+ end
24
+ end
25
+
26
+ context "without id:" do
27
+ before { options.delete(:id) }
28
+
29
+ it "raises ArgumentError without sending a request" do
30
+ expect { subject }.to raise_error(ArgumentError)
31
+ expect(a_request(:any, //)).not_to have_been_made
32
+ end
33
+ end
34
+
35
+ context "without token:" do
36
+ before { options.delete(:token) }
37
+
38
+ it "raises ArgumentError without sending a request" do
39
+ expect { subject }.to raise_error(ArgumentError)
40
+ expect(a_request(:any, //)).not_to have_been_made
41
+ end
42
+ end
43
+
44
+ context "when API responded with error:" do
45
+ before do
46
+ stub_request(:get, path)
47
+ .with(basic_auth: %w(foo bar))
48
+ .to_return status: 500,
49
+ headers: { "Content-Type" => "application/json" },
50
+ body: "Server error"
51
+ end
52
+
53
+ it "raises ResponseError" do
54
+ expect { subject }.to raise_error(ABBYY::Cloud::ResponseError)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,129 @@
1
+ RSpec.describe "files.upload" do
2
+ subject do
3
+ client.files.upload file,
4
+ synthesis_mode: synthesis_mode,
5
+ export_format: export_format,
6
+ languages: languages,
7
+ quality: quality
8
+ end
9
+
10
+ let(:client) { ABBYY::Cloud.new(id: "foo", token: "bar") }
11
+ let(:path) { %r{https://api.abbyy.cloud/v0/file} }
12
+ let(:file) { StringIO.new("Hello world!") }
13
+ let(:export_format) { "Doc" }
14
+ let(:quality) { "Fast" }
15
+ let(:synthesis_mode) { "EditableCopy" }
16
+ let(:languages) { %w(en) }
17
+ let(:response) do
18
+ {
19
+ id: "foo",
20
+ token: "bar",
21
+ name: "data.xml",
22
+ mime: "application/xml",
23
+ is_recognizable: true,
24
+ expected_languages: %w(en),
25
+ ocr_settings: {
26
+ format: "Doc",
27
+ quality: "Fast",
28
+ mode: "EditableCopy"
29
+ },
30
+ statistics: {
31
+ language: "en",
32
+ chars_count: 0,
33
+ words_count: 0,
34
+ pages_count: 0
35
+ },
36
+ ocr_statistics: {
37
+ exported_pages: 0,
38
+ total_characters: 0,
39
+ uncertain_characters: 0,
40
+ success_part: 0
41
+ },
42
+ created: "2016-09-05T08:33:03.567Z",
43
+ processed: "2016-09-05T08:33:03.567Z",
44
+ deleted: "2016-09-05T08:33:03.567Z",
45
+ reading_progress: 0,
46
+ reading_status: "New",
47
+ ocr_warnings: [
48
+ {
49
+ warning_type: "BrightnessIsTooHigh",
50
+ page_number: 0,
51
+ dpi: 0,
52
+ language_count: 0,
53
+ full_warning_message: "Uwaga!"
54
+ }
55
+ ],
56
+ error: "Unsupported",
57
+ is_deleted: true
58
+ }
59
+ end
60
+
61
+ before do
62
+ stub_request(:post, path)
63
+ .with(basic_auth: %w(foo bar))
64
+ .to_return status: 200,
65
+ headers: { "Content-Type" => "application/json" },
66
+ body: JSON([response])
67
+ end
68
+
69
+ context "with valid params" do
70
+ it "sends a request to ABBYY Cloud API" do
71
+ subject
72
+ expect(a_request(:post, path)).to have_been_made
73
+ end
74
+
75
+ it "returns array of file info" do
76
+ expect(subject.first).to be_kind_of ABBYY::Cloud::Models::FileInfo
77
+ end
78
+ end
79
+
80
+ context "with wrong export_format:" do
81
+ let(:export_format) { "Unknown" }
82
+
83
+ it "raises ArgumentError without sending a request" do
84
+ expect { subject }.to raise_error(ABBYY::Cloud::ArgumentError)
85
+ expect(a_request(:any, //)).not_to have_been_made
86
+ end
87
+ end
88
+
89
+ context "with wrong quality:" do
90
+ let(:quality) { "Unknown" }
91
+
92
+ it "raises ArgumentError without sending a request" do
93
+ expect { subject }.to raise_error(ABBYY::Cloud::ArgumentError)
94
+ expect(a_request(:any, //)).not_to have_been_made
95
+ end
96
+ end
97
+
98
+ context "with wrong synthesis_mode:" do
99
+ let(:synthesis_mode) { "Unknown" }
100
+
101
+ it "raises ArgumentError without sending a request" do
102
+ expect { subject }.to raise_error(ABBYY::Cloud::ArgumentError)
103
+ expect(a_request(:any, //)).not_to have_been_made
104
+ end
105
+ end
106
+
107
+ context "with wrong languages:" do
108
+ let(:languages) { "foo" }
109
+
110
+ it "raises ArgumentError without sending a request" do
111
+ expect { subject }.to raise_error(ABBYY::Cloud::ArgumentError)
112
+ expect(a_request(:any, //)).not_to have_been_made
113
+ end
114
+ end
115
+
116
+ context "when API responded with error:" do
117
+ before do
118
+ stub_request(:post, path)
119
+ .with(basic_auth: %w(foo bar))
120
+ .to_return status: 500,
121
+ headers: { "Content-Type" => "application/json" },
122
+ body: "Server error"
123
+ end
124
+
125
+ it "raises ResponseError" do
126
+ expect { subject }.to raise_error(ABBYY::Cloud::ResponseError)
127
+ end
128
+ end
129
+ end