Skip to content

Commit cdf66ff

Browse files
committed
spike(typespec): simplify Functions invoke to generic HTTPBody with */* content type
Remove @SharedRoute multi-variant approach in favour of a single operation per HTTP method. A variable contentType header alongside bytes body causes TypeSpec to emit content-type */* in OpenAPI, which swift-openapi-generator maps to case any(HTTPBody) - generic enough for JSON, binary, text, event-stream, etc. This also removes the need for patch-openapi.py entirely.
1 parent 6bc9f4e commit cdf66ff

2 files changed

Lines changed: 54 additions & 134 deletions

File tree

typespec/functions.tsp

Lines changed: 45 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -21,86 +21,70 @@ model FunctionsError {
2121
message?: string;
2222
}
2323

24-
// Three response models cover the content types a function can return.
25-
// Each @sharedRoute variant is paired with one response model; TypeSpec merges
26-
// them into a single OpenAPI operation with multiple response content entries,
27-
// which swift-openapi-generator maps to a Body enum with one case per type.
28-
model JsonResponse {
24+
// A single response model covers all content types a function can return.
25+
// Using a variable contentType header alongside bytes makes TypeSpec emit
26+
// content-type */* in OpenAPI, which swift-openapi-generator maps to
27+
// case any(HTTPBody)
28+
// This is generic enough for JSON, binary, text, event-stream, etc.
29+
model FunctionResponse {
2930
@statusCode statusCode: 200;
30-
@body body: unknown;
31-
}
32-
33-
model OctetStreamResponse {
34-
@statusCode statusCode: 200;
35-
@header contentType: "application/octet-stream";
31+
@header contentType: string;
3632
@body body: bytes;
3733
}
3834

39-
model TextResponse {
40-
@statusCode statusCode: 200;
41-
@header contentType: "text/plain";
42-
@body body: string;
43-
}
44-
4535
// ─── Operations ────────────────────────────────────────────────────────────
4636
//
47-
// TypeSpec, like Smithy, requires a fixed HTTP method per operation.
48-
// FunctionsClient.invoke() dispatches to the appropriate generated method
49-
// at runtime based on FunctionInvokeOptions.method.
50-
//
51-
// Advantage over Smithy: TypeSpec's interface groups these naturally vs
52-
// Smithy's flat list at the service level.
37+
// Each HTTP method is a single operation. The request body uses a variable
38+
// contentType header + bytes, which TypeSpec emits as content-type */* in
39+
// OpenAPI → HTTPBody in Swift. Callers set the actual Content-Type header
40+
// via middleware when constructing the request.
5341
//
5442
// Known limitation: FunctionInvokeOptions.query (dynamic query params)
5543
// cannot be modelled here — requires middleware URL-rewriting in each SDK.
56-
//
57-
// Each HTTP method is expressed as a group of @sharedRoute operations — one per
58-
// supported request body content type — that TypeSpec merges into a single OpenAPI
59-
// operation with multiple requestBody.content entries. swift-openapi-generator
60-
// maps those to a Body enum with one case per content type:
61-
// .json(OpenAPIValueContainer)
62-
// .binary(HTTPBody)
63-
// .plainText(String)
64-
// .urlEncoded(String)
65-
//
66-
// The operationId override (@operationId) avoids the auto-generated
67-
// concatenated name TypeSpec would otherwise produce for sharedRoute groups.
6844

6945
@route("/functions/v1")
7046
interface FunctionInvocations {
71-
// GET has no request body — functions called with GET pass params in the URL.
47+
// GET no request body; params pass in the URL
7248
@get
7349
@route("/{functionName}")
7450
invokeGet(
7551
@path functionName: string,
7652
@header("x-region") region?: string,
77-
): OctetStreamResponse | FunctionsError;
53+
): FunctionResponse | FunctionsError;
7854

79-
// POST — one @sharedRoute variant per supported content type.
80-
@sharedRoute @post @route("/{functionName}") @operationId("FunctionInvocations_invokePost")
81-
invokePostJson(@path functionName: string, @header("x-region") region?: string, @body body?: unknown): JsonResponse | FunctionsError;
82-
@sharedRoute @post @route("/{functionName}") invokePostBinary(@path functionName: string, @header("x-region") region?: string, @header contentType: "application/octet-stream", @body body?: bytes): OctetStreamResponse | FunctionsError;
83-
@sharedRoute @post @route("/{functionName}") invokePostText(@path functionName: string, @header("x-region") region?: string, @header contentType: "text/plain", @body body?: string): TextResponse | FunctionsError;
84-
@sharedRoute @post @route("/{functionName}") invokePostForm(@path functionName: string, @header("x-region") region?: string, @header contentType: "application/x-www-form-urlencoded", @body body?: string): JsonResponse | FunctionsError;
55+
@post
56+
@route("/{functionName}")
57+
invokePost(
58+
@path functionName: string,
59+
@header("x-region") region?: string,
60+
@header contentType: string,
61+
@body body?: bytes,
62+
): FunctionResponse | FunctionsError;
8563

86-
// PUT
87-
@sharedRoute @put @route("/{functionName}") @operationId("FunctionInvocations_invokePut")
88-
invokePutJson(@path functionName: string, @header("x-region") region?: string, @body body?: unknown): JsonResponse | FunctionsError;
89-
@sharedRoute @put @route("/{functionName}") invokePutBinary(@path functionName: string, @header("x-region") region?: string, @header contentType: "application/octet-stream", @body body?: bytes): OctetStreamResponse | FunctionsError;
90-
@sharedRoute @put @route("/{functionName}") invokePutText(@path functionName: string, @header("x-region") region?: string, @header contentType: "text/plain", @body body?: string): TextResponse | FunctionsError;
91-
@sharedRoute @put @route("/{functionName}") invokePutForm(@path functionName: string, @header("x-region") region?: string, @header contentType: "application/x-www-form-urlencoded", @body body?: string): JsonResponse | FunctionsError;
64+
@put
65+
@route("/{functionName}")
66+
invokePut(
67+
@path functionName: string,
68+
@header("x-region") region?: string,
69+
@header contentType: string,
70+
@body body?: bytes,
71+
): FunctionResponse | FunctionsError;
9272

93-
// PATCH
94-
@sharedRoute @patch @route("/{functionName}") @operationId("FunctionInvocations_invokePatch")
95-
invokePatchJson(@path functionName: string, @header("x-region") region?: string, @body body?: unknown): JsonResponse | FunctionsError;
96-
@sharedRoute @patch @route("/{functionName}") invokePatchBinary(@path functionName: string, @header("x-region") region?: string, @header contentType: "application/octet-stream", @body body?: bytes): OctetStreamResponse | FunctionsError;
97-
@sharedRoute @patch @route("/{functionName}") invokePatchText(@path functionName: string, @header("x-region") region?: string, @header contentType: "text/plain", @body body?: string): TextResponse | FunctionsError;
98-
@sharedRoute @patch @route("/{functionName}") invokePatchForm(@path functionName: string, @header("x-region") region?: string, @header contentType: "application/x-www-form-urlencoded", @body body?: string): JsonResponse | FunctionsError;
73+
@patch
74+
@route("/{functionName}")
75+
invokePatch(
76+
@path functionName: string,
77+
@header("x-region") region?: string,
78+
@header contentType: string,
79+
@body body?: bytes,
80+
): FunctionResponse | FunctionsError;
9981

100-
// DELETE
101-
@sharedRoute @delete @route("/{functionName}") @operationId("FunctionInvocations_invokeDelete")
102-
invokeDeleteJson(@path functionName: string, @header("x-region") region?: string, @body body?: unknown): JsonResponse | FunctionsError;
103-
@sharedRoute @delete @route("/{functionName}") invokeDeleteBinary(@path functionName: string, @header("x-region") region?: string, @header contentType: "application/octet-stream", @body body?: bytes): OctetStreamResponse | FunctionsError;
104-
@sharedRoute @delete @route("/{functionName}") invokeDeleteText(@path functionName: string, @header("x-region") region?: string, @header contentType: "text/plain", @body body?: string): TextResponse | FunctionsError;
105-
@sharedRoute @delete @route("/{functionName}") invokeDeleteForm(@path functionName: string, @header("x-region") region?: string, @header contentType: "application/x-www-form-urlencoded", @body body?: string): JsonResponse | FunctionsError;
82+
@delete
83+
@route("/{functionName}")
84+
invokeDelete(
85+
@path functionName: string,
86+
@header("x-region") region?: string,
87+
@header contentType: string,
88+
@body body?: bytes,
89+
): FunctionResponse | FunctionsError;
10690
}

typespec/openapi/@typespec/openapi3/openapi.Supabase.Functions.yaml

Lines changed: 9 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ paths:
2222
'200':
2323
description: The request has succeeded.
2424
content:
25-
application/octet-stream:
25+
'*/*':
2626
schema:
2727
type: string
2828
format: binary
@@ -49,18 +49,10 @@ paths:
4949
'200':
5050
description: The request has succeeded.
5151
content:
52-
application/json:
53-
schema:
54-
anyOf:
55-
- {}
56-
- {}
57-
application/octet-stream:
52+
'*/*':
5853
schema:
5954
type: string
6055
format: binary
61-
text/plain:
62-
schema:
63-
type: string
6456
default:
6557
description: An unexpected error response.
6658
content:
@@ -70,18 +62,10 @@ paths:
7062
requestBody:
7163
required: false
7264
content:
73-
application/json:
74-
schema: {}
75-
application/octet-stream:
65+
'*/*':
7666
schema:
7767
type: string
7868
format: binary
79-
text/plain:
80-
schema:
81-
type: string
82-
application/x-www-form-urlencoded:
83-
schema:
84-
type: string
8569
put:
8670
operationId: FunctionInvocations_invokePut
8771
parameters:
@@ -99,18 +83,10 @@ paths:
9983
'200':
10084
description: The request has succeeded.
10185
content:
102-
application/json:
103-
schema:
104-
anyOf:
105-
- {}
106-
- {}
107-
application/octet-stream:
86+
'*/*':
10887
schema:
10988
type: string
11089
format: binary
111-
text/plain:
112-
schema:
113-
type: string
11490
default:
11591
description: An unexpected error response.
11692
content:
@@ -120,18 +96,10 @@ paths:
12096
requestBody:
12197
required: false
12298
content:
123-
application/json:
124-
schema: {}
125-
application/octet-stream:
99+
'*/*':
126100
schema:
127101
type: string
128102
format: binary
129-
text/plain:
130-
schema:
131-
type: string
132-
application/x-www-form-urlencoded:
133-
schema:
134-
type: string
135103
patch:
136104
operationId: FunctionInvocations_invokePatch
137105
parameters:
@@ -149,18 +117,10 @@ paths:
149117
'200':
150118
description: The request has succeeded.
151119
content:
152-
application/json:
153-
schema:
154-
anyOf:
155-
- {}
156-
- {}
157-
application/octet-stream:
120+
'*/*':
158121
schema:
159122
type: string
160123
format: binary
161-
text/plain:
162-
schema:
163-
type: string
164124
default:
165125
description: An unexpected error response.
166126
content:
@@ -170,18 +130,10 @@ paths:
170130
requestBody:
171131
required: false
172132
content:
173-
application/json:
174-
schema: {}
175-
application/octet-stream:
133+
'*/*':
176134
schema:
177135
type: string
178136
format: binary
179-
text/plain:
180-
schema:
181-
type: string
182-
application/x-www-form-urlencoded:
183-
schema:
184-
type: string
185137
delete:
186138
operationId: FunctionInvocations_invokeDelete
187139
parameters:
@@ -199,18 +151,10 @@ paths:
199151
'200':
200152
description: The request has succeeded.
201153
content:
202-
application/json:
203-
schema:
204-
anyOf:
205-
- {}
206-
- {}
207-
application/octet-stream:
154+
'*/*':
208155
schema:
209156
type: string
210157
format: binary
211-
text/plain:
212-
schema:
213-
type: string
214158
default:
215159
description: An unexpected error response.
216160
content:
@@ -220,18 +164,10 @@ paths:
220164
requestBody:
221165
required: false
222166
content:
223-
application/json:
224-
schema: {}
225-
application/octet-stream:
167+
'*/*':
226168
schema:
227169
type: string
228170
format: binary
229-
text/plain:
230-
schema:
231-
type: string
232-
application/x-www-form-urlencoded:
233-
schema:
234-
type: string
235171
components:
236172
schemas:
237173
FunctionsError:

0 commit comments

Comments
 (0)