Class: Scanii::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/scanii/client.rb

Overview

Synchronous client for the Scanii REST API v2.2.

Construct with either key: + secret: (HTTP Basic Auth) or token: (auth-token authentication). Mixing the two raises ArgumentError.

Per SDK Principle 3 the client is integration-only: it does not retry, batch, or paginate. Each public method maps to exactly one HTTP request.

Examples:

client = Scanii::Client.new(key: "your-key", secret: "your-secret")
result = client.process("./file.pdf")
puts result.findings  # [] when clean

See Also:

Constant Summary collapse

DEFAULT_ENDPOINT =
"https://api.scanii.com".freeze
DEFAULT_TIMEOUT =
60
API_VERSION_PATH =
"/v2.2".freeze
USER_AGENT =
"scanii-ruby/#{Scanii::VERSION}".freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key: nil, secret: nil, token: nil, endpoint: DEFAULT_ENDPOINT, timeout: DEFAULT_TIMEOUT, user_agent: nil) ⇒ Client

Returns a new instance of Client.

Parameters:

  • key (String, nil) (defaults to: nil)

    API key (mutually exclusive with token)

  • secret (String, nil) (defaults to: nil)

    API secret (required when key is set)

  • token (String, nil) (defaults to: nil)

    auth-token id (mutually exclusive with key/secret)

  • endpoint (String) (defaults to: DEFAULT_ENDPOINT)

    base URL; defaults to api.scanii.com

  • timeout (Integer) (defaults to: DEFAULT_TIMEOUT)

    open + read timeout in seconds; default 60

  • user_agent (String, nil) (defaults to: nil)

    optional fragment prepended to the SDK’s default User-Agent

Raises:

  • (ArgumentError)


34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/scanii/client.rb', line 34

def initialize(key: nil, secret: nil, token: nil, endpoint: DEFAULT_ENDPOINT,
               timeout: DEFAULT_TIMEOUT, user_agent: nil)
  @auth_header = build_auth_header(key, secret, token)
  @endpoint    = endpoint.to_s.sub(%r{/+\z}, "")
  raise ArgumentError, "endpoint must not be empty" if @endpoint.empty?

  @base_uri = URI.parse("#{@endpoint}#{API_VERSION_PATH}")
  raise ArgumentError, "endpoint must be http(s)" unless %w[http https].include?(@base_uri.scheme)

  @timeout    = Integer(timeout)
  @user_agent = user_agent && !user_agent.empty? ? "#{user_agent} #{USER_AGENT}" : USER_AGENT
end

Instance Attribute Details

#endpointObject (readonly)

Returns the value of attribute endpoint.



26
27
28
# File 'lib/scanii/client.rb', line 26

def endpoint
  @endpoint
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



26
27
28
# File 'lib/scanii/client.rb', line 26

def timeout
  @timeout
end

#user_agentObject (readonly)

Returns the value of attribute user_agent.



26
27
28
# File 'lib/scanii/client.rb', line 26

def user_agent
  @user_agent
end

Instance Method Details

#create_auth_token(timeout_seconds) ⇒ Scanii::AuthToken

Mint a short-lived auth token. timeout_seconds must be positive.

Returns:

Raises:

  • (ArgumentError)

See Also:



122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/scanii/client.rb', line 122

def create_auth_token(timeout_seconds)
  ts = Integer(timeout_seconds)
  raise ArgumentError, "timeout_seconds must be positive" if ts <= 0

  status, resp_body, headers = post(
    "/auth/tokens",
    body: URI.encode_www_form("timeout" => ts),
    content_type: "application/x-www-form-urlencoded"
  )
  raise_for_status(status, resp_body, headers) unless [200, 201].include?(status)
  AuthToken.from_response(resp_body, headers)
end

#delete_auth_token(id) ⇒ Boolean

Revoke an auth token.

Returns:

  • (Boolean)

    true on 204

Raises:

  • (ArgumentError)

See Also:



151
152
153
154
155
156
157
# File 'lib/scanii/client.rb', line 151

def delete_auth_token(id)
  raise ArgumentError, "id must not be empty" if id.nil? || id.empty?

  status, resp_body, headers = request("DELETE", "/auth/tokens/#{url_encode(id)}")
  raise_for_status(status, resp_body, headers) unless status == 204
  true
end

#fetch(url, metadata: nil, callback: nil) ⇒ Scanii::PendingResult

Ask Scanii to download a remote URL and scan it asynchronously.

Returns:

Raises:

  • (ArgumentError)

See Also:



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/scanii/client.rb', line 79

def fetch(url, metadata: nil, callback: nil)
  raise ArgumentError, "url must not be empty" if url.nil? || url.empty?

  form = { "location" => url }
  form["callback"] = callback if callback && !callback.empty?
  ( || {}).each { |k, v| form["metadata[#{k}]"] = v.to_s }

  status, resp_body, headers = post(
    "/files/fetch",
    body: URI.encode_www_form(form),
    content_type: "application/x-www-form-urlencoded"
  )
  raise_for_status(status, resp_body, headers) unless status == 202
  PendingResult.from_response(resp_body, headers)
end

#pingBoolean

Verify that the configured credentials reach the API.

Returns:

  • (Boolean)

    true when the API responds 200

See Also:



111
112
113
114
115
116
# File 'lib/scanii/client.rb', line 111

def ping
  status, resp_body, headers = request("GET", "/ping")
  return true if status == 200

  raise_for_status(status, resp_body, headers)
end

#process(file_path, metadata: nil, callback: nil) ⇒ Scanii::ProcessingResult

Submit a file for synchronous scanning.

Returns:

See Also:



51
52
53
54
55
56
57
58
# File 'lib/scanii/client.rb', line 51

def process(file_path, metadata: nil, callback: nil)
  assert_readable(file_path)
  fields = build_text_fields(, callback)
  body, content_type = Multipart.encode(fields, file_path)
  status, resp_body, headers = post("/files", body: body, content_type: content_type)
  raise_for_status(status, resp_body, headers) unless status == 201
  ProcessingResult.from_response(resp_body, headers)
end

#process_async(file_path, metadata: nil, callback: nil) ⇒ Scanii::PendingResult

Submit a file for server-side asynchronous scanning. Returns a pending id; the final result is delivered to callback (when supplied) or fetched via #retrieve.



66
67
68
69
70
71
72
73
# File 'lib/scanii/client.rb', line 66

def process_async(file_path, metadata: nil, callback: nil)
  assert_readable(file_path)
  fields = build_text_fields(, callback)
  body, content_type = Multipart.encode(fields, file_path)
  status, resp_body, headers = post("/files/async", body: body, content_type: content_type)
  raise_for_status(status, resp_body, headers) unless status == 202
  PendingResult.from_response(resp_body, headers)
end

#retrieve(id) ⇒ Scanii::ProcessingResult

Retrieve a previously submitted scan result by id.

Returns:

Raises:

  • (ArgumentError)

See Also:



99
100
101
102
103
104
105
# File 'lib/scanii/client.rb', line 99

def retrieve(id)
  raise ArgumentError, "id must not be empty" if id.nil? || id.empty?

  status, resp_body, headers = request("GET", "/files/#{url_encode(id)}")
  raise_for_status(status, resp_body, headers) unless status == 200
  ProcessingResult.from_response(resp_body, headers)
end

#retrieve_auth_token(id) ⇒ Scanii::AuthToken

Inspect a previously created auth token.

Returns:

Raises:

  • (ArgumentError)

See Also:



139
140
141
142
143
144
145
# File 'lib/scanii/client.rb', line 139

def retrieve_auth_token(id)
  raise ArgumentError, "id must not be empty" if id.nil? || id.empty?

  status, resp_body, headers = request("GET", "/auth/tokens/#{url_encode(id)}")
  raise_for_status(status, resp_body, headers) unless status == 200
  AuthToken.from_response(resp_body, headers)
end