diff --git a/packages/typespec-test/test/anomalyDetector/generated/typespec-ts/review/ai-anomaly-detector.api.md b/packages/typespec-test/test/anomalyDetector/generated/typespec-ts/review/ai-anomaly-detector.api.md index cae2846141..8325ace9c1 100644 --- a/packages/typespec-test/test/anomalyDetector/generated/typespec-ts/review/ai-anomaly-detector.api.md +++ b/packages/typespec-test/test/anomalyDetector/generated/typespec-ts/review/ai-anomaly-detector.api.md @@ -49,7 +49,7 @@ export interface DeleteMultivariateModelDefaultHeaders { // @public (undocumented) export interface DeleteMultivariateModelDefaultResponse extends HttpResponse { // (undocumented) - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; // (undocumented) headers: RawHttpHeaders & DeleteMultivariateModelDefaultHeaders; // (undocumented) @@ -93,7 +93,7 @@ export interface DetectMultivariateBatchAnomalyDefaultHeaders { // @public (undocumented) export interface DetectMultivariateBatchAnomalyDefaultResponse extends HttpResponse { // (undocumented) - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; // (undocumented) headers: RawHttpHeaders & DetectMultivariateBatchAnomalyDefaultHeaders; // (undocumented) @@ -129,7 +129,7 @@ export interface DetectMultivariateLastAnomalyDefaultHeaders { // @public (undocumented) export interface DetectMultivariateLastAnomalyDefaultResponse extends HttpResponse { // (undocumented) - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; // (undocumented) headers: RawHttpHeaders & DetectMultivariateLastAnomalyDefaultHeaders; // (undocumented) @@ -271,7 +271,7 @@ export interface GetMultivariateBatchDetectionResultDefaultHeaders { // @public (undocumented) export interface GetMultivariateBatchDetectionResultDefaultResponse extends HttpResponse { // (undocumented) - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; // (undocumented) headers: RawHttpHeaders & GetMultivariateBatchDetectionResultDefaultHeaders; // (undocumented) @@ -297,7 +297,7 @@ export interface GetMultivariateModelDefaultHeaders { // @public (undocumented) export interface GetMultivariateModelDefaultResponse extends HttpResponse { // (undocumented) - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; // (undocumented) headers: RawHttpHeaders & GetMultivariateModelDefaultHeaders; // (undocumented) @@ -359,7 +359,7 @@ export interface ListMultivariateModelsDefaultHeaders { // @public (undocumented) export interface ListMultivariateModelsDefaultResponse extends HttpResponse { // (undocumented) - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; // (undocumented) headers: RawHttpHeaders & ListMultivariateModelsDefaultHeaders; // (undocumented) @@ -548,6 +548,12 @@ export interface MultivariateMultivariateLastDetectionResultOutput { variableStates?: Array; } +// @public +export interface MultivariateResponseErrorOutput { + code: string; + message: string; +} + // @public export interface MultivariateVariableState { effectiveCount?: number; @@ -638,7 +644,7 @@ export interface TrainMultivariateModelDefaultHeaders { // @public (undocumented) export interface TrainMultivariateModelDefaultResponse extends HttpResponse { // (undocumented) - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; // (undocumented) headers: RawHttpHeaders & TrainMultivariateModelDefaultHeaders; // (undocumented) diff --git a/packages/typespec-test/test/anomalyDetector/generated/typespec-ts/src/outputModels.ts b/packages/typespec-test/test/anomalyDetector/generated/typespec-ts/src/outputModels.ts index c6590b313b..5611e2155f 100644 --- a/packages/typespec-test/test/anomalyDetector/generated/typespec-ts/src/outputModels.ts +++ b/packages/typespec-test/test/anomalyDetector/generated/typespec-ts/src/outputModels.ts @@ -257,6 +257,14 @@ export interface MultivariateCorrelationChangesOutput { changedVariables?: string[]; } +/** Error response */ +export interface MultivariateResponseErrorOutput { + /** The error code. */ + code: string; + /** The message explaining the error reported by the service. */ + message: string; +} + /** * Training result of a model including its status, errors and diagnostics * information. diff --git a/packages/typespec-test/test/anomalyDetector/generated/typespec-ts/src/responses.ts b/packages/typespec-test/test/anomalyDetector/generated/typespec-ts/src/responses.ts index 4e65770593..8b680449ab 100644 --- a/packages/typespec-test/test/anomalyDetector/generated/typespec-ts/src/responses.ts +++ b/packages/typespec-test/test/anomalyDetector/generated/typespec-ts/src/responses.ts @@ -9,7 +9,7 @@ import { UnivariateUnivariateLastDetectionResultOutput, UnivariateUnivariateChangePointDetectionResultOutput, MultivariateMultivariateDetectionResultOutput, - MultivariateErrorResponseOutput, + MultivariateResponseErrorOutput, MultivariateAnomalyDetectionModelOutput, MultivariateModelListOutput, MultivariateMultivariateLastDetectionResultOutput, @@ -83,7 +83,7 @@ export interface GetMultivariateBatchDetectionResultDefaultHeaders { export interface GetMultivariateBatchDetectionResultDefaultResponse extends HttpResponse { status: string; - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; headers: RawHttpHeaders & GetMultivariateBatchDetectionResultDefaultHeaders; } @@ -106,7 +106,7 @@ export interface TrainMultivariateModelDefaultHeaders { export interface TrainMultivariateModelDefaultResponse extends HttpResponse { status: string; - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; headers: RawHttpHeaders & TrainMultivariateModelDefaultHeaders; } @@ -123,7 +123,7 @@ export interface ListMultivariateModelsDefaultHeaders { export interface ListMultivariateModelsDefaultResponse extends HttpResponse { status: string; - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; headers: RawHttpHeaders & ListMultivariateModelsDefaultHeaders; } @@ -139,7 +139,7 @@ export interface DeleteMultivariateModelDefaultHeaders { export interface DeleteMultivariateModelDefaultResponse extends HttpResponse { status: string; - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; headers: RawHttpHeaders & DeleteMultivariateModelDefaultHeaders; } @@ -156,7 +156,7 @@ export interface GetMultivariateModelDefaultHeaders { export interface GetMultivariateModelDefaultResponse extends HttpResponse { status: string; - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; headers: RawHttpHeaders & GetMultivariateModelDefaultHeaders; } @@ -183,7 +183,7 @@ export interface DetectMultivariateBatchAnomalyDefaultHeaders { export interface DetectMultivariateBatchAnomalyDefaultResponse extends HttpResponse { status: string; - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; headers: RawHttpHeaders & DetectMultivariateBatchAnomalyDefaultHeaders; } @@ -201,6 +201,6 @@ export interface DetectMultivariateLastAnomalyDefaultHeaders { export interface DetectMultivariateLastAnomalyDefaultResponse extends HttpResponse { status: string; - body: MultivariateErrorResponseOutput; + body: MultivariateResponseErrorOutput; headers: RawHttpHeaders & DetectMultivariateLastAnomalyDefaultHeaders; } diff --git a/packages/typespec-test/test/contentsafety_modular/generated/typespec-ts/review/ai-content-safety.api.md b/packages/typespec-test/test/contentsafety_modular/generated/typespec-ts/review/ai-content-safety.api.md index 77e23bdc58..4a7910cc14 100644 --- a/packages/typespec-test/test/contentsafety_modular/generated/typespec-ts/review/ai-content-safety.api.md +++ b/packages/typespec-test/test/contentsafety_modular/generated/typespec-ts/review/ai-content-safety.api.md @@ -175,7 +175,7 @@ export interface TextAnalyzeSeverityResult { // @public export interface TextBlockItem { - readonly blockItemId: string; + blockItemId: string; description?: string; text: string; } @@ -188,7 +188,7 @@ export interface TextBlockItemInfo { // @public export interface TextBlocklist { - readonly blocklistName: string; + blocklistName: string; description?: string; } diff --git a/packages/typespec-test/test/contentsafety_modular/generated/typespec-ts/src/api/operations.ts b/packages/typespec-test/test/contentsafety_modular/generated/typespec-ts/src/api/operations.ts index 6ec7aa8f1e..80f5cdbb12 100644 --- a/packages/typespec-test/test/contentsafety_modular/generated/typespec-ts/src/api/operations.ts +++ b/packages/typespec-test/test/contentsafety_modular/generated/typespec-ts/src/api/operations.ts @@ -211,7 +211,10 @@ export function _createOrUpdateTextBlocklistSend( ...operationOptionsToRequestParameters(options), contentType: (options.contentType as any) ?? "application/merge-patch+json", - body: { description: resource["description"] }, + body: { + blocklistName: resource["blocklistName"], + description: resource["description"], + }, }); } diff --git a/packages/typespec-test/test/contentsafety_modular/generated/typespec-ts/src/models/models.ts b/packages/typespec-test/test/contentsafety_modular/generated/typespec-ts/src/models/models.ts index 9cebdc9b56..ad9fe40123 100644 --- a/packages/typespec-test/test/contentsafety_modular/generated/typespec-ts/src/models/models.ts +++ b/packages/typespec-test/test/contentsafety_modular/generated/typespec-ts/src/models/models.ts @@ -4,7 +4,7 @@ /** Text Blocklist. */ export interface TextBlocklist { /** Text blocklist name. */ - readonly blocklistName: string; + blocklistName: string; /** Text blocklist description. */ description?: string; } @@ -32,7 +32,7 @@ export interface AddOrUpdateBlockItemsResult { /** Item in TextBlocklist. */ export interface TextBlockItem { /** Block Item Id. It will be uuid. */ - readonly blockItemId: string; + blockItemId: string; /** Block item description. */ description?: string; /** Block item content. */ diff --git a/packages/typespec-test/test/todo_non_branded/generated/typespec-ts/review/todo-non-branded.api.md b/packages/typespec-test/test/todo_non_branded/generated/typespec-ts/review/todo-non-branded.api.md index 607af876e8..702ccddad5 100644 --- a/packages/typespec-test/test/todo_non_branded/generated/typespec-ts/review/todo-non-branded.api.md +++ b/packages/typespec-test/test/todo_non_branded/generated/typespec-ts/review/todo-non-branded.api.md @@ -35,6 +35,14 @@ export interface ErrorModelOutput { message: string; } +// @public (undocumented) +export interface InvalidTodoItemOutput extends ErrorModelOutput { +} + +// @public +export interface InvalidUserResponseOutput extends ErrorModelOutput { +} + // @public (undocumented) export interface Routes { (path: "/users"): UsersCreate; @@ -194,7 +202,7 @@ export interface TodoItemsCreateForm200Response extends HttpResponse { // @public export interface TodoItemsCreateForm422Response extends HttpResponse { // (undocumented) - body: ErrorModelOutput; + body: InvalidTodoItemOutput; // (undocumented) status: "422"; } @@ -228,7 +236,7 @@ export interface TodoItemsCreateJson200Response extends HttpResponse { // @public export interface TodoItemsCreateJson422Response extends HttpResponse { // (undocumented) - body: ErrorModelOutput; + body: InvalidTodoItemOutput; // (undocumented) status: "422"; } @@ -413,6 +421,10 @@ export interface UserCreatedResponseOutput { username: string; } +// @public +export interface UserExistsResponseOutput extends ErrorModelOutput { +} + // @public (undocumented) export interface UsersCreate { // (undocumented) @@ -430,7 +442,7 @@ export interface UsersCreate200Response extends HttpResponse { // @public export interface UsersCreate409Response extends HttpResponse { // (undocumented) - body: ErrorModelOutput; + body: UserExistsResponseOutput; // (undocumented) status: "409"; } @@ -438,7 +450,7 @@ export interface UsersCreate409Response extends HttpResponse { // @public export interface UsersCreate422Response extends HttpResponse { // (undocumented) - body: ErrorModelOutput; + body: InvalidUserResponseOutput; // (undocumented) status: "422"; } @@ -575,7 +587,7 @@ export interface UsersValidate200Response extends HttpResponse { // @public export interface UsersValidate422Response extends HttpResponse { // (undocumented) - body: ErrorModelOutput; + body: InvalidUserResponseOutput; // (undocumented) status: "422"; } diff --git a/packages/typespec-test/test/todo_non_branded/generated/typespec-ts/src/outputModels.ts b/packages/typespec-test/test/todo_non_branded/generated/typespec-ts/src/outputModels.ts index f8ef8d083b..5d16f151b6 100644 --- a/packages/typespec-test/test/todo_non_branded/generated/typespec-ts/src/outputModels.ts +++ b/packages/typespec-test/test/todo_non_branded/generated/typespec-ts/src/outputModels.ts @@ -16,6 +16,9 @@ export interface UserCreatedResponseOutput { token: string; } +/** The user already exists */ +export interface UserExistsResponseOutput extends ErrorModelOutput {} + export interface ErrorModelOutput { /** A machine readable error code */ code: string; @@ -23,6 +26,11 @@ export interface ErrorModelOutput { message: string; } +/** The user is invalid (e.g. forgot to enter email address) */ +export interface InvalidUserResponseOutput extends ErrorModelOutput {} + +export interface InvalidTodoItemOutput extends ErrorModelOutput {} + export interface TodoPageOutput { /** The items in the page */ items: Array; diff --git a/packages/typespec-test/test/todo_non_branded/generated/typespec-ts/src/responses.ts b/packages/typespec-test/test/todo_non_branded/generated/typespec-ts/src/responses.ts index 696ddd97bd..ab5166049a 100644 --- a/packages/typespec-test/test/todo_non_branded/generated/typespec-ts/src/responses.ts +++ b/packages/typespec-test/test/todo_non_branded/generated/typespec-ts/src/responses.ts @@ -3,9 +3,11 @@ import { HttpResponse } from "@typespec/ts-http-runtime"; import { UserCreatedResponseOutput, - ErrorModelOutput, + UserExistsResponseOutput, + InvalidUserResponseOutput, TodoPageOutput, TodoItemOutput, + InvalidTodoItemOutput, TodoAttachmentOutput, } from "./outputModels"; @@ -18,13 +20,13 @@ export interface UsersCreate200Response extends HttpResponse { /** The request conflicts with the current state of the server. */ export interface UsersCreate409Response extends HttpResponse { status: "409"; - body: ErrorModelOutput; + body: UserExistsResponseOutput; } /** Client error */ export interface UsersCreate422Response extends HttpResponse { status: "422"; - body: ErrorModelOutput; + body: InvalidUserResponseOutput; } /** The request has succeeded. */ @@ -35,7 +37,7 @@ export interface UsersValidate200Response extends HttpResponse { /** Client error */ export interface UsersValidate422Response extends HttpResponse { status: "422"; - body: ErrorModelOutput; + body: InvalidUserResponseOutput; } /** The request has succeeded. */ @@ -88,7 +90,7 @@ export interface TodoItemsCreateJson200Response extends HttpResponse { /** Client error */ export interface TodoItemsCreateJson422Response extends HttpResponse { status: "422"; - body: ErrorModelOutput; + body: InvalidTodoItemOutput; } /** The request has succeeded. */ @@ -100,7 +102,7 @@ export interface TodoItemsCreateForm200Response extends HttpResponse { /** Client error */ export interface TodoItemsCreateForm422Response extends HttpResponse { status: "422"; - body: ErrorModelOutput; + body: InvalidTodoItemOutput; } /** The request has succeeded. */ diff --git a/packages/typespec-ts/src/modular/buildCodeModel.ts b/packages/typespec-ts/src/modular/buildCodeModel.ts index c3324647f7..a0458f17bb 100644 --- a/packages/typespec-ts/src/modular/buildCodeModel.ts +++ b/packages/typespec-ts/src/modular/buildCodeModel.ts @@ -252,20 +252,25 @@ function getEffectiveSchemaType(program: Program, type: Model | Union): Model { return !(headerInfo || queryInfo || pathInfo || statusCodeinfo); } - let effective: Model; + // If type is an anonymous model, tries to find a named model that has the same properties + let effective: Model | undefined = undefined; if (type.kind === "Union") { const nonNullOptions = [...type.variants.values()] .map((x) => x.type) .filter((t) => !isNullType(t)); - if (nonNullOptions.length === 1 && nonNullOptions[0]?.kind === "Model") { + if ( + nonNullOptions.length === 1 && + nonNullOptions[0]?.kind === "Model" && + nonNullOptions[0]?.name === "" + ) { effective = getEffectiveModelType(program, nonNullOptions[0]); } return type as any; - } else { + } else if (type.name === "") { effective = getEffectiveModelType(program, type, isSchemaProperty); } - if (effective.name) { + if (effective?.name) { return effective; } return type as Model; @@ -951,7 +956,7 @@ function isReadOnly(program: Program, type: ModelProperty): boolean { // Only "read" should be readOnly const visibility = getVisibility(program, type); if (visibility) { - return visibility.includes("read"); + return visibility.includes("read") && visibility.length === 1; } else { return false; } diff --git a/packages/typespec-ts/src/utils/modelUtils.ts b/packages/typespec-ts/src/utils/modelUtils.ts index 05fc30fbf9..216f0ee823 100644 --- a/packages/typespec-ts/src/utils/modelUtils.ts +++ b/packages/typespec-ts/src/utils/modelUtils.ts @@ -229,7 +229,11 @@ export function getSchemaForType( return undefined; } export function getEffectiveModelFromType(program: Program, type: Type): Type { - if (type.kind === "Model") { + /** + * If type is an anonymous model, tries to find a named model that has the same + * set of properties when non-schema properties are excluded. + */ + if (type.kind === "Model" && type.name === "") { const effective = getEffectiveModelType(program, type, isSchemaProperty); if (effective.name) { return effective; diff --git a/packages/typespec-ts/test/modularUnit/modelsGenerator.spec.ts b/packages/typespec-ts/test/modularUnit/modelsGenerator.spec.ts index 4218603eab..4a4e5d2ba6 100644 --- a/packages/typespec-ts/test/modularUnit/modelsGenerator.spec.ts +++ b/packages/typespec-ts/test/modularUnit/modelsGenerator.spec.ts @@ -1778,9 +1778,8 @@ describe("inheritance & polymorphism", () => { true ); assert.isUndefined(schemaOutput); - const paramOutput = await emitModularOperationsFromTypeSpec( - tspDefinition - ); + const paramOutput = + await emitModularOperationsFromTypeSpec(tspDefinition); assert.ok(paramOutput); assert.strictEqual(paramOutput?.length, 1); await assertEqualContent( @@ -1854,10 +1853,9 @@ describe("inheritance & polymorphism", () => { | "text/plain; charset=utf-8" | "text/vnd.ms.protobuf"; ` - ) - const paramOutput = await emitModularOperationsFromTypeSpec( - tspDefinition ); + const paramOutput = + await emitModularOperationsFromTypeSpec(tspDefinition); assert.ok(paramOutput); assert.strictEqual(paramOutput?.length, 1); await assertEqualContent( @@ -1954,3 +1952,93 @@ describe("inheritance & polymorphism", () => { }); }); }); + +describe("`is`", () => { + it("should generate correct name and properties if A is B