poli-page 0.9.0 → 0.11.0
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/CHANGELOG.md +229 -1
- data/MIGRATION.md +184 -6
- data/README.md +105 -76
- data/lib/poli_page/client.rb +31 -15
- data/lib/poli_page/documents.rb +35 -65
- data/lib/poli_page/errors.rb +93 -47
- data/lib/poli_page/inputs/render_input.rb +39 -0
- data/lib/poli_page/inputs/thumbnail_options.rb +7 -4
- data/lib/poli_page/internal/constants.rb +14 -1
- data/lib/poli_page/internal/http.rb +42 -24
- data/lib/poli_page/internal/presigned_fetch.rb +44 -44
- data/lib/poli_page/internal/transport.rb +145 -34
- data/lib/poli_page/internal/watchdog.rb +70 -0
- data/lib/poli_page/internal/wire.rb +30 -24
- data/lib/poli_page/models/document_descriptor.rb +20 -3
- data/lib/poli_page/models/document_preview_result.rb +2 -3
- data/lib/poli_page/models/page_format.rb +4 -3
- data/lib/poli_page/render.rb +45 -78
- data/lib/poli_page/render_to_file.rb +0 -1
- data/lib/poli_page/version.rb +1 -1
- data/lib/poli_page.rb +1 -3
- data/sig/poli_page/errors.rbs +32 -18
- data/sig/poli_page/models.rbs +8 -29
- data/sig/poli_page/render.rbs +4 -10
- metadata +18 -5
- data/lib/poli_page/inputs/inline_mode_input.rb +0 -31
- data/lib/poli_page/inputs/project_mode_input.rb +0 -38
- data/lib/poli_page/models/preview_result.rb +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0caa9c1105af62976da1ba1a5a6ef40ad34386359f1b43cea033976146054d6c
|
|
4
|
+
data.tar.gz: ed1c09e7c54ab2ee34d9b6286a674c755ef6a1450b53569490be006939c3ae0d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 779392d70b3ca9aa042d5e935f5d24d9da8d4eede920f43dfb656907107f924672aa644744a0edceb3950538118d99ff3b8fd24ebef274fa2da27d400dc3b68d
|
|
7
|
+
data.tar.gz: 0ddcc5b1c996fb19e51c60025a9353412a6f3f198e84d263a3385eff435d659e303828795efb5185067e2bf8383fb88218b56928e640f35d70cecc3046c38e06
|
data/CHANGELOG.md
CHANGED
|
@@ -5,11 +5,239 @@ here.
|
|
|
5
5
|
|
|
6
6
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
7
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
|
-
Breaking
|
|
8
|
+
Breaking releases (minor bumps while on 0.x) are summarized in
|
|
9
9
|
[MIGRATION.md](MIGRATION.md).
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.11.0] - 2026-10-30
|
|
14
|
+
|
|
15
|
+
Conformance with the Poli Page API contract v1.3.0
|
|
16
|
+
([poli-page/api-contract](https://github.com/poli-page/api-contract)). See
|
|
17
|
+
[MIGRATION.md](MIGRATION.md#from-0100-to-0110).
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **BREAKING**: the keys inside `data:` and `metadata:` are no longer
|
|
22
|
+
renamed. They are sent exactly as you write them, at every depth: before,
|
|
23
|
+
every key was camelized (`data: { invoice_number: 1 }` reached the template
|
|
24
|
+
as `invoiceNumber`). `DocumentDescriptor#metadata` is returned exactly as
|
|
25
|
+
the API stored it, with its String keys: before, every key was snake_cased
|
|
26
|
+
and symbolized (`{ "customerId" => "c" }` came back as
|
|
27
|
+
`{ customer_id: "c" }`), and two keys such as `someKey` and `some_key`
|
|
28
|
+
collided. The SDK's own names (`project`, `template`, the descriptor's
|
|
29
|
+
readers, the thumbnail options…) keep their Ruby spelling. See
|
|
30
|
+
[MIGRATION.md](MIGRATION.md#payload-keys-are-no-longer-renamed).
|
|
31
|
+
- **BREAKING**: `QUOTA_EXCEEDED` and `OVERAGE_CAP_EXCEEDED` (429) and
|
|
32
|
+
`STORAGE_ACCESS_DENIED` (502) are no longer retried, even with a
|
|
33
|
+
`Retry-After` header, and `retryable?` returns `false` for them (it
|
|
34
|
+
returned `true` for any 429 or 5xx). They last until the monthly reset or
|
|
35
|
+
until the bucket access is restored, so the SDK used to wait up to 30 s per
|
|
36
|
+
retry for nothing. `RATE_LIMIT_EXCEEDED` and every other 429 and 5xx are
|
|
37
|
+
still retried. Code that requeues on `retryable?` stops requeueing these
|
|
38
|
+
three.
|
|
39
|
+
- **BREAKING**: `retryable?` is `false` for every `DOWNLOAD_FAILED`
|
|
40
|
+
(`PoliPage::DownloadError`), whatever the storage status: it was `true`
|
|
41
|
+
with a storage 5xx or 429. The SDK never retried the download itself, and
|
|
42
|
+
still does not.
|
|
43
|
+
- **BREAKING**: the error predicates read `status` and `code`, as the SDK
|
|
44
|
+
specification defines them, instead of the error class: `auth_error?` is
|
|
45
|
+
`status` 401 or 403, `rate_limit_error?` 429, `validation_error?` 400, and
|
|
46
|
+
`network_error?` is `code` `network_error` or `timeout`. For API errors
|
|
47
|
+
only the never-retried codes above change. A `DownloadError` now follows
|
|
48
|
+
the storage's status: a 401 or 403 is an auth error (an expired presigned
|
|
49
|
+
URL gets 403), a 429 a rate-limit error and a 400 a validation error; all
|
|
50
|
+
three were `false` before.
|
|
51
|
+
- **BREAKING**: SDK error messages are the exact texts of the SDK
|
|
52
|
+
specification, the same in every Poli Page SDK:
|
|
53
|
+
- `invalid_options`: `apiKey is required` (was `api_key is required`).
|
|
54
|
+
- `network_error` (`ConnectionError`): `Network request failed` (was the
|
|
55
|
+
HTTP library's exception text, which stays available as `cause`).
|
|
56
|
+
- `timeout` (`TimeoutError`): `Request timed out after <ms>ms`, the
|
|
57
|
+
client's `timeout` in milliseconds (was `request timed out after <s>s`).
|
|
58
|
+
`TimeoutError#timeout` is still in seconds.
|
|
59
|
+
- `DOWNLOAD_FAILED`: `Failed to download PDF: HTTP <status>` (was
|
|
60
|
+
`Failed to download: <status> <reason phrase>`), and
|
|
61
|
+
`Failed to download PDF: network error` when the download gets no
|
|
62
|
+
complete answer (was the HTTP library's text, now `cause`).
|
|
63
|
+
- An error answer whose body is not JSON: `HTTP <status>` (was
|
|
64
|
+
`HTTP <status>: response body was not valid JSON`); the code is still
|
|
65
|
+
`INTERNAL_ERROR` with the real status.
|
|
66
|
+
- `DownloadError.new` takes `status:` and `cause:` and builds its message
|
|
67
|
+
from them; `message:` is optional. `ConnectionError.new`'s `message:` is
|
|
68
|
+
optional too.
|
|
69
|
+
- **BREAKING**: `format:` and `orientation:` are no longer checked before the
|
|
70
|
+
request (SDK specification §5.4: the SDK validates nothing else
|
|
71
|
+
client-side). A value outside `PoliPage::PageFormat::FORMATS` or
|
|
72
|
+
`portrait`/`landscape` is sent as given, and the API answers it with
|
|
73
|
+
`PoliPage::ValidationError` (`400 VALIDATION_ERROR`) instead of the SDK
|
|
74
|
+
raising `PoliPage::InvalidOptionsError` (`invalid_options`, which the
|
|
75
|
+
specification keeps for the constructor). `PageFormat.valid?` and
|
|
76
|
+
`Orientation.valid?` stay for callers who check their own input.
|
|
77
|
+
- `render.document`, `render.pdf`, `render.pdf_stream` and `render_to_file`
|
|
78
|
+
raise `PoliPage::InvalidOptionsError` with code
|
|
79
|
+
`PROJECT_REQUIRED_FOR_DOCUMENT` and message `project is required` before
|
|
80
|
+
any request when `project:` is nil or empty (it was sent, and the API
|
|
81
|
+
answered `400`).
|
|
82
|
+
- `Retry-After` is read as integer seconds or an HTTP-date (IMF-fixdate,
|
|
83
|
+
RFC 850 or asctime) only, clamped to 0–30 s; a date's delay keeps its
|
|
84
|
+
sub-second rest instead of being rounded down to whole seconds. Any other
|
|
85
|
+
value (`1.5`, `-1`, `soon`) falls back to the backoff. `Retry-After-Ms` is
|
|
86
|
+
ignored, as before.
|
|
87
|
+
- The client's `timeout` is one deadline for each whole attempt: opening the
|
|
88
|
+
connection (DNS, TCP, a proxy's `CONNECT`, the TLS handshake), the request
|
|
89
|
+
and the answer, body included. Before, it applied to each phase and each
|
|
90
|
+
socket operation on its own, so a slow connection followed by a slow TLS
|
|
91
|
+
handshake, or an answer that kept sending a byte now and then, could
|
|
92
|
+
outlive it while the error still named `timeout`. The connection phase is
|
|
93
|
+
now bounded by the time left, and a per-attempt watchdog thread closes the
|
|
94
|
+
connection at the deadline.
|
|
95
|
+
- An answer's whole body is read inside its attempt. A 2xx cut mid-body is a
|
|
96
|
+
`network_error` (`Network request failed`, the read error as `cause`), and
|
|
97
|
+
one that stalls past `timeout` a `timeout`; both are retried. A 2xx body
|
|
98
|
+
shorter than its `Content-Length` used to be accepted, then failed as
|
|
99
|
+
`INTERNAL_ERROR` ("response body was not valid JSON"). A render whose 2xx
|
|
100
|
+
answer is cut was already stored by the API, so its retry can store a
|
|
101
|
+
second document.
|
|
102
|
+
- An error answer whose body fails while it is read is handled as a body
|
|
103
|
+
that is not JSON: `INTERNAL_ERROR`, `HTTP <status>`, the real status and
|
|
104
|
+
`x-request-id`, the read error as `cause`, and retried by its status (a 503
|
|
105
|
+
yes, a 400 no). It never takes a code from the bytes received so far.
|
|
106
|
+
- `on_response` fires once the 2xx body is read, and never for a 2xx whose
|
|
107
|
+
body failed.
|
|
108
|
+
- `PoliPage::Error#initialize` takes `cause:`; `Error#cause` returns it even
|
|
109
|
+
when the error is raised outside the `rescue` that caught the original.
|
|
110
|
+
Network errors, timeouts and download failures carry the HTTP library's
|
|
111
|
+
exception there.
|
|
112
|
+
- `documents.preview` returns its HTML as a UTF-8 string, read in the
|
|
113
|
+
`charset` of its `Content-Type` (UTF-8 when there is none). It was binary,
|
|
114
|
+
`ASCII-8BIT`, so any non-ASCII character compared unequal to a UTF-8
|
|
115
|
+
string.
|
|
116
|
+
- A 2xx whose body is not a JSON object (not JSON, empty, absent, or another
|
|
117
|
+
JSON value) still raises `INTERNAL_ERROR`, not retried, but now fires
|
|
118
|
+
`on_error` first, like every error the SDK raises, and keeps the parser's
|
|
119
|
+
error as `cause`. An absent body raised a raw `TypeError` and a JSON array a
|
|
120
|
+
raw `NoMethodError`.
|
|
121
|
+
- `documents.thumbnails` ignores thumbnail fields the SDK does not know,
|
|
122
|
+
instead of raising `ArgumentError`, so the API can add fields.
|
|
123
|
+
|
|
124
|
+
### Fixed
|
|
125
|
+
|
|
126
|
+
- A GET or DELETE (`documents.get`, `documents.preview`, `documents.delete`)
|
|
127
|
+
and the presigned-URL download are never sent again behind the SDK's
|
|
128
|
+
back. `Net::HTTP` resends an idempotent request once by itself
|
|
129
|
+
(`max_retries`, default 1) after a dropped connection, a read error or a
|
|
130
|
+
read timeout: that attempt was not counted by the SDK's `max_retries` nor
|
|
131
|
+
reported to `on_request`, a timed-out attempt lasted twice the `timeout`
|
|
132
|
+
its error named, and the download, which must not be retried, could be
|
|
133
|
+
fetched twice. The SDK now sets `max_retries = 0` on every connection.
|
|
134
|
+
`Net::HTTP` never resent a POST by itself.
|
|
135
|
+
- A dropped connection (`EOFError`, `IOError`, `Errno::EPIPE`, …) was not
|
|
136
|
+
translated and escaped as the raw Ruby exception, outside the retry loop;
|
|
137
|
+
it is now a retried `network_error`. A dropped or timed-out download is
|
|
138
|
+
`DOWNLOAD_FAILED`.
|
|
139
|
+
- A retry wait is never shorter than its delay: when `sleep` returns early,
|
|
140
|
+
the SDK sleeps again for the rest, on the monotonic clock.
|
|
141
|
+
- An error raised by the caller's `pdf_stream` block (for example
|
|
142
|
+
`Errno::ENOSPC` from writing the chunk) propagates unchanged instead of
|
|
143
|
+
being wrapped.
|
|
144
|
+
- Docs: the retries page no longer claims the API deduplicates on
|
|
145
|
+
`Idempotency-Key`. It does not: the key is stable across the retries of one
|
|
146
|
+
call, and a render retried after a late 5xx, a `timeout` or a
|
|
147
|
+
`network_error` can store a second document. The errors docs no longer
|
|
148
|
+
list `QUOTA_EXCEEDED`, `OVERAGE_CAP_EXCEEDED` and `STORAGE_ACCESS_DENIED`
|
|
149
|
+
as retried.
|
|
150
|
+
|
|
151
|
+
### Added
|
|
152
|
+
|
|
153
|
+
- Known error code `TEMPLATE_UNSAFE` (400: the template, a partial or an SVG
|
|
154
|
+
asset holds an injection attempt; the message names it; not retried), next
|
|
155
|
+
to `TEMPLATE_ERROR`.
|
|
156
|
+
- `spec/contract` runs every SDK case of the API contract against a local
|
|
157
|
+
mock server over real sockets; CI pins the contract by commit
|
|
158
|
+
(`poli-page/api-contract` action) and Dependabot proposes each new version.
|
|
159
|
+
Run it locally with `POLI_CONTRACT=<path to contract.json> bundle exec rspec`.
|
|
160
|
+
|
|
161
|
+
## [0.10.0] - 2026-10-27
|
|
162
|
+
|
|
163
|
+
Environments contract: an API key carries its environment (`pp_{env}_…`,
|
|
164
|
+
e.g. `pp_sandbox_…`, `pp_live_…`, `pp_staging_…`) and a render uses the
|
|
165
|
+
project's copy published in that environment. Breaking, with no deprecation
|
|
166
|
+
window.
|
|
167
|
+
|
|
168
|
+
### Removed
|
|
169
|
+
|
|
170
|
+
- **BREAKING**: **`render.preview`** and its **`PreviewResult`** type. The
|
|
171
|
+
API removed `POST /v1/render/preview` (a call now gets `404`).
|
|
172
|
+
`documents.preview(id)` stays.
|
|
173
|
+
- **BREAKING**: **Inline mode**: the `InlineModeInput` type and raw HTML in
|
|
174
|
+
`template:`. `template:` is always a slug.
|
|
175
|
+
- **BREAKING**: The **`version:`** keyword of `render.document`,
|
|
176
|
+
`render.pdf`, `render.pdf_stream` and `render_to_file`, and with it draft
|
|
177
|
+
rendering by an API key. Passing `version:` now raises `ArgumentError`.
|
|
178
|
+
- **BREAKING**: The **`version`** field of `DocumentDescriptor`.
|
|
179
|
+
- **BREAKING**: From `PoliPage::ErrorCodes`: `VERSION_NOT_FOUND`,
|
|
180
|
+
`INVALID_VERSION_FORMAT`, `VERSION_REQUIRED`, `INVALID_VERSION_FOR_KEY_ENV`,
|
|
181
|
+
`MISSING_PROJECT_OR_TEMPLATE`, `GONE` (the API answers `410 DOCUMENT_GONE`)
|
|
182
|
+
and `PAYMENT_REQUIRED` (payment grace is `403 ORGANIZATION_PAYMENT_GRACE`).
|
|
183
|
+
|
|
184
|
+
### Changed
|
|
185
|
+
|
|
186
|
+
- **BREAKING**: `ProjectModeInput` is renamed **`RenderInput`** and is the
|
|
187
|
+
only input type.
|
|
188
|
+
- The render body is built from the seven documented fields only
|
|
189
|
+
(`project`, `template`, `data`, `metadata`, `format`, `orientation`,
|
|
190
|
+
`locale`); unset optional fields are omitted. The SDK never sends an
|
|
191
|
+
environment: the API resolves it from the key.
|
|
192
|
+
- `DocumentDescriptor#environment` is any environment name (`sandbox`,
|
|
193
|
+
`live`, `staging`, …), returned unchanged. Response fields the SDK does not
|
|
194
|
+
know are ignored instead of failing the `Data.define` constructor.
|
|
195
|
+
- `documents.thumbnails(id)` takes optional options: `width:` is no longer
|
|
196
|
+
required (API default 400) and `format:` accepts `"webp"` (API default
|
|
197
|
+
`"jpeg"`). With no option set the body is `{}`. `ThumbnailOptions.new`
|
|
198
|
+
takes every field as optional.
|
|
199
|
+
- **BREAKING**: `documents.thumbnails(id)` no longer pre-checks `width`,
|
|
200
|
+
`format`, `quality` or `pages`: an out-of-bounds option raises
|
|
201
|
+
`PoliPage::ValidationError` (the API's `400 VALIDATION_ERROR`) instead of
|
|
202
|
+
`PoliPage::InvalidOptionsError` before the request.
|
|
203
|
+
- `documents.delete` is documented as idempotent: the API answers `204` again
|
|
204
|
+
for an already-deleted document, so a re-delete returns `nil`.
|
|
205
|
+
- README, docs site and `examples/demo.rb` follow the environments contract:
|
|
206
|
+
the quick start and the demo render `getting-started`/`welcome` with a
|
|
207
|
+
`sandbox` key and no version, "Authentication & environments" carries the
|
|
208
|
+
key table and the key regex, and previews point to the playground
|
|
209
|
+
(<https://play.poli.page>). The demo checks a pasted key against the key
|
|
210
|
+
regex only and its last step is `not-found` (`404 TEMPLATE_NOT_FOUND`).
|
|
211
|
+
- The nightly integration workflow no longer passes a template version; it
|
|
212
|
+
renders `getting-started`/`welcome` with the `POLI_PAGE_API_KEY` secret (a
|
|
213
|
+
`pp_sandbox_` key).
|
|
214
|
+
- `MIGRATION.md` has a "From 0.9.x to 0.10.0" entry.
|
|
215
|
+
|
|
216
|
+
### Added
|
|
217
|
+
|
|
218
|
+
- `PoliPage::ErrorCodes`: `PUBLISH_IN_PROGRESS` (503, retried),
|
|
219
|
+
`ENVIRONMENT_MISMATCH` (400), `PROJECT_NOT_IN_ENVIRONMENT` (404),
|
|
220
|
+
`TEMPLATE_NOT_FOUND` (404), `DOCUMENT_GONE` (410), `ENVIRONMENT_PROTECTED`
|
|
221
|
+
(403), `ORGANIZATION_PAYMENT_GRACE` (403), `EMPTY_DOCUMENT` (400),
|
|
222
|
+
`STORAGE_ACCESS_DENIED` (502), `ORGANIZATION_MIGRATING` (503),
|
|
223
|
+
`PROJECT_NOT_FOUND` (404), `TEMPLATE_ERROR` (400),
|
|
224
|
+
`THUMBNAILS_NOT_AVAILABLE` (403), `RATE_LIMIT_EXCEEDED` (429),
|
|
225
|
+
`RENDER_BODY_TOO_LARGE` (413), `DOCUMENT_TOO_LARGE` (413) and
|
|
226
|
+
`RENDER_TIMEOUT` (504): 31 known API codes in all. The list stays open:
|
|
227
|
+
`Error#code` is whatever string the API returns.
|
|
228
|
+
- A render held by a publish of the same project and environment gets
|
|
229
|
+
`503 PUBLISH_IN_PROGRESS` with `Retry-After`; the existing retry policy
|
|
230
|
+
retries it after that delay.
|
|
231
|
+
|
|
232
|
+
## [0.9.1] - 2026-06-22
|
|
233
|
+
|
|
234
|
+
### Fixed
|
|
235
|
+
|
|
236
|
+
- Declare `base64` as a runtime dependency. The SDK hands back base64-encoded
|
|
237
|
+
thumbnail data (decoded by consumers with `Base64.decode64`) and ships an
|
|
238
|
+
example that requires `base64`; on Ruby 3.4+, where `base64` is no longer a
|
|
239
|
+
default gem, this surfaced as `LoadError -- base64` without the dependency.
|
|
240
|
+
|
|
13
241
|
## [0.9.0] - 2026-06-18
|
|
14
242
|
|
|
15
243
|
### Added — first release
|
data/MIGRATION.md
CHANGED
|
@@ -1,14 +1,192 @@
|
|
|
1
1
|
# Migration Guide
|
|
2
2
|
|
|
3
|
-
This file documents breaking changes between
|
|
3
|
+
This file documents breaking changes between releases of `poli-page`
|
|
4
4
|
(the Poli Page SDK for Ruby). The SDK follows
|
|
5
|
-
[Semantic Versioning](https://semver.org/spec/v2.0.0.html):
|
|
6
|
-
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html): while it is on
|
|
6
|
+
0.x, a breaking change bumps the minor version; from 1.0 on, only a major
|
|
7
|
+
version does. Every breaking release comes with an entry here.
|
|
7
8
|
|
|
8
|
-
##
|
|
9
|
+
## From 0.10.0 to 0.11.0
|
|
9
10
|
|
|
10
|
-
The
|
|
11
|
-
|
|
11
|
+
The SDK now follows the Poli Page API contract v1.3.0, the same retry policy,
|
|
12
|
+
error predicates and messages as every other Poli Page SDK. No method or
|
|
13
|
+
option is renamed. Code may need a change where it relied on the renaming
|
|
14
|
+
of `data` / `metadata` keys, and in error handling.
|
|
15
|
+
|
|
16
|
+
### Payload keys are no longer renamed
|
|
17
|
+
|
|
18
|
+
The SDK specification says `metadata` is sent as is and returned unchanged,
|
|
19
|
+
and `data` is the template's own data. The SDK used to camelize every key of
|
|
20
|
+
both on the way out and snake_case (and symbolize) every key of `metadata` on
|
|
21
|
+
the way back. It no longer touches them; only its own names (`project:`,
|
|
22
|
+
`template:`, `doc.page_count`, …) keep their Ruby spelling.
|
|
23
|
+
|
|
24
|
+
- **`data:`** — write the keys your template uses. If it reads
|
|
25
|
+
`invoiceNumber` and you relied on the conversion, send `invoiceNumber`:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
# before (converted to invoiceNumber on the wire)
|
|
29
|
+
client.render.pdf(project: "billing", template: "invoice", data: { invoice_number: "INV-001" })
|
|
30
|
+
|
|
31
|
+
# now (sent as written)
|
|
32
|
+
client.render.pdf(project: "billing", template: "invoice", data: { invoiceNumber: "INV-001" })
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- **`metadata:`** — sent as written too; send the keys you want stored.
|
|
36
|
+
Reads use the API's String keys:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
# before
|
|
40
|
+
doc.metadata[:customer_id]
|
|
41
|
+
|
|
42
|
+
# now
|
|
43
|
+
doc.metadata["customerId"]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Documents rendered before the upgrade keep the keys they were stored with
|
|
47
|
+
(camelCase, since the old SDK converted them).
|
|
48
|
+
|
|
49
|
+
### Codes that are no longer retried
|
|
50
|
+
|
|
51
|
+
`QUOTA_EXCEEDED`, `OVERAGE_CAP_EXCEEDED` (429) and `STORAGE_ACCESS_DENIED`
|
|
52
|
+
(502) reach you after the first attempt, and `retryable?` is `false` for
|
|
53
|
+
them. `DOWNLOAD_FAILED` is never `retryable?`, whatever the storage status.
|
|
54
|
+
If you requeued on `retryable?`, these no longer requeue: surface the quota
|
|
55
|
+
and the bucket errors instead.
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
rescue PoliPage::Error => e
|
|
59
|
+
case e.code
|
|
60
|
+
when "QUOTA_EXCEEDED", "OVERAGE_CAP_EXCEEDED" then show_banner("Monthly quota reached.")
|
|
61
|
+
when "STORAGE_ACCESS_DENIED" then alert_ops("Bucket access revoked.")
|
|
62
|
+
else raise unless e.retryable?
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Predicates read `status` and `code`
|
|
68
|
+
|
|
69
|
+
`auth_error?` (401/403), `rate_limit_error?` (429), `validation_error?` (400)
|
|
70
|
+
and `network_error?` (`network_error`/`timeout`) no longer look at the class.
|
|
71
|
+
The answer only changes for `PoliPage::DownloadError`: a storage 403 (an
|
|
72
|
+
expired presigned URL) is now `auth_error?`. Rescuing by class works as
|
|
73
|
+
before.
|
|
74
|
+
|
|
75
|
+
### Messages
|
|
76
|
+
|
|
77
|
+
Matching on an SDK error's message needs updating; prefer `code`:
|
|
78
|
+
|
|
79
|
+
| Error | Before | Now |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| `InvalidOptionsError` (no key) | `api_key is required` | `apiKey is required` |
|
|
82
|
+
| `ConnectionError` | the HTTP library's text | `Network request failed` (`cause` keeps the original) |
|
|
83
|
+
| `TimeoutError` | `request timed out after 60s` | `Request timed out after 60000ms` |
|
|
84
|
+
| `DownloadError` (status) | `Failed to download: 403 Forbidden` | `Failed to download PDF: HTTP 403` |
|
|
85
|
+
| `DownloadError` (no answer) | the HTTP library's text | `Failed to download PDF: network error` |
|
|
86
|
+
| non-JSON error body | `HTTP 502: response body was not valid JSON` | `HTTP 502` |
|
|
87
|
+
|
|
88
|
+
### `format:` and `orientation:` are checked by the API
|
|
89
|
+
|
|
90
|
+
An unknown `format:` or `orientation:` is no longer refused by the SDK
|
|
91
|
+
before the request: the API answers it with `400 VALIDATION_ERROR`.
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
# before
|
|
95
|
+
rescue PoliPage::InvalidOptionsError # "invalid format: ..."
|
|
96
|
+
|
|
97
|
+
# now
|
|
98
|
+
rescue PoliPage::ValidationError => e # e.code == "VALIDATION_ERROR", e.status == 400
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
To refuse a bad value before any request, check it yourself with
|
|
102
|
+
`PoliPage::PageFormat.valid?(format)` and `PoliPage::Orientation.valid?(orientation)`.
|
|
103
|
+
|
|
104
|
+
### `project:` nil or empty
|
|
105
|
+
|
|
106
|
+
`render.*` and `render_to_file` raise `InvalidOptionsError` with code
|
|
107
|
+
`PROJECT_REQUIRED_FOR_DOCUMENT` before any request, instead of sending the
|
|
108
|
+
render and getting a `ValidationError` from the API.
|
|
109
|
+
|
|
110
|
+
## From 0.9.x to 0.10.0
|
|
111
|
+
|
|
112
|
+
0.10.0 follows the environments contract: an API key belongs to exactly one
|
|
113
|
+
environment of your organization, its text names that environment
|
|
114
|
+
(`pp_{env}_{32 lowercase hex}`: `pp_sandbox_…`, `pp_live_…`,
|
|
115
|
+
`pp_staging_…`), and a render uses the project's copy published in that
|
|
116
|
+
environment. Nobody used the new contract before this release, so every
|
|
117
|
+
removal ships at once with no deprecation window.
|
|
118
|
+
|
|
119
|
+
### API keys
|
|
120
|
+
|
|
121
|
+
The `pp_test_` and `pp_sa_` prefixes are gone; standard and service keys
|
|
122
|
+
share the one format above. Create a key in the environment you render in:
|
|
123
|
+
a `sandbox` key replaces a test key (watermarked, not counted toward the
|
|
124
|
+
monthly PDF quota, documents expire after 30 days), a `live` key renders
|
|
125
|
+
unwatermarked, counted documents. The SDK passes the key through unchanged
|
|
126
|
+
and never sends an environment.
|
|
127
|
+
|
|
128
|
+
### `version:` is removed
|
|
129
|
+
|
|
130
|
+
`render.pdf`, `render.pdf_stream`, `render.document` and `render_to_file`
|
|
131
|
+
no longer take `version:`; passing it raises `ArgumentError` (unknown
|
|
132
|
+
keyword) before any request. Publish the project to the key's environment
|
|
133
|
+
instead of pinning a version. `DocumentDescriptor#version` is removed too.
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
# 0.9.x
|
|
137
|
+
client.render.pdf(project: "billing", template: "invoice", version: "1.0.0", data: data)
|
|
138
|
+
|
|
139
|
+
# 0.10.0
|
|
140
|
+
client.render.pdf(project: "billing", template: "invoice", data: data)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### `render.preview` and inline HTML are removed
|
|
144
|
+
|
|
145
|
+
`render.preview`, `PreviewResult` and `InlineModeInput` are gone: the API
|
|
146
|
+
renders published templates only. Preview a template or render inline HTML
|
|
147
|
+
in the playground, <https://play.poli.page>. `documents.preview(id)`, the
|
|
148
|
+
stored HTML of a document you already rendered, stays.
|
|
149
|
+
|
|
150
|
+
### Types
|
|
151
|
+
|
|
152
|
+
| 0.9.x | 0.10.0 |
|
|
153
|
+
| -------------------------------------- | ---------------------------------------- |
|
|
154
|
+
| `PoliPage::ProjectModeInput` | `PoliPage::RenderInput` |
|
|
155
|
+
| `PoliPage::InlineModeInput` | removed |
|
|
156
|
+
| `PoliPage::PreviewResult` | removed |
|
|
157
|
+
| `DocumentDescriptor#version` | removed |
|
|
158
|
+
| `DocumentDescriptor#environment` (`"sandbox"` or `"live"`) | any environment name (`"sandbox"`, `"live"`, `"staging"`, …) |
|
|
159
|
+
|
|
160
|
+
### Error codes
|
|
161
|
+
|
|
162
|
+
| 0.9.x code | 0.10.0 |
|
|
163
|
+
| -------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
164
|
+
| `GONE` | `DOCUMENT_GONE` (410) |
|
|
165
|
+
| `PAYMENT_REQUIRED` | `ORGANIZATION_PAYMENT_GRACE` (403) |
|
|
166
|
+
| `VERSION_NOT_FOUND`, `INVALID_VERSION_FORMAT`, `VERSION_REQUIRED`, `INVALID_VERSION_FOR_KEY_ENV` | removed (there is no version); a project or template missing from the key's environment answers `PROJECT_NOT_IN_ENVIRONMENT` or `TEMPLATE_NOT_FOUND` (404) |
|
|
167
|
+
| `MISSING_PROJECT_OR_TEMPLATE` | removed |
|
|
168
|
+
|
|
169
|
+
`PoliPage::ErrorCodes` gains `PROJECT_NOT_FOUND`, `PROJECT_NOT_IN_ENVIRONMENT`,
|
|
170
|
+
`TEMPLATE_NOT_FOUND`, `DOCUMENT_GONE`, `ENVIRONMENT_PROTECTED`,
|
|
171
|
+
`PUBLISH_IN_PROGRESS` and the other codes listed in the CHANGELOG. Code that
|
|
172
|
+
matches on `e.code` should switch to the new names; code that rescues by
|
|
173
|
+
class (`PoliPage::GoneError`, `PoliPage::NotFoundError`, …) keeps working.
|
|
174
|
+
|
|
175
|
+
### Stored documents
|
|
176
|
+
|
|
177
|
+
- `documents.thumbnails(id)` no longer checks its options on the client: an
|
|
178
|
+
out-of-bounds `width`, `format`, `quality` or `pages` raises
|
|
179
|
+
`PoliPage::ValidationError` (the API's `400 VALIDATION_ERROR`) instead of
|
|
180
|
+
`PoliPage::InvalidOptionsError`. Every option is optional, and
|
|
181
|
+
`format: "webp"` is accepted.
|
|
182
|
+
- `documents.delete(id)` is idempotent: deleting an already-deleted document
|
|
183
|
+
returns `nil` instead of raising `PoliPage::GoneError`. A later
|
|
184
|
+
`documents.get(id)` raises `PoliPage::GoneError` with code `DOCUMENT_GONE`.
|
|
185
|
+
|
|
186
|
+
## 0.9.0
|
|
187
|
+
|
|
188
|
+
The first release, published to RubyGems on 2026-06-18. The surface below is
|
|
189
|
+
0.9.x's; see [From 0.9.x to 0.10.0](#from-09x-to-0100) for what changed since.
|
|
12
190
|
|
|
13
191
|
### Surface
|
|
14
192
|
|