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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43690cd24afff7146d8e06a8fa97981242ac044b9f681c594101f648f3771016
4
- data.tar.gz: e9e4f5fd4d7097ae37a482fe44acf6277ea5bfe47abc730363ad86e1e4719aa2
3
+ metadata.gz: eefd130f1dc0dfc251471d88510fb28d2464ddfe23b8a3c5055404708d1118a4
4
+ data.tar.gz: e33c04ea6e50216fe7988d2d5fd05e0327de309bbd26d6956098a86ef07afa4e
5
5
  SHA512:
6
- metadata.gz: 242ce180cfb7061f9e232bfafb3c469fd4c7bb47b0ddf194150d97a17f734f25b224ece23e3cff1ee21b15bdc6eb04572a65b8a0e1de740748f8194a0520a4fb
7
- data.tar.gz: a672905ff810166a17076d8db8df271002489399b08ed56f9b156135f7550f2d1493bf8c78ee2c49f401fd519ce30c671d6af358485bb085c9b604d1b1238388
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
@@ -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
- @http.request(:post, "/api/v1/upload-sessions/#{uuid}/images/#{image_id}/ack")
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
- # @return [Array<Hash>] delivery status records
107
- def delivery_status(uuid)
108
- @http.request(:get, "/api/v1/upload-sessions/#{uuid}/delivery-status")
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
- payload = encrypted.merge(
63
- "filename" => filename,
64
- "mimeType" => mime_type,
65
- "source" => source || "sdk"
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
- headers = {
69
- "X-Aptr-Encrypted" => "default"
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.request(:post, "/api/v1/upload/#{uuid}/images", body: payload, headers: headers)
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) && File.exist?(file) && file.length < 1024
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Apertur
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.6"
5
5
  end
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.0
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-04-14 00:00:00.000000000 Z
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,