apertur-sdk 0.1.0 → 0.1.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.
- checksums.yaml +4 -4
- data/README.md +8 -0
- data/lib/apertur/http_client.rb +8 -4
- data/lib/apertur/resources/polling.rb +3 -1
- data/lib/apertur/resources/sessions.rb +26 -3
- data/lib/apertur/resources/upload.rb +33 -9
- data/lib/apertur/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eefd130f1dc0dfc251471d88510fb28d2464ddfe23b8a3c5055404708d1118a4
|
|
4
|
+
data.tar.gz: e33c04ea6e50216fe7988d2d5fd05e0327de309bbd26d6956098a86ef07afa4e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '08c21fe268c9ddf93ddd2aa5d44e7a0312051287b272b639a7b131212384ef91c7020f13ae7ac86a7818ce6372a6e7a0fccbf8247df0102b4e5d20ae3f341685'
|
|
7
|
+
data.tar.gz: 62d086c9c4ab7c9122831fc579e5224764ed892874df7a686ed603ad7eeed2a7c8f50c2c1d41511c72c5922eb243ddd2345b140c9f1f32219a34b7eade389ed1
|
data/README.md
CHANGED
|
@@ -64,7 +64,15 @@ client.sessions.list(page: 1, page_size: 20)
|
|
|
64
64
|
client.sessions.recent(limit: 5)
|
|
65
65
|
client.sessions.qr("uuid", format: "png", size: 300)
|
|
66
66
|
client.sessions.verify_password("uuid", "secret")
|
|
67
|
+
|
|
68
|
+
# Delivery status returns:
|
|
69
|
+
# { "status" => "pending|active|completed|expired",
|
|
70
|
+
# "files" => [...], "lastChanged" => "<ISO 8601>" }
|
|
67
71
|
client.sessions.delivery_status("uuid")
|
|
72
|
+
|
|
73
|
+
# Long-poll: server holds up to 5 min until something changes. Passing
|
|
74
|
+
# `poll_from:` widens the per-request read timeout to 360 s automatically.
|
|
75
|
+
client.sessions.delivery_status("uuid", poll_from: "2026-04-18T12:34:56Z")
|
|
68
76
|
```
|
|
69
77
|
|
|
70
78
|
### Upload
|
data/lib/apertur/http_client.rb
CHANGED
|
@@ -25,9 +25,11 @@ module Apertur
|
|
|
25
25
|
# @param body [Hash, nil] request body to be serialized as JSON
|
|
26
26
|
# @param query [Hash, nil] query parameters
|
|
27
27
|
# @param headers [Hash] additional request headers
|
|
28
|
+
# @param read_timeout [Integer, Float, nil] override the per-request
|
|
29
|
+
# read timeout (seconds). Defaults to the connection default (60s).
|
|
28
30
|
# @return [Hash, Array, nil] parsed JSON response, or nil for 204
|
|
29
31
|
# @raise [Apertur::Error] on API errors
|
|
30
|
-
def request(method, path, body: nil, query: nil, headers: {})
|
|
32
|
+
def request(method, path, body: nil, query: nil, headers: {}, read_timeout: nil)
|
|
31
33
|
uri = build_uri(path, query)
|
|
32
34
|
req = build_request(method, uri, headers)
|
|
33
35
|
|
|
@@ -36,7 +38,7 @@ module Apertur
|
|
|
36
38
|
req.body = body.is_a?(String) ? body : JSON.generate(body)
|
|
37
39
|
end
|
|
38
40
|
|
|
39
|
-
response = execute(uri, req)
|
|
41
|
+
response = execute(uri, req, read_timeout: read_timeout)
|
|
40
42
|
handle_response(response)
|
|
41
43
|
end
|
|
42
44
|
|
|
@@ -119,12 +121,14 @@ module Apertur
|
|
|
119
121
|
|
|
120
122
|
# @param uri [URI]
|
|
121
123
|
# @param req [Net::HTTPRequest]
|
|
124
|
+
# @param read_timeout [Integer, Float, nil] optional override for
|
|
125
|
+
# +Net::HTTP#read_timeout+ (seconds).
|
|
122
126
|
# @return [Net::HTTPResponse]
|
|
123
|
-
def execute(uri, req)
|
|
127
|
+
def execute(uri, req, read_timeout: nil)
|
|
124
128
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
125
129
|
http.use_ssl = (uri.scheme == "https")
|
|
126
130
|
http.open_timeout = 30
|
|
127
|
-
http.read_timeout = 60
|
|
131
|
+
http.read_timeout = read_timeout || 60
|
|
128
132
|
http.request(req)
|
|
129
133
|
end
|
|
130
134
|
|
|
@@ -35,7 +35,9 @@ module Apertur
|
|
|
35
35
|
# @param image_id [String] the image ID
|
|
36
36
|
# @return [Hash] acknowledgement status
|
|
37
37
|
def ack(uuid, image_id)
|
|
38
|
-
|
|
38
|
+
# The server requires a JSON body on this endpoint; sending none
|
|
39
|
+
# results in a 500. An empty object is sufficient.
|
|
40
|
+
@http.request(:post, "/api/v1/upload-sessions/#{uuid}/images/#{image_id}/ack", body: {})
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
# Blocking polling loop that fetches, downloads, and acknowledges images.
|
|
@@ -102,10 +102,33 @@ module Apertur
|
|
|
102
102
|
|
|
103
103
|
# Get the delivery status for a session.
|
|
104
104
|
#
|
|
105
|
+
# Returns a hash with the following shape:
|
|
106
|
+
#
|
|
107
|
+
# {
|
|
108
|
+
# "status" => "pending" | "active" | "completed" | "expired",
|
|
109
|
+
# "files" => [ { "record_id" => ..., "filename" => ..., "size_bytes" => ...,
|
|
110
|
+
# "destinations" => [ { "destination_id" => ..., "status" => ..., ... } ] } ],
|
|
111
|
+
# "lastChanged" => "<ISO 8601>"
|
|
112
|
+
# }
|
|
113
|
+
#
|
|
114
|
+
# When +poll_from+ (ISO 8601 timestamp) is provided, the server long-polls for
|
|
115
|
+
# up to 5 minutes waiting for something to change before responding. This call
|
|
116
|
+
# automatically widens the read timeout to 360 s (6 min) in that case so the
|
|
117
|
+
# server releases first under the happy path.
|
|
118
|
+
#
|
|
105
119
|
# @param uuid [String] the session UUID
|
|
106
|
-
# @
|
|
107
|
-
|
|
108
|
-
|
|
120
|
+
# @param poll_from [String, nil] ISO 8601 timestamp; when set, enables long-poll mode
|
|
121
|
+
# @return [Hash] delivery status snapshot
|
|
122
|
+
def delivery_status(uuid, poll_from: nil)
|
|
123
|
+
query = {}
|
|
124
|
+
query["pollFrom"] = poll_from if poll_from
|
|
125
|
+
read_timeout = poll_from ? 360 : nil
|
|
126
|
+
@http.request(
|
|
127
|
+
:get,
|
|
128
|
+
"/api/v1/upload-sessions/#{uuid}/delivery-status",
|
|
129
|
+
query: query,
|
|
130
|
+
read_timeout: read_timeout,
|
|
131
|
+
)
|
|
109
132
|
end
|
|
110
133
|
end
|
|
111
134
|
end
|
|
@@ -59,18 +59,31 @@ module Apertur
|
|
|
59
59
|
file_data = read_file(file)
|
|
60
60
|
encrypted = Apertur::Crypto.encrypt_image(file_data, public_key)
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
# The server's "default" encryption mode expects a multipart file
|
|
63
|
+
# upload whose body is the JSON-serialized EncryptedPayload (camelCase
|
|
64
|
+
# keys), which it then decrypts with its private key. Sending a JSON
|
|
65
|
+
# request body instead yields a 500 ("No file uploaded").
|
|
66
|
+
payload_json = JSON.generate(
|
|
67
|
+
"encryptedKey" => encrypted["encrypted_key"],
|
|
68
|
+
"iv" => encrypted["iv"],
|
|
69
|
+
"encryptedData" => encrypted["encrypted_data"],
|
|
70
|
+
"algorithm" => encrypted["algorithm"]
|
|
66
71
|
)
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
fields = {}
|
|
74
|
+
fields["source"] = source if source
|
|
75
|
+
|
|
76
|
+
headers = { "X-Aptr-Encrypted" => "default" }
|
|
71
77
|
headers["x-session-password"] = password if password
|
|
72
78
|
|
|
73
|
-
@http.
|
|
79
|
+
@http.request_multipart(
|
|
80
|
+
"/api/v1/upload/#{uuid}/images",
|
|
81
|
+
payload_json,
|
|
82
|
+
filename: "#{filename}.enc",
|
|
83
|
+
mime_type: "application/octet-stream",
|
|
84
|
+
fields: fields,
|
|
85
|
+
headers: headers
|
|
86
|
+
)
|
|
74
87
|
end
|
|
75
88
|
|
|
76
89
|
private
|
|
@@ -82,7 +95,7 @@ module Apertur
|
|
|
82
95
|
def read_file(file)
|
|
83
96
|
if file.respond_to?(:read)
|
|
84
97
|
file.read.b
|
|
85
|
-
elsif file.is_a?(String) &&
|
|
98
|
+
elsif file.is_a?(String) && looks_like_path?(file) && File.exist?(file)
|
|
86
99
|
# Treat short strings that point to existing files as paths.
|
|
87
100
|
# Raw image bytes will almost never be < 1024 bytes AND match an
|
|
88
101
|
# existing filename, so this heuristic is safe in practice.
|
|
@@ -93,6 +106,17 @@ module Apertur
|
|
|
93
106
|
raise ArgumentError, "Unsupported file input. Use a file path String, IO object, or raw String bytes."
|
|
94
107
|
end
|
|
95
108
|
end
|
|
109
|
+
|
|
110
|
+
# Heuristic: does this String plausibly name a file path (as opposed to
|
|
111
|
+
# raw image bytes)? Must be short and contain no NUL byte. Calling
|
|
112
|
+
# File.exist? on binary image data that contains a NUL byte raises
|
|
113
|
+
# "ArgumentError: path name contains null byte", so we must guard it.
|
|
114
|
+
#
|
|
115
|
+
# @param str [String]
|
|
116
|
+
# @return [Boolean]
|
|
117
|
+
def looks_like_path?(str)
|
|
118
|
+
str.length < 1024 && !str.include?("\x00")
|
|
119
|
+
end
|
|
96
120
|
end
|
|
97
121
|
end
|
|
98
122
|
end
|
data/lib/apertur/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: apertur-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Apertur
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Official Ruby client for the Apertur image upload and delivery API. Supports
|
|
14
14
|
session management, image uploads (including client-side encryption), polling, destinations,
|