Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
64 changes: 42 additions & 22 deletions packages/typespec-ts/src/modular/helpers/operationHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,30 +232,42 @@ export function getDeserializePrivateFunction(

if (deserializedType) {
const contentTypes = operation.operation.responses[0]?.contentTypes;
const deserializeFunctionName = buildModelDeserializer(
context,
deserializedType,
{
nameOnly: true,
skipDiscriminatedUnionSuffix: false
}
);
if (deserializeFunctionName) {
statements.push(`return ${deserializeFunctionName}(${deserializedRoot})`);
} else if (isAzureCoreErrorType(context.program, deserializedType.__raw)) {
// For File type responses, directly return the body
if (
deserializedType.kind === "model" &&
deserializedType.crossLanguageDefinitionId === "TypeSpec.Http.File"
) {
statements.push(`return ${deserializedRoot}`);
} else {
statements.push(
`return ${deserializeResponseValue(
context,
deserializedType,
deserializedRoot,
true,
isBinaryPayload(context, response.type!.__raw!, contentTypes!)
? "binary"
: getEncodeForType(deserializedType)
)}`
const deserializeFunctionName = buildModelDeserializer(
context,
deserializedType,
{
nameOnly: true,
skipDiscriminatedUnionSuffix: false
}
);
if (deserializeFunctionName) {
statements.push(
`return ${deserializeFunctionName}(${deserializedRoot})`
);
} else if (
isAzureCoreErrorType(context.program, deserializedType.__raw)
) {
statements.push(`return ${deserializedRoot}`);
} else {
statements.push(
`return ${deserializeResponseValue(
context,
deserializedType,
deserializedRoot,
true,
isBinaryPayload(context, response.type!.__raw!, contentTypes!)
? "binary"
: getEncodeForType(deserializedType)
)}`
);
}
}
} else if (returnType.type === "void") {
statements.push("return;");
Expand Down Expand Up @@ -501,7 +513,11 @@ export function getOperationFunction(
const parameterList = parameters.map((p) => p.name).join(", ");
// Special case for binary-only bodies: use helper to call streaming methods so that Core doesn't poison the response body by
// doing a UTF-8 decode on the raw bytes.
if (response?.type?.kind === "bytes" && response.type.encode === "bytes") {
const isBinaryResponse =
(response?.type?.kind === "bytes" && response.type.encode === "bytes") ||
(response?.type?.kind === "model" &&
response.type.crossLanguageDefinitionId === "TypeSpec.Http.File");
if (isBinaryResponse) {
statements.push(`const streamableMethod = _${name}Send(${parameterList});`);
statements.push(
`const result = await ${resolveReference(SerializationHelpers.getBinaryResponse)}(streamableMethod);`
Expand Down Expand Up @@ -1685,6 +1701,10 @@ export function deserializeResponseValue(
return `${restValue} as any`;
}
case "model": // generate deserialize logic for spread model types
// Special handling for TypeSpec.Http.File - it's already a Uint8Array
if (type.crossLanguageDefinitionId === "TypeSpec.Http.File") {
return restValue;
}
return `{${getResponseMapping(context, type, "").join(",")}}`;
case "nullable":
return deserializeResponseValue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ export function buildModelDeserializer(
}
): FunctionDeclarationStructure | undefined | string {
// const modelTcgcType = getTcgcType(type) as SdkModelType;
// TypeSpec.Http.File doesn't need deserialization - it's already a Uint8Array
if (
type.kind === "model" &&
type.crossLanguageDefinitionId === "TypeSpec.Http.File"
) {
return undefined;
}
if (!isSupportedSerializeType(type)) {
return undefined;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ export function getTypeExpression(
return `Record<string, ${valueType}>`;
}
case "model":
// Special handling for TypeSpec.Http.File
if (type.crossLanguageDefinitionId === "TypeSpec.Http.File") {
return "Uint8Array";
}
return getModelExpression(context, type);
case "nullable":
return getNullableExpression(context, type, options);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Should generate file model

## TypeSpec

```tsp
model Site {
name: string;
}
op test(@body site:Site): File<"application/octet-stream">;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you change as following?

Suggested change
op test(@body site:Site): File<"application/octet-stream">;
op test(@body site:Site): {
@header("Content-Type")
contentType: "application/octet-stream";
@doc("Receipt body in COSE format")
@bodyRoot
body: bytes;
};

```

## Model

```ts models
/**
* This file contains only generated model types and their (de)serializers.
* Disable the following rules for internal models with '_' prefix and deserializers which require 'any' for raw JSON input.
*/
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/** model interface Site */
export interface Site {
name: string;
}

export function siteSerializer(item: Site): any {
return { name: item["name"] };
}
```

## Operations

```ts operations
import { TestingContext as Client } from "./index.js";
import { Site, siteSerializer } from "../models/models.js";
import { getBinaryResponse } from "../static-helpers/serialization/get-binary-response.js";
import { TestOptionalParams } from "./options.js";
import {
StreamableMethod,
PathUncheckedResponse,
createRestError,
operationOptionsToRequestParameters,
} from "@azure-rest/core-client";

export function _testSend(
context: Client,
site: Site,
options: TestOptionalParams = { requestOptions: {} },
): StreamableMethod {
return context
.path("/")
.post({
...operationOptionsToRequestParameters(options),
contentType: "application/json",
headers: { accept: "application/octet-stream", ...options.requestOptions?.headers },
body: siteSerializer(site),
});
}

export async function _testDeserialize(result: PathUncheckedResponse): Promise<Uint8Array> {
const expectedStatuses = ["200"];
if (!expectedStatuses.includes(result.status)) {
throw createRestError(result);
}

return result.body;
}

export async function test(
context: Client,
site: Site,
options: TestOptionalParams = { requestOptions: {} },
): Promise<Uint8Array> {
const streamableMethod = _testSend(context, site, options);
const result = await getBinaryResponse(streamableMethod);
return _testDeserialize(result);
}
```
Loading