Class: Gini::Api::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/gini-api/client.rb

Overview

Main class to operate on the Gini API

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Client

Instantiate a new Gini::Api::Client object with OAuth capabilities

Examples:

api = Gini::Api::Client.new(
  client_id: 'my_client_id',
  client_secret: 'my_client_secret',
)

Parameters:

  • options (Hash) (defaults to: {})

    Hash of available config settings

Options Hash (options):

  • :client_id (String)

    OAuth client_id

  • :client_secret (String)

    OAuth client_secret

  • :oauth_site (String)

    OAuth site to connect to (user.gini.net)

  • :oauth_redirect (String)

    Redirect URI

  • :upload_timeout (Integer)

    Upload timeout in seconds

  • :processing_timeout (Integer)

    API operational timeout in seconds

  • :api_uri (String)

    API URI (api.gini.net)

  • :api_version (String)

    API version to use (v1)

  • :log (Logger)

    logger object to use (initialized with STDOUT otherwise)

  • :user_agent (String)

    HTTP User-Agent (gini-api-ruby/VERSION (Faraday vFaraday::VERSION))


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/gini-api/client.rb', line 36

def initialize(options = {})
  opts = {
    oauth_site: 'https://user.gini.net/',
    oauth_redirect: 'http://localhost',
    api_uri: 'https://api.gini.net',
    api_version: 'v1',
    api_type: 'json',
    upload_timeout: 90,
    processing_timeout: 180,
    log: Logger.new(STDOUT),
    user_agent: "gini-api-ruby/#{VERSION} (Faraday v#{Faraday::VERSION})"
  }.merge(options)

  # Ensure mandatory keys are set
  [:client_id, :client_secret].each do |k|
    raise Gini::Api::Error.new("Mandatory option key is missing: #{k}") unless opts.key?(k)
  end

  # Populate instance variables from merged opts
  opts.each do |k, v|
    instance_variable_set("@#{k}", v)
    self.class.send(:attr_reader, k)
  end

  # Ensure STDOUT is flushed
  STDOUT.sync = true

  # Sanitize api_uri
  @api_uri.sub!(/(\/)+$/, '')

  # Register parser (json+xml) based on API version
  register_parser

  @log.info('Gini API client initialized')
  @log.info("Target: #{@api_uri}")
end

Instance Attribute Details

#logObject (readonly)

Returns the value of attribute log


14
15
16
# File 'lib/gini-api/client.rb', line 14

def log
  @log
end

#tokenObject (readonly)

Returns the value of attribute token


14
15
16
# File 'lib/gini-api/client.rb', line 14

def token
  @token
end

Instance Method Details

#delete(id) ⇒ Object

Delete document

Parameters:

  • id (String)

    document ID


201
202
203
204
205
206
207
208
209
210
# File 'lib/gini-api/client.rb', line 201

def delete(id)
  response = request(:delete, "/documents/#{id}")
  unless response.status == 204
    raise Gini::Api::DocumentError.new(
      "Deletion of docId #{id} failed (code=#{response.status})",
      response
    )
  end
  @log.info("Deleted document #{id}")
end

#get(id) ⇒ Gini::Api::Document

Get document by Id

Parameters:

  • id (String)

    document ID

Returns:


218
219
220
# File 'lib/gini-api/client.rb', line 218

def get(id)
  Gini::Api::Document.new(self, "/documents/#{id}")
end

#list(options = {}) ⇒ Gini::Api::DocumentSet

List all documents

Parameters:

  • options (Hash) (defaults to: {})

    List options (offset and limit)

Options Hash (options):

  • :limit (Integer)

    Maximum number of documents to return (defaults to 20)

  • :offset (Integer)

    Start offset. Defaults to 0

Returns:


230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/gini-api/client.rb', line 230

def list(options = {})
  opts   = { limit: 20, offset: 0 }.merge(options)
  limit  = Integer(opts[:limit])
  offset = Integer(opts[:offset])

  response = request(:get, "/documents?limit=#{limit}&next=#{offset}")
  unless response.status == 200
    raise Gini::Api::DocumentError.new(
      "Failed to get list of documents (code=#{response.status})",
      response
    )
  end
  Gini::Api::DocumentSet.new(self, response.parsed)
end

#login(opts) ⇒ Object

Acquire OAuth2 token and popolate @oauth (instance of Gini::Api::OAuth.new) and @token (OAuth2::AccessToken). Supports 2 strategies: username/password and authorization code

Examples:

api.(auth_code: '1234567890')
api.(username: '[email protected]', password: 'secret')

Parameters:

  • opts (Hash)

    Your authorization credentials

Options Hash (opts):

  • :auth_code (String)

    OAuth authorization code. Will be exchanged for a token

  • :username (String)

    API username

  • :password (String)

    API password


100
101
102
103
# File 'lib/gini-api/client.rb', line 100

def (opts)
  @oauth = Gini::Api::OAuth.new(self, opts)
  @token = @oauth.token
end

#logoutObject

Destroy OAuth2 token


107
108
109
# File 'lib/gini-api/client.rb', line 107

def logout
  @oauth.destroy
end

#register_parserObject

Register OAuth2 response parser


75
76
77
78
79
80
81
82
83
84
85
# File 'lib/gini-api/client.rb', line 75

def register_parser
  OAuth2::Response.register_parser(:gini_json, [version_header(:json)[:accept]]) do |body|
    MultiJson.load(body, symbolize_keys: true) rescue body
  end
  OAuth2::Response.register_parser(:gini_xml, [version_header(:xml)[:accept]]) do |body|
    MultiXml.parse(body) rescue body
  end
  OAuth2::Response.register_parser(:gini_incubator, [version_header(:json, :incubator)[:accept]]) do |body|
    MultiJson.load(body, symbolize_keys: true) rescue body
  end
end

#request(verb, resource, options = {}) ⇒ Object

Request wrapper that sets URI and accept header

Parameters:

  • verb (Symbol)

    HTTP request verb (:get, :post, :put, :delete)

  • resource (String)

    API resource like /documents

  • options (Hash) (defaults to: {})

    Optional type and custom headers

Options Hash (options):

  • :type (String)

    Type to pass to version_header (:xml, :json)

  • :headers (Hash)

    Custom headers. Must include accept


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/gini-api/client.rb', line 130

def request(verb, resource, options = {})
  opts = {
    headers: version_header(options.delete(:type) || @api_type)
  }.merge(options)

  timeout(@processing_timeout) do
    @token.send(verb.to_sym, resource_to_location(resource).to_s , opts)
  end
rescue OAuth2::Error => e
  raise Gini::Api::RequestError.new(
    "API request failed: #{verb} #{resource} (code=#{e.response.status})",
    e.response
  )
rescue Timeout::Error => e
  raise Gini::Api::ProcessingError.new(
    "API request timed out: #{verb} #{resource} (#{e.message})"
  )
end

#search(query, options = {}) ⇒ Gini::Api::DocumentSet

Fulltext search for documents

Parameters:

  • query (String, Array)

    The search term(s), separated by space. Multiple terms as array

  • options (Hash) (defaults to: {})

    Search options

Options Hash (options):

  • :type (String)

    Only include documents with the given doctype

  • :limit (Integer)

    Number of results per page. Must be between 1 and 250. Defaults to 20

  • Integer (])

    nteger] :offset Start offset. Defaults to 0

Returns:


255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/gini-api/client.rb', line 255

def search(query, options = {})
  opts   = { type: '', limit: 20, offset: 0 }.merge(options)
  query  = URI.escape(query)
  type   = URI.escape(opts[:type])
  limit  = Integer(opts[:limit])
  offset = Integer(opts[:offset])

  response = request(:get, "/search?q=#{query}&type=#{type}&limit=#{limit}&next=#{offset}")
  unless response.status == 200
    raise Gini::Api::SearchError.new(
      "Search query failed with code #{response.status}",
      response
    )
  end
  Gini::Api::DocumentSet.new(self, response.parsed)
end

#upload(file, options = {}, &block) ⇒ Gini::Api::Document

Upload a document

Examples:

Upload and wait for completion

doc = api.upload('/tmp/myfile.pdf')

Upload with doctype hint

doc = api.upload('/tmp/myfile.pdf', doctype_hint: 'Receipt')

Upload and monitor progress

doc = api.upload('/tmp/myfile.pdf') { |d| puts "Progress: #{d.progress}" }

Upload and monitor progress

doc = api.upload('This is a text message i would love to get extractions from', text: true)

Parameters:

  • file (String)

    path or open filehandle of the document to upload

  • options (Hash) (defaults to: {})

    Hash of available upload settings

Options Hash (options):

  • :doctype_hint (String)

    Document type hint to optimize results or get incubator results

  • :text (String)

    Use given file-string as text upload

  • :interval (Float)

    Interval to poll progress

Returns:


168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/gini-api/client.rb', line 168

def upload(file, options = {}, &block)
  opts = {
    doctype_hint: nil,
    text: false,
    interval: 0.5
  }.merge(options)

  duration = Hash.new(0)

  # Document upload
  duration[:upload], response = upload_document(file, opts)

  # Start polling (0.5s) when document has been uploaded successfully
  if response.status == 201
    doc = Gini::Api::Document.new(self, response.headers['location'])
    duration[:processing] = poll_document(doc, opts[:interval], &block)

    duration[:total] = duration.values.inject(:+)
    doc.duration = duration

    doc
  else
    fail Gini::Api::UploadError.new(
      "Document upload failed with HTTP code #{response.status}",
      response
    )
  end
end

#version_header(type = @api_type, version = @api_version) ⇒ Hash

Version accept header based on @api_version

Parameters:

  • type (Symbol, String) (defaults to: @api_type)

    Expected response type (:xml, :json)

  • version (Symbol, String) (defaults to: @api_version)

    API version (:v1, :incubator)

Returns:

  • (Hash)

    Return accept header or empty hash


118
119
120
# File 'lib/gini-api/client.rb', line 118

def version_header(type = @api_type, version = @api_version)
  { accept: "application/vnd.gini.#{version}+#{type}" }
end