Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
9f0cbc1
Add contract-generated ordinary API client and native wire tests
kvz Sep 25, 2026
474010c
Mark contract projections as generated
kvz Sep 25, 2026
0ad7504
Preserve auth constraints, safe Template paths and Go 1.15 support
kvz Sep 25, 2026
54402e1
Regenerate stable operation-local Go contract type aliases
kvz Sep 25, 2026
ac6e2d1
Make contract uploads cancellable and decoding forward-compatible
kvz Sep 25, 2026
0067817
Guard integer exponents and retain union fields in native decoding
kvz Sep 25, 2026
a195757
Regenerate lossless union selection from API2 85df8a2a5e
kvz Sep 25, 2026
1897a05
Reject API origins with an empty query delimiter
kvz Sep 25, 2026
43ffe00
Improve generated model discovery and demonstrate typed workflows
kvz Sep 27, 2026
eee099b
Bound recursive decoding and validate multipart headers
kvz Sep 27, 2026
48aec87
Bound union nesting and share field-path prefixes
kvz Sep 27, 2026
d624565
Keep generated clients opt-in and prefilter tagged unions safely
kvz Sep 27, 2026
bf3ae05
Stop example polling on terminal aborts and keep toolchain tests port…
kvz Sep 27, 2026
0ea6516
Normalize endpoint join boundaries without changing proxy paths
kvz Sep 27, 2026
2520099
Exercise shared SDK workflows and repair waiting and Smart CDN signing
kvz Sep 28, 2026
4656681
Keep workflow guidance honest and test opt-in imports with trimmed to…
kvz Sep 28, 2026
d659e21
Handle the relative GOROOT marker in trimmed Go 1.15 tests
kvz Sep 28, 2026
2d0147a
Verify deadline workflows reach the HTTP fixture
kvz Sep 28, 2026
41d2124
Use source-owned domain types in the draft contract client
kvz Sep 28, 2026
3729aa4
Clarify generated multipart support and workflow ownership
kvz Sep 28, 2026
de08156
Add owner-aware contract-client Assembly wait and cancellation workflows
kvz Sep 29, 2026
d93bf25
Preserve explicitly configured endpoints in Assembly workflows
kvz Sep 29, 2026
6ba2e18
Confirm terminal cancellation races and bound safe read retries
kvz Sep 29, 2026
d29caf8
Validate configured uploader origins consistently
kvz Sep 29, 2026
54bde4c
Canonicalize configured uploader authorities consistently
kvz Sep 29, 2026
33ef947
Document fail-closed oversized workflow responses
kvz Sep 29, 2026
75abeff
Add contract-client resumable uploads with persisted sessions
kvz Sep 29, 2026
b2a81ee
Fix resumable upload ownership and recovery boundaries
kvz Sep 29, 2026
0ccc951
Recover safe reads and partially acknowledged uploads
kvz Sep 29, 2026
fbf919c
Document transient status read recovery
kvz Sep 29, 2026
a35909e
Report unconfirmed cleanup for aborted Assembly connections
kvz Sep 29, 2026
e0faa1a
Reject writes to stopped Assemblies and drain bounded tus responses
kvz Sep 29, 2026
bc933ee
Reconcile finished uploads after temporary tus resource cleanup
kvz Sep 29, 2026
f9d28b1
Confirm transfer receipt independently of later processing outcome
kvz Sep 29, 2026
ee28c68
Require explicit admission before bypassing a configured proxy path
kvz Sep 29, 2026
b9af56e
Treat aborted requests as finite workflow outcomes
kvz Sep 29, 2026
49ccde5
Regenerate finite workflow outcomes and cancellation failure contract
kvz Sep 29, 2026
9af9601
Clarify workflow destination and timeout boundaries
kvz Sep 29, 2026
3abc2e2
Verify source-owned upload identity and finished receipts
kvz Oct 4, 2026
821dc73
Harden contract workflow identity and ambiguous-response recovery
kvz Oct 4, 2026
de5fd25
Retain both cancellation and confirmation failures
kvz Oct 4, 2026
0a4f5f7
Expose both cancellation failure diagnostics
kvz Oct 4, 2026
93dedeb
Retain cancellation failure after unusable confirmation
kvz Oct 4, 2026
55ff13d
Preserve future Assembly errors in generated native readers
kvz Oct 6, 2026
c2144cf
Preserve resumed upload fields and aborted example cleanup
kvz Oct 6, 2026
c7a5235
Clarify draft OAuth limits and regenerate reader proof coverage
kvz Oct 6, 2026
e0976c1
Pass upload deadline to checkpoint persistence
kvz Oct 6, 2026
5362ca6
Retain canary cleanup after aborted Assembly requests
kvz Oct 6, 2026
db2348e
Honor grant-specific account authentication
kvz Oct 6, 2026
d152a41
Clarify credentialless transport ownership
kvz Oct 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/contract/client_generated.go linguist-generated=true
/contract/coverage.json linguist-generated=true
/contract/manifest.json linguist-generated=true
/contract/wire-vectors.json linguist-generated=true
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ test-examples:
go build ./examples/...

test-package:
go test -v -coverprofile=coverage.out -covermode=atomic .
go test -v -coverprofile=coverage.out -covermode=atomic . ./contract ./examples/contract-workflow
env -u GOROOT go test -trimpath . -run '^TestContractImportIsOptIn$$' -count=1

test: test-package test-examples

Expand Down
171 changes: 171 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,177 @@ func main() {
}
```

## Contract-generated API methods (experimental)

Import `github.com/transloadit/go-sdk/contract` explicitly to use the typed low-level client.
Existing SDK consumers do not compile the generated package unless they import it. Existing
APIs remain available alongside the opt-in package.

Token exchanges follow the contract's grant-specific authentication. `client_credentials`
requires an Auth Key and secret. Authorization-code and refresh-token exchanges send no account
credentials or configured cookies, even on a signed/bearer client; supply the grant's proof in
the request body. Use `contract.Config{NoAccountCredentials: true}` when you have no Auth Key.
Protected operations fail locally on that client; an empty configuration remains an error.
Each exchange makes one attempt and does not follow redirects. Login, consent, token storage
and automatic refresh belong to your application or OAuth library, not this low-level client.
Cookie-jar isolation applies to all operations whose resolved account authentication is `none`.
Custom HTTP transports remain trusted application code and must not inject credentials.

```go
api, err := contract.NewClient(contract.Config{AuthKey: key, AuthSecret: secret})
if err != nil {
return err
}
templates, err := api.ListTemplates(ctx, contract.ListTemplatesInput{})
```

The generated namespace covers ordinary HTTP operations. It returns the HTTP response, not a
completed Assembly. `CreateAssembly.Files` supports multipart uploads. The explicit
`WaitForAssembly(ctx, contract.AssemblyWorkflowOptions{AssemblyID: id})` and
`CancelAndWaitForAssembly(ctx, options)` workflows safely follow the owning uploader and return
only after confirming a terminal status. Check `status.GetOk() == "ASSEMBLY_COMPLETED"` for
successful processing; cancellation and processing errors are terminal too.
Future nonempty error strings are terminal failures too. Known codes are not exhaustive;
`status.WithError.Error` preserves the exact value as a string-based type. Treat it as untrusted
text when displaying or logging it. Unknown `ok` values and malformed or contradictory statuses
remain invalid.

For a resumable upload, set the top-level `CreateAssemblyInput.Fields` (not `Params.Fields`) to
`map[string]string{"num_expected_upload_files": "1"}`, then call
`UploadAssemblyFile(ctx, contract.AssemblyUploadOptions{AssemblyID: id, Reader: file,
Size: size, Filename: "example.jpg", OnSession: persistSession})`.
`Reader` is a caller-owned `io.ReaderAt`, such as an open `*os.File`; keep it open and unchanged
until the call returns. The SDK hashes and uploads in bounded chunks, not one whole-file buffer.

`OnSession(ctx context.Context, session contract.AssemblyUploadSession) error` receives the upload's
deadline-bound context and a JSON-serializable checkpoint before the first file bytes are sent.
Save it securely; it contains a secret capability URL and must not be logged or shared with
other users. Return an error if persistence fails. A fresh client can then call
`ResumeAssemblyFile(ctx, input, savedSession)` using the original file. It checks the file's
SHA-256, upload metadata and destination, reads the server offset, and never creates a second
upload. Already completed transfers send no more bytes. Transfer completion is not Assembly
processing success: call `WaitForAssembly` afterward and inspect its terminal status.

`ChunkSize` defaults to 5 MiB, `Timeout` to five minutes and `MaxRetries` to five recovery attempts.
The timeout includes hashing the complete file, discovery, session persistence, transfer and backoff.
Choose a larger `Timeout` for files or connections that cannot finish that work within five minutes.
Caller-owned `ReaderAt` and synchronous `OnSession` code must return promptly; the SDK cannot
interrupt that code. Use the callback's context for persistence I/O so both caller cancellation
and the upload deadline are honored.
A pointer to zero disables recovery. Ambiguous PATCH failures require a fresh offset read before
more bytes are sent. Safe discovery and tus recovery share that budget and honor `Retry-After`.
Creation never retries; if its response is lost before a session is saved,
inspect the Assembly before starting another upload. `errors.As` with `*contract.AssemblyUploadError`
provides the saved `Session` when available; `errors.Is` still recognizes caller cancellation.
An observed terminal Assembly prevents new upload writes; `AssemblyCode` preserves
that status. A resume can still confirm an already complete transfer without sending more bytes,
even if later Assembly processing failed. Use `WaitForAssembly` to check processing separately.
If HEAD returns 404 after temporary upload cleanup, the workflow refreshes Assembly status and
requires one finished `tus_uploads` receipt matching the saved URL, filename, fieldname, size and
completed offset. Missing or mismatched receipts remain errors; no replacement upload is created.
If your proxy rewrites upload capability URLs, it must also rewrite their receipt URLs consistently.
The SDK does not infer that a proxy URL and a different uploader URL identify the same resource.
Stopping locally does not delete bytes or cancel the Assembly. Use `CancelAndWaitForAssembly`
explicitly when abandoning the job. Deferred lengths, concatenation and non-seekable streams are
not supported by these bounded fixed-size helpers.
These helpers require single-segment upload IDs without percent escapes. A different ID format is
rejected before the upload session is persisted, even if the server already created that resource.
Workflow `Timeout` defaults to five minutes and `Interval` to one second. An earlier context
deadline wins. Cancel-and-wait sends one cancellation attempt, then polls; a timeout or caller
cancellation stops waiting but does not prove remote cleanup. Private deployments may set
`Config.AssemblyOrigins` to preconfigured trusted origins, never values copied from response data.
These origins are additional to the public Transloadit uploader hosts admitted by the contract.
A custom `Origin` is not an exclusive egress policy: returned public uploaders can still receive
workflow requests directly. Enforce mandatory proxy routing in your network or custom transport.
When a response points back to the exact configured `Origin` plus the Assembly path, its proxy
prefix is retained. Prefixes are never inferred from an untrusted response URL.
Uploader requests carry no credentials or cookie jar; redirects and changed owners are rejected.
`Config.HTTPClient.Transport` handles both API and uploader/tus requests, so the same observation
or fault-injection transport works for the whole workflow.
Status GETs retry transient network failures and HTTP 429/5xx within the overall deadline,
honoring `Retry-After`, including when an HTTP error body is interrupted. An HTTP error or lost
response from DELETE can be followed by a status GET to confirm a terminal race within the remaining
workflow deadline; DELETE is never retried.
If that GET fails or returns an unusable status, `errors.Is` and `errors.As` can inspect both
the DELETE error and the read or validation error, with the DELETE
failure taking precedence when their concrete types match. Caller cancellation and the overall
deadline still take precedence over this combined diagnostic.
Use `errors.As` with `*contract.AssemblyCancellationConfirmationError` to access its
`CancellationError` and `ConfirmationError` separately, including when both are `ResponseError`.
`REQUEST_ABORTED` is a finite, unsuccessful outcome: waiting returns that typed status without
an error or indefinite polling. It does not mean processing succeeded or all background work
has stopped. An explicit cancel still contacts the owning uploader once, preserving a completed
or failed outcome if work already ended. If that owner cannot be discovered after `REQUEST_ABORTED`,
cancel-and-wait returns `ErrAssemblyWorkflowUnconfirmed` (check with `errors.Is`). A later GET with
`REQUEST_ABORTED` does not hide a failed cancellation request. No terminal response promises that
worker cleanup or billing has already stopped.
Fixed-size upload and resume use the new contract client; SSE and Webhook receivers remain
separate work. Go 1.15 remains supported.
Smart CDN signing remains a local helper in the root package; see the
[Smart CDN example](examples/smart-cdn-signature/main.go).

Set `BearerToken` instead of Auth Key credentials to use an existing token; the client never mints
one implicitly. Pass raw, unencoded path values. Signed requests authenticate the exact serialized
`params` bytes sent. Multipart files transfer `io.ReadCloser` ownership to the API call, which closes
every supplied stream before returning, including on cancellation or early responses. `Close` must
unblock a concurrent `Read`; wrap in-memory readers with `ioutil.NopCloser`. The default client has
no total upload deadline: use a context deadline or an explicitly configured HTTP client. Optional
fields are pointers so `false` and `0` are not lost. Nullable fields use generated union wrappers so explicit
`null` differs from omission. Set exactly one union choice (or its null choice). These wire types
are not a full JSON Schema validator. `Integer` uses signed 64-bit storage and accepts integral
decimal/exponent JSON representations without rounding. Unknown response fields are tolerated;
fields explicitly modeled as additional properties are retained. Union decoding selects an alternative
that retains the fields represented by the successful alternatives, or fails if none can retain them
all. Generated union members use schema discriminants such as `ImageResize` where available.
Public type aliases can be followed with `go doc`; comments come from the contract. Assembly unions
provide `GetAssemblyId()`, `GetOk()` and `GetResults()` so callers need not guess which success
variant was decoded. Scalar wrappers offer methods such as `GetString()`. These accessors return
zero values for absent/null fields or an inactive scalar variant; inspect the variant pointers when
the distinction matters. Both wire spellings of the legacy Assembly ID remain represented:
`AssemblyId` is `assembly_id`, and `AssemblyIdCamelCase` is `assemblyId`.

`ResponseError` retains status and decoded JSON without printing response data. Use
`errors.As(err, &response)` with `var response *contract.ResponseError`, then `response.Code()`
to classify recognized public codes. Unknown or malformed codes return an empty string. For example,
`TEMPLATE_NOT_FOUND` uses HTTP 400, not 404; the SDK preserves that API behavior. Redirects are
rejected and responses are limited to 128 MiB. Union decoding rejects values deeper than 64 nested
objects/arrays to bound native decoding work. This is a client resource limit, not an API schema rule.

The [complete generated-client example](examples/contract-workflow/main.go) builds typed Template
Steps, uploads an image, polls to completion and reads a typed result. With server-side
`TRANSLOADIT_KEY` and `TRANSLOADIT_SECRET` set, run `go run ./examples/contract-workflow ./image.jpg`
from this checkout. It creates one billable Assembly and removes its temporary Template; completed
results expire normally. The example uses the contract-client wait/cancel workflows and reports
cleanup failures. It does not automatically retry writes or replace the existing upload API.

Maintainers: `contract/client_generated.go` and its JSON manifests come from API2. Never edit them
directly. From the matching API2 checkout's `api2/` directory run `./bin/cli.ts contracts sdks
--target go --output <go-sdk>/contract`, then repeat with `--check`. The manifest records the exact
contract digest. Native behavior belongs in `contract/transport.go` and its tests; API2 pins these
sources for regeneration and local-server acceptance. Update that pin when changing them. The
coverage report keeps missing targets and protocols visible; generated does not mean runtime-proven.

The experimental types use API2-owned domains such as `AssemblySteps`, `ApiError` and
`JsonDocument`. Their names do not depend on which endpoint happens to be generated first. This
unreleased draft intentionally replaces earlier operation-prefixed type names; existing SDK APIs
are unchanged. Model naming belongs in API2's `api2/lib/contract/schemaModels.ts`, not local aliases.

API2 also owns `contract/workflow-vectors.json`. `go test -race . -run '^TestSharedWorkflow' -v`
exercises contract-client wait/cancel/upload/resume and local Smart CDN signing with the same
observations used by the Node SDK. Tests use synthetic credentials and loopback HTTP servers;
adapters may not implement missing SDK polling, retries or signing. CI runs these cases as part of
the root tests. The new contract client's resume case is required and no longer skipped; the old
multipart API is unchanged. New unclassified cases fail. Passing fixture tests is not proof of
every live-server behavior or support for every tus extension. Change shared scenarios
in API2's `api2/lib/contract/sdk/workflowVectors.ts` and regenerate rather than editing the JSON.

The workflow tests also correct existing SDK behavior: Smart CDN signing now uses the server's
path/query encoding and UTF-16 key order, preserves an explicit expiry's milliseconds, and replaces
stale authentication fields. Default expiry also retains millisecond precision, so generated URLs
can differ from earlier SDK versions. Waiting continues through `ASSEMBLY_REPLAYING` and preserves
identifiable context cancellation/deadline errors. A canceled or failed Assembly remains a terminal
result, not a successful completion.

## Example

For fully working examples on how to use templates, non-blocking processing and more, take a look at [`examples/`](https://github.com/transloadit/go-sdk/tree/main/examples).
Expand Down
Loading
Loading