spike(codegen): Smithy → OpenAPI → swift-openapi-generator pipeline for Storage + Functions#1047
Draft
grdsdev wants to merge 32 commits into
Draft
spike(codegen): Smithy → OpenAPI → swift-openapi-generator pipeline for Storage + Functions#1047grdsdev wants to merge 32 commits into
grdsdev wants to merge 32 commits into
Conversation
…odels - Fix storage.smithy: remove @httpPayload from list members (AWS restJson1 constraint), add @idempotent to PUT/DELETE operations, suppress UnexpectedPayload danger for DeleteObjects - Update Makefile generate-smithy to copy build output to smithy/output/openapi/ - Update Makefile to use ~/bin/swift-openapi-generator (built from source) - Add openapi-generator-config.yaml for Storage and Functions - Generate StorageService.openapi.json and FunctionsService.openapi.json - Generate Sources/Storage/Generated/{Types,Client}.swift - Generate Sources/Functions/Generated/{Types,Client}.swift Build currently fails with "multiple producers" for Types.swift — existing Types.swift in each module conflicts with generated Types.swift; resolved in Task 4/5 when Package.swift targets are restructured.
- Add package init accepting ClientTransport for testing - Delegate listBuckets/getBucket/createBucket/updateBucket/emptyBucket/deleteBucket to generated Client - Add BucketConversions.swift for Bucket ↔ generated schema conversion - Add MockTransport for unit testing generated client integration - Add StorageClientGeneratedTests with Swift Testing - Add OpenAPIURLSession to Storage target, OpenAPIRuntime to StorageTests target
- Remove @_spi(Generated) from MockTransport — ClientTransport is public API - Extract normalizeClientInfoHeaders and resolveStorageURL static helpers to eliminate ~60 lines of duplication between the two package inits - Promote supabase host regex to a static let to avoid repeated compilation - Remove dead #if canImport(Darwin) block (both branches were identical) - Add ISO8601DateFormatter as nonisolated(unsafe) static let in BucketConversions - Add owner:"" comment explaining why the field is absent from generated schema - Add storageError(statusCode:body:) helper; update all .undocumented handlers to extract body detail for more informative error messages - Add Swift Testing tests for getBucket, createBucket, updateBucket, emptyBucket, and deleteBucket (happy path + error path each)
…e config YAMLs Replace bare URLSessionTransport with SupabaseClientTransport in the production StorageClient init so that Authorization headers are injected from the token provider. Also exclude openapi-generator-config.yaml from the Storage and Functions SPM targets to eliminate unhandled-file warnings.
…to production init
…to production init
Contributor
|
…th streaming file body
Replace MultipartUploadEngine.makeTask in upload/update methods with direct calls to client.generatedClient.UploadObject / UpdateObject. Replace hand-rolled URLRequest HTTP in TUSUploadEngine with CreateTusUpload / GetUploadOffset / UploadChunk generated operations. Delete the spike helper file StorageFileApi+GeneratedUpload.swift.
Remove the hand-written _HTTPClient path from FunctionsClient. Both invoke() and invokeStream() now go exclusively through the generated InvokeFunction operation. invokeStream() bridges the HTTPBody AsyncSequence from the generated response body to AsyncThrowingStream<UInt8> with no intermediate buffering — bytes are forwarded chunk-by-chunk as they arrive. Spike limitations (documented in class-level doc comment): - POST-only: FunctionInvokeOptions.method / .query not forwarded - HTTPURLResponse is fabricated (status code only, no headers)
…peration Smithy model Smithy requires a fixed HTTP method per operation, so we model all five methods Supabase Edge Functions accept as separate operations on the same path. FunctionsClient.invoke() and invokeStream() now dispatch to the appropriate generated method based on FunctionInvokeOptions.method. Generated Types.swift shares InvokeFunctionOutput and InvokeFunctionBodyInput across POST/PUT/PATCH/DELETE to avoid duplication. GET gets its own input type (no body — Smithy rejects @httpPayload on GET). The invokeGeneratedClient() helper centralises the dispatch switch so both invoke() and invokeStream() stay focused on response handling.
…odel - Rewrite Makefile to use sync-models target pulling pre-built OpenAPI artifacts from supabase/sdk instead of maintaining local Smithy models - Add generate-swift-postgrest target and PostgREST openapi-generator-config - Add DatabaseService.openapi.json to spike's output/openapi/ - Commit generated Client.swift (912 lines) and Types.swift (2057 lines) for the PostgREST database service Key finding: the @httpQueryParams StringMap for `filters` is emitted as a single named query param "filters" by Smithy → OpenAPI, then serialized as one param by swift-openapi-generator. PostgREST requires each filter key to be its own query param (?id=eq.5&name=like.*foo*). This confirms the transport layer (path, method, fixed params, headers, body, Content-Range) is fully codegen-able; only the query-builder that populates the filter map stays hand-written — which was already the design intent.
…in supabase/sdk) Generated from typespec/openapi artifacts in supabase/sdk PR #53 for side-by-side comparison against the Smithy-generated clients. Line counts: Storage: 1376+3963 = 5339 (Smithy: 1773+4729 = 6502) Functions: 494+1134 = 1628 (Smithy: 253+ 290 = 543) PostgREST: 745+1724 = 2469 (Smithy: 912+2057 = 2969) Total: = 9436 (Smithy: = 10014)
…c model Updated postgrest.tsp in supabase/sdk (commit 720bc1d) splits named fixed params from the dynamic filters map. Changes in generated output: - select, order, limit (Swift.Int), offset (Swift.Int) are now separate typed members on each table operation query struct - filters: filtersPayload (additionalProperties[String:String]) is its own member, not mixed with fixed params - FilterOperator enum (24 operators) generated from TypeSpec enum - RpcOperations_rpcGet added as a separate GET operation for RPC - 2969→3107 lines (TypeSpec now matches Smithy coverage)
…fixed TypeSpec models Storage: Objects_upload (POST) and Objects_update (PUT) now generated with native multipart/form-data bodies (cacheControl + file parts) — no patch script needed. Smithy required patch-openapi.py to inject these manually. PostgREST: select/order/limit(Int)/offset(Int) are separate typed query members; filters is a separate additionalProperties map; FilterOperator enum and RpcOperations_rpcGet are present. Storage line count: 1376→1646 Client + 3963→4443 Types (multipart added) All remaining gaps from initial comparison are now closed.
…equest bodies Follows supabase/sdk commit 3cd468b. Replaces OpenAPIValueContainer (any-JSON) with HTTPBody (streaming binary) on all response and request bodies — now identical to the Smithy-generated output in this respect.
…ected body types Functions invoke Body enum now has four cases: .json(OpenAPIValueContainer) — application/json .binary(HTTPBody) — application/octet-stream .plainText(HTTPBody) — text/plain .urlEncodedForm(String) — application/x-www-form-urlencoded PostgREST request body reverted to .json(OpenAPIValueContainer) since PostgREST accepts JSON row arrays/objects; response body stays .binary(HTTPBody) so the SDK layer decodes into the caller's Decodable type.
… body Response Body enum now has three cases matching the response content-type: .json(OpenAPIValueContainer) — application/json .binary(HTTPBody) — application/octet-stream .plainText(HTTPBody) — text/plain Request Body enum unchanged (json/binary/plainText/urlEncodedForm).
…/* content type) Replace the @SharedRoute multi-variant approach with a single operation per HTTP method. Both request body and response body are now HTTPBody (case any), generic enough for any content type: JSON, binary, text, event-stream, etc.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is an exploratory spike proving the full Smithy IDL → OpenAPI →
swift-openapi-generatorcodegen pipeline for the Storage and Functions clients.codegen/make codegenlistBuckets,getBucket,createBucket,deleteBucket,updateBucket,emptyBucket) to the generated clientinvoketo the generated clientSupabaseMiddleware(ClientMiddleware) to consolidate static header injection (apikey,X-Client-Info) and dynamic Bearer token injectionx-relay-erroron genuine 200 detection gap viaRelayErrorMiddleware(in progress)Status
Work in progress — middleware wiring for Functions not yet complete. The branch is being developed iteratively.
Known limitations / out of scope
HTTPURLResponseon the Functions generated path (no real response headers exposed) — structural change required, deferredTest plan
SupabaseMiddlewareunit tests pass (5 tests)