feat(storage): add analytics bucket and Iceberg namespace/table management (alpha) - #1183
feat(storage): add analytics bucket and Iceberg namespace/table management (alpha)#1183grdsdev wants to merge 13 commits into
Conversation
Implements the storage.vectors client by hand, using the same StorageApi/StorageHTTPSession HTTP stack as the rest of the Storage module, instead of a generated OpenAPI client.
Verified against a local Supabase stack (supabase start): create, get, list (incl. prefix filtering), and delete, plus the 404 error shape for a missing bucket.
Adds storage.vectors.from(_:) for bucket-scoped index management (createIndex/getIndex/listIndexes/deleteIndex) and .index(_:) for vector data operations (putVectors/getVectors/listVectors/ queryVectors/deleteVectors), implemented by hand on the existing StorageApi HTTP stack, matching supabase-js's storage-js vectors client and the supabase/storage backend's wire format.
The wire value is a raw UNIX timestamp (seconds), not an ISO8601 string, so Int obscured the semantics. Matches Session.expiresAt's existing TimeInterval convention for UNIX timestamps in this codebase.
Same fix as the base branch's VectorBucket.creationTime: the wire value is a raw UNIX timestamp (seconds), not an ISO8601 string.
…ement (alpha)
Adds storage.analytics.{createBucket,listBuckets,deleteBucket,from} and,
via AnalyticsBucketClient/IcebergNamespaceClient, Iceberg namespace CRUD
(createNamespace/getNamespace/listNamespaces/namespaceExists/deleteNamespace)
and table CRUD (createTable/getTable/listTables/tableExists/deleteTable).
Implemented by hand on the existing StorageApi HTTP stack, matching
supabase-js's StorageAnalyticsClient for the public API shape and the
supabase/storage backend's rest-catalog-client.ts types for the exact
wire format. Also fixes StorageError to parse the nested
{error: {message, type, code}} shape used by the analytics/Iceberg
error formatter, in addition to the flat shape used elsewhere.
Table commit/schema-evolution (updateTable) is out of scope for this
pass, matching the backend's own additionalProperties:true (opaque)
response typing for that endpoint.
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Composition instead of subclassing StorageApi: the client now holds a StorageApi dependency passed in at init, rather than inheriting execute() and configuration directly. SupabaseStorageClient.vectors now passes itself as that dependency, so custom headers set via setHeader() are shared between the main client and .vectors.
…ndexes # Conflicts: # Sources/Storage/StorageVectorsClient.swift
…over StorageApi Same composition-over-inheritance change as StorageVectorsClient: these now hold a StorageApi dependency passed in at init instead of subclassing it, threaded through from(_:)/index(_:).
…eApi Same composition-over-inheritance change as the vector clients: AnalyticsClient, AnalyticsBucketClient, and IcebergNamespaceClient now hold a StorageApi dependency passed in at init instead of subclassing it, threaded through from(_:)/namespace(_:).
|
The following capabilities are marked
These may have been renamed, removed, or never registered. Please update the capability matrix. |
Coverage Report for CI Build 31160582079Warning Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes. Warning No base build found for commit Coverage: 83.832%Details
Uncovered Changes
Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats
💛 - Coveralls |
Summary
Stacked on #1181 (which is stacked on #1153). Implements SDK-1301: Supabase Storage's alpha analytics buckets (Iceberg-backed) feature.
storage.analytics.{createBucket,listBuckets,deleteBucket}— analytics bucket CRUD.storage.analytics.from(_:) -> AnalyticsBucketClient— bucket-scoped Iceberg namespace management:createNamespace,getNamespace,listNamespaces,namespaceExists,deleteNamespace.AnalyticsBucketClient.namespace(_:) -> IcebergNamespaceClient— namespace-scoped table management:createTable,getTable,listTables,tableExists,deleteTable.Implemented by hand on the existing
StorageApi/StorageHTTPSessionHTTP stack (no code generation), matching:@supabase/storage-js'sStorageAnalyticsClientfor the bucket CRUD shape.supabase/storagebackend's ownrest-catalog-client.tsTypeScript types (TableMetadata,Schema,PartitionSpec,SortOrder, etc.) for the exact wire format — this is the backend's authoritative, narrower-than-generic-Iceberg-spec contract (e.g. nosort-orders/refs/snapshotsonTableMetadatain this backend's actual implementation), soIcebergTableMetadatamodels exactly what the server returns rather than the full Apache Iceberg spec.Notable fix:
StorageErrornested error shapeThe analytics/Iceberg routes run through a different backend error formatter than the rest of Storage:
{ error: { message, type, code } }instead of the flat{ error, message, statusCode }shape used everywhere else (confirmed by readingerror-handler.tsvs.iceberg/index.ts'ssetErrorHandlercall).StorageErrornow tries the flat shape first and falls back to the nested one, so callers get a correctly-populatedStorageErrorregardless of which shape the backend used — no new public API, no behavior change for existing endpoints.API design notes
IcebergTypeis a recursiveindirect enum(primitive/structType/listType/mapType) withExpressibleByStringLiteralfor primitives, soIcebergStructField(id: 1, name: "id", type: "long", required: true)reads naturally.IcebergSortDirection/IcebergNullOrder/AnalyticsBucketSortColumnfollow this module's existing open string-backed-struct convention (ResizeMode/ImageFormat/SortOrder);listBucketsreuses the existingSortOrdertype directly.IcebergNamespace.nameand table identifiers are flattened to plainString(not[String]) since this backend only supports single-level namespaces — the wire format still sends/receives arrays for forward-compatibility with the general Iceberg spec, but exposing that generality here would just be friction for a constraint the backend itself enforces.namespaceExists/tableExistsreturnBool(catching 400/404), matching the existingStorageFileApi.exists(path:)convention instead of forcing callers to catch exceptions for a common existence check.AnalyticsClient/AnalyticsBucketClient/IcebergNamespaceClientarestructs holding aStorageApidependency (composition, matching feat(storage): add vector bucket CRUD (alpha) #1153/feat(storage): add vector index and vector data operations (alpha) #1181's vector clients), notStorageApisubclasses.from(_:)/namespace(_:)thread that same dependency through each scoping step, andSupabaseStorageClient.analyticspassesselfas the dependency.Out of scope
updateTable(commit-table-changes / schema evolution): the backend itself types this endpoint's response asadditionalProperties: true(opaque), and the requirements/updates payload is a complex tagged-union shape. Not part of the SDK-1301 capability list either.iceberg.getConfig: bootstraps a generic external Iceberg client (e.g. pyiceberg); not needed since this SDK's namespace/table clients already know their own base URL and headers.Test plan
swift buildswift test(full suite, 1100 tests)StorageErrorshapes.ICEBERG_INTEGRATION_TESTSflag (not the defaultINTEGRATION_TESTS): confirmed viasupabase startthat analytics/Iceberg routes 404 on the CLI's local dev stack today — the backend requiresICEBERG_ENABLED=trueplus a separate Iceberg REST Catalog server and warehouse/shard config (per the backend's own.env.test.sample) that isn't wired into the CLI. This avoids breaking CI'sintegration-testsjob, which runsINTEGRATION_TESTS=1against the same CLI stack../scripts/format.sh./scripts/spell-check.sh./scripts/test-docs.sh