Skip to content

Commit 7349b57

Browse files
authored
[tcgc] ignore visibility when finding response type if it is anonymous model (#2402)
with previous logic, http library will return a created anonymous model without `Create` visibility, and tcgc could not find the original model, but use this anonymous model as the response type (`GetResponse`). see [playground](https://cadlplayground.z22.web.core.windows.net/cadl-azure/?c=QHNlcnZpY2UNCm5hbWVzcGFjZSBUZXN0Q2xpZW50IHsNCiAgbW9kZWzFFsYQICBAdmlzaWJpbGl0eShMaWZlY3ljbGUuQ3JlYXRlKcYjY8UNOiBzdHJpbmc7DQrGF3JlYWTLFSAgfcYab3AgZ2V0KCk6xWg7DQp9DQo%3D&e=%40azure-tools%2Ftypespec-client-generator-core&options=%7B%7D), line 59. with current logic, tcgc could find the original model and will use that model as the response type (`Test`). see [playground](https://cadlplayground.z22.web.core.windows.net/typespec-azure/prs/2402/?c=QHNlcnZpY2UNCm5hbWVzcGFjZSBUZXN0Q2xpZW50IHsNCiAgbW9kZWzFFsYQICBAdmlzaWJpbGl0eShMaWZlY3ljbGUuQ3JlYXRlKcYjY8UNOiBzdHJpbmc7DQrGF3JlYWTLFSAgfcYab3AgZ2V0KCk6xWg7DQp9DQo%3D&e=%40azure-tools%2Ftypespec-client-generator-core&options=%7B%7D), line59.
1 parent a84457f commit 7349b57

File tree

5 files changed

+57
-9
lines changed

5 files changed

+57
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
changeKind: fix
3+
packages:
4+
- "@azure-tools/typespec-client-generator-core"
5+
---
6+
7+
Ignore visibility when finding HTTP response type if it is anonymous model.

packages/typespec-client-generator-core/src/http.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
HttpOperationParameter,
1919
HttpOperationPathParameter,
2020
HttpOperationQueryParameter,
21+
Visibility,
2122
getCookieParamOptions,
2223
getHeaderFieldName,
2324
getHeaderFieldOptions,
@@ -496,7 +497,7 @@ function getSdkHttpResponseAndExceptions(
496497
contentTypes = contentTypes.concat(innerResponse.body.contentTypes);
497498
body =
498499
innerResponse.body.type.kind === "Model"
499-
? getEffectivePayloadType(context, innerResponse.body.type)
500+
? getEffectivePayloadType(context, innerResponse.body.type, Visibility.Read)
500501
: innerResponse.body.type;
501502
if (getStreamMetadata(context.program, innerResponse)) {
502503
// map stream response body type to bytes

packages/typespec-client-generator-core/src/public-utils.ts

+21-6
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ import {
1717
isService,
1818
resolveEncodedName,
1919
} from "@typespec/compiler";
20-
import { HttpOperation, getHttpOperation, getHttpPart, isMetadata } from "@typespec/http";
20+
import {
21+
HttpOperation,
22+
Visibility,
23+
getHttpOperation,
24+
getHttpPart,
25+
isMetadata,
26+
isVisible,
27+
} from "@typespec/http";
2128
import { Version, getVersions } from "@typespec/versioning";
2229
import { pascalCase } from "change-case";
2330
import pluralize from "pluralize";
@@ -112,15 +119,20 @@ export function getClientNamespaceString(context: TCGCContext): string | undefin
112119
}
113120

114121
/**
115-
* If the given type is an anonymous model and all of its properties excluding
116-
* header/query/path/status-code are sourced from a named model, returns that original named model.
117-
* Otherwise the given type is returned unchanged.
122+
* If the given type is an anonymous model, returns a named model with same shape.
123+
* The finding logic will ignore all the properties of header/query/path/status-code metadata,
124+
* as well as the properties that are not visible in the given visibility if provided.
125+
* If the model found is also anonymous, the input type is returned unchanged.
118126
*
119127
* @param context
120128
* @param type
121129
* @returns
122130
*/
123-
export function getEffectivePayloadType(context: TCGCContext, type: Model): Model {
131+
export function getEffectivePayloadType(
132+
context: TCGCContext,
133+
type: Model,
134+
visibility?: Visibility,
135+
): Model {
124136
const program = context.program;
125137

126138
// if a type has name, we should resolve the name
@@ -135,7 +147,10 @@ export function getEffectivePayloadType(context: TCGCContext, type: Model): Mode
135147
const effective = getEffectiveModelType(
136148
program,
137149
type,
138-
(t) => !isMetadata(context.program, t) && !hasNoneVisibility(context, t),
150+
(t) =>
151+
!isMetadata(context.program, t) &&
152+
!hasNoneVisibility(context, t) &&
153+
(visibility === undefined || isVisible(program, t, visibility)),
139154
);
140155
if (effective.name) {
141156
return effective;

packages/typespec-client-generator-core/src/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1752,7 +1752,7 @@ function updateTypesFromOperation(
17521752
if (innerResponse.body?.type && !isNeverOrVoidType(innerResponse.body.type)) {
17531753
const body =
17541754
innerResponse.body.type.kind === "Model"
1755-
? getEffectivePayloadType(context, innerResponse.body.type)
1755+
? getEffectivePayloadType(context, innerResponse.body.type, Visibility.Read)
17561756
: innerResponse.body.type;
17571757
const sdkType = diagnostics.pipe(getClientTypeWithDiagnostics(context, body, operation));
17581758
if (generateConvenient) {

packages/typespec-client-generator-core/test/methods/responses.test.ts

+26-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { deepStrictEqual, ok, strictEqual } from "assert";
22
import { beforeEach, it } from "vitest";
3-
import { SdkHttpOperation, SdkServiceMethod } from "../../src/interfaces.js";
3+
import { SdkHttpOperation, SdkMethodResponse, SdkServiceMethod } from "../../src/interfaces.js";
44
import { SdkTestRunner, createSdkTestRunner } from "../test-host.js";
55
import { getServiceMethodOfClient } from "../utils.js";
66

@@ -371,3 +371,28 @@ it("description shall be included in response", async () => {
371371
strictEqual(responses.length, 1);
372372
strictEqual(responses[0].description, "description on response");
373373
});
374+
375+
it("response body with non-read visibility", async () => {
376+
await runner.compile(`
377+
@service
378+
namespace TestClient {
379+
model Test {
380+
@visibility(Lifecycle.Create)
381+
create: string;
382+
383+
read: string;
384+
}
385+
386+
op get(): Test;
387+
}
388+
`);
389+
const models = runner.context.sdkPackage.models;
390+
strictEqual(models.length, 1);
391+
const model = models[0];
392+
strictEqual(model.name, "Test");
393+
const client = runner.context.sdkPackage.clients[0];
394+
ok(client);
395+
const method = client.methods[0];
396+
ok(method);
397+
strictEqual((method.response as SdkMethodResponse).type, model);
398+
});

0 commit comments

Comments
 (0)