Skip to content

Commit c328ea5

Browse files
authored
Basic HttpPart support for RLC (#3124)
1 parent 7408c04 commit c328ea5

File tree

5 files changed

+247
-76
lines changed

5 files changed

+247
-76
lines changed

packages/rlc-common/src/buildObjectTypes.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,38 @@ function buildMultipartPartDefinitions(
110110

111111
for (const signature of propertySignatures) {
112112
const name = signature.name;
113+
const propertySchema = schema.properties?.[name];
113114
const typeName = getMultipartPartTypeName(schema.name, name);
114115

115116
const isFileUpload = signature.type?.toString().includes("File") ?? false;
116117

118+
const additionalProperties: any[] = [];
119+
120+
if ((propertySchema as any).multipartOptions) {
121+
const multipartOptions: any = (propertySchema as any).multipartOptions;
122+
if (multipartOptions.filenameSchema) {
123+
additionalProperties.push(
124+
getPropertySignature(
125+
{ name: "filename", ...multipartOptions.filenameSchema },
126+
[SchemaContext.Input],
127+
importedModels
128+
)
129+
);
130+
}
131+
if (multipartOptions.contentTypeSchema) {
132+
additionalProperties.push(
133+
getPropertySignature(
134+
{ name: "contentType", ...multipartOptions.contentTypeSchema },
135+
[SchemaContext.Input],
136+
importedModels
137+
)
138+
);
139+
}
140+
} else if (isFileUpload) {
141+
// default additional file metadata properties (legacy)
142+
additionalProperties.push(...MULTIPART_FILE_METADATA_PROPERTIES);
143+
}
144+
117145
structures.push({
118146
kind: StructureKind.Interface,
119147
isExported: true,
@@ -127,7 +155,7 @@ function buildMultipartPartDefinitions(
127155
name: "body",
128156
type: signature.type
129157
},
130-
...(isFileUpload ? MULTIPART_FILE_METADATA_PROPERTIES : [])
158+
...additionalProperties
131159
]
132160
});
133161
}

packages/typespec-ts/src/utils/modelUtils.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,11 @@ import {
6262
HttpOperationQueryParameter,
6363
Visibility,
6464
getHeaderFieldName,
65+
getHttpFileModel,
66+
getHttpPart,
6567
getPathParamName,
6668
getQueryParamName,
69+
isBody,
6770
isStatusCode
6871
} from "@typespec/http";
6972
import {
@@ -152,7 +155,7 @@ export function getSchemaForType(
152155
dpgContext: SdkContext,
153156
typeInput: Type,
154157
options?: GetSchemaOptions
155-
) {
158+
): any {
156159
const program = dpgContext.program;
157160
const { usage } = options ?? {};
158161
const type = getEffectiveModelFromType(dpgContext, typeInput);
@@ -172,6 +175,40 @@ export function getSchemaForType(
172175
}
173176

174177
if (type.kind === "Model") {
178+
const httpPart = getHttpPart(program, type);
179+
if (httpPart) {
180+
const fileModel = getHttpFileModel(program, httpPart.type);
181+
if (fileModel) {
182+
const schema: any = getSchemaForType(
183+
dpgContext,
184+
fileModel.contents,
185+
options
186+
);
187+
return {
188+
...schema,
189+
multipartOptions: {
190+
filenameSchema: {
191+
required: !fileModel.filename.optional,
192+
...getSchemaForType(dpgContext, fileModel.filename, options)
193+
},
194+
contentTypeSchema: {
195+
required: !fileModel.contentType.optional,
196+
...getSchemaForType(dpgContext, fileModel.contentType, options)
197+
}
198+
}
199+
};
200+
} else {
201+
// extract body if required
202+
const body =
203+
(httpPart.type.kind === "Model" &&
204+
[...httpPart.type.properties.values()].find((x) =>
205+
isBody(program, x)
206+
)) ||
207+
httpPart.type;
208+
return getSchemaForType(dpgContext, body, options);
209+
}
210+
}
211+
175212
const schema = getSchemaForModel(dpgContext, type, options) as any;
176213
if (isAnonymousObjectSchema(schema)) {
177214
if (Object.keys(schema.properties ?? {}).length === 0) {

packages/typespec-ts/test/azureIntegration/generated/payload/multipart/src/index.d.ts

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,31 @@ export declare type ComplexHttpPartsModelRequest = FormData | Array<ComplexHttpP
2626

2727
export declare interface ComplexHttpPartsModelRequestAddressPartDescriptor {
2828
name: "address";
29-
body: HttpPartAddress;
29+
body: Address;
3030
}
3131

3232
export declare interface ComplexHttpPartsModelRequestIdPartDescriptor {
3333
name: "id";
34-
body: HttpPartString;
34+
body: string;
3535
}
3636

3737
export declare interface ComplexHttpPartsModelRequestPicturesPartDescriptor {
3838
name: "pictures";
39-
body: Array<HttpPartFileRequiredMetaData>;
39+
body: string | Uint8Array | ReadableStream<Uint8Array> | NodeJS.ReadableStream | File;
4040
filename?: string;
4141
contentType?: string;
4242
}
4343

4444
export declare interface ComplexHttpPartsModelRequestPreviousAddressesPartDescriptor {
4545
name: "previousAddresses";
46-
body: HttpPartArray;
46+
body: Array<Address>;
4747
}
4848

4949
export declare interface ComplexHttpPartsModelRequestProfileImagePartDescriptor {
5050
name: "profileImage";
51-
body: HttpPartFileRequiredMetaData;
52-
filename?: string;
53-
contentType?: string;
51+
body: string | Uint8Array | ReadableStream<Uint8Array> | NodeJS.ReadableStream | File;
52+
filename: string;
53+
contentType: string;
5454
}
5555

5656
export declare type ComplexPartsRequest = FormData | Array<ComplexPartsRequestIdPartDescriptor | ComplexPartsRequestAddressPartDescriptor | ComplexPartsRequestProfileImagePartDescriptor | ComplexPartsRequestPicturesPartDescriptor>;
@@ -86,27 +86,27 @@ export declare type FileWithHttpPartOptionalContentTypeRequest = FormData | Arra
8686

8787
export declare interface FileWithHttpPartOptionalContentTypeRequestProfileImagePartDescriptor {
8888
name: "profileImage";
89-
body: HttpPartFileOptionalContentType;
90-
filename?: string;
89+
body: string | Uint8Array | ReadableStream<Uint8Array> | NodeJS.ReadableStream | File;
90+
filename: string;
9191
contentType?: string;
9292
}
9393

9494
export declare type FileWithHttpPartRequiredContentTypeRequest = FormData | Array<FileWithHttpPartRequiredContentTypeRequestProfileImagePartDescriptor>;
9595

9696
export declare interface FileWithHttpPartRequiredContentTypeRequestProfileImagePartDescriptor {
9797
name: "profileImage";
98-
body: HttpPartFileRequiredMetaData;
99-
filename?: string;
100-
contentType?: string;
98+
body: string | Uint8Array | ReadableStream<Uint8Array> | NodeJS.ReadableStream | File;
99+
filename: string;
100+
contentType: string;
101101
}
102102

103103
export declare type FileWithHttpPartSpecificContentTypeRequest = FormData | Array<FileWithHttpPartSpecificContentTypeRequestProfileImagePartDescriptor>;
104104

105105
export declare interface FileWithHttpPartSpecificContentTypeRequestProfileImagePartDescriptor {
106106
name: "profileImage";
107-
body: HttpPartFileSpecificContentType;
108-
filename?: string;
109-
contentType?: string;
107+
body: string | Uint8Array | ReadableStream<Uint8Array> | NodeJS.ReadableStream | File;
108+
filename: string;
109+
contentType: "image/jpg";
110110
}
111111

112112
export declare interface FormDataAnonymousModel {
@@ -287,7 +287,7 @@ export declare interface FormDataHttpPartsNonStringFloat204Response extends Http
287287
export declare interface FormDataHttpPartsNonStringFloatBodyParam {
288288
body: FormData | Array<{
289289
name: "temperature";
290-
body: HttpPart;
290+
body: number;
291291
}>;
292292
}
293293

@@ -333,27 +333,6 @@ export declare interface FormDataMultiBinaryPartsMediaTypesParam {
333333

334334
export declare type FormDataMultiBinaryPartsParameters = FormDataMultiBinaryPartsMediaTypesParam & FormDataMultiBinaryPartsBodyParam & RequestParameters;
335335

336-
export declare interface HttpPart {
337-
}
338-
339-
export declare interface HttpPartAddress {
340-
}
341-
342-
export declare interface HttpPartArray {
343-
}
344-
345-
export declare interface HttpPartFileOptionalContentType {
346-
}
347-
348-
export declare interface HttpPartFileRequiredMetaData {
349-
}
350-
351-
export declare interface HttpPartFileSpecificContentType {
352-
}
353-
354-
export declare interface HttpPartString {
355-
}
356-
357336
export declare type JsonPartRequest = FormData | Array<JsonPartRequestAddressPartDescriptor | JsonPartRequestProfileImagePartDescriptor>;
358337

359338
export declare interface JsonPartRequestAddressPartDescriptor {

packages/typespec-ts/test/integration/generated/payload/multipart/src/index.d.ts

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,31 @@ export declare type ComplexHttpPartsModelRequest = FormData | Array<ComplexHttpP
2626

2727
export declare interface ComplexHttpPartsModelRequestAddressPartDescriptor {
2828
name: "address";
29-
body: HttpPartAddress;
29+
body: Address;
3030
}
3131

3232
export declare interface ComplexHttpPartsModelRequestIdPartDescriptor {
3333
name: "id";
34-
body: HttpPartString;
34+
body: string;
3535
}
3636

3737
export declare interface ComplexHttpPartsModelRequestPicturesPartDescriptor {
3838
name: "pictures";
39-
body: Array<HttpPartFileRequiredMetaData>;
39+
body: string | Uint8Array | ReadableStream<Uint8Array> | NodeJS.ReadableStream | File;
4040
filename?: string;
4141
contentType?: string;
4242
}
4343

4444
export declare interface ComplexHttpPartsModelRequestPreviousAddressesPartDescriptor {
4545
name: "previousAddresses";
46-
body: HttpPartArray;
46+
body: Array<Address>;
4747
}
4848

4949
export declare interface ComplexHttpPartsModelRequestProfileImagePartDescriptor {
5050
name: "profileImage";
51-
body: HttpPartFileRequiredMetaData;
52-
filename?: string;
53-
contentType?: string;
51+
body: string | Uint8Array | ReadableStream<Uint8Array> | NodeJS.ReadableStream | File;
52+
filename: string;
53+
contentType: string;
5454
}
5555

5656
export declare type ComplexPartsRequest = FormData | Array<ComplexPartsRequestIdPartDescriptor | ComplexPartsRequestAddressPartDescriptor | ComplexPartsRequestProfileImagePartDescriptor | ComplexPartsRequestPicturesPartDescriptor>;
@@ -86,27 +86,27 @@ export declare type FileWithHttpPartOptionalContentTypeRequest = FormData | Arra
8686

8787
export declare interface FileWithHttpPartOptionalContentTypeRequestProfileImagePartDescriptor {
8888
name: "profileImage";
89-
body: HttpPartFileOptionalContentType;
90-
filename?: string;
89+
body: string | Uint8Array | ReadableStream<Uint8Array> | NodeJS.ReadableStream | File;
90+
filename: string;
9191
contentType?: string;
9292
}
9393

9494
export declare type FileWithHttpPartRequiredContentTypeRequest = FormData | Array<FileWithHttpPartRequiredContentTypeRequestProfileImagePartDescriptor>;
9595

9696
export declare interface FileWithHttpPartRequiredContentTypeRequestProfileImagePartDescriptor {
9797
name: "profileImage";
98-
body: HttpPartFileRequiredMetaData;
99-
filename?: string;
100-
contentType?: string;
98+
body: string | Uint8Array | ReadableStream<Uint8Array> | NodeJS.ReadableStream | File;
99+
filename: string;
100+
contentType: string;
101101
}
102102

103103
export declare type FileWithHttpPartSpecificContentTypeRequest = FormData | Array<FileWithHttpPartSpecificContentTypeRequestProfileImagePartDescriptor>;
104104

105105
export declare interface FileWithHttpPartSpecificContentTypeRequestProfileImagePartDescriptor {
106106
name: "profileImage";
107-
body: HttpPartFileSpecificContentType;
108-
filename?: string;
109-
contentType?: string;
107+
body: string | Uint8Array | ReadableStream<Uint8Array> | NodeJS.ReadableStream | File;
108+
filename: string;
109+
contentType: "image/jpg";
110110
}
111111

112112
export declare interface FormDataAnonymousModel {
@@ -287,7 +287,7 @@ export declare interface FormDataHttpPartsNonStringFloat204Response extends Http
287287
export declare interface FormDataHttpPartsNonStringFloatBodyParam {
288288
body: FormData | Array<{
289289
name: "temperature";
290-
body: HttpPart;
290+
body: number;
291291
}>;
292292
}
293293

@@ -333,27 +333,6 @@ export declare interface FormDataMultiBinaryPartsMediaTypesParam {
333333

334334
export declare type FormDataMultiBinaryPartsParameters = FormDataMultiBinaryPartsMediaTypesParam & FormDataMultiBinaryPartsBodyParam & RequestParameters;
335335

336-
export declare interface HttpPart {
337-
}
338-
339-
export declare interface HttpPartAddress {
340-
}
341-
342-
export declare interface HttpPartArray {
343-
}
344-
345-
export declare interface HttpPartFileOptionalContentType {
346-
}
347-
348-
export declare interface HttpPartFileRequiredMetaData {
349-
}
350-
351-
export declare interface HttpPartFileSpecificContentType {
352-
}
353-
354-
export declare interface HttpPartString {
355-
}
356-
357336
export declare type JsonPartRequest = FormData | Array<JsonPartRequestAddressPartDescriptor | JsonPartRequestProfileImagePartDescriptor>;
358337

359338
export declare interface JsonPartRequestAddressPartDescriptor {

0 commit comments

Comments
 (0)