From a15862cc3f6d10214f9e88fb42a41027d60e60e1 Mon Sep 17 00:00:00 2001 From: Linh Phan Date: Fri, 1 May 2026 13:31:09 -0700 Subject: [PATCH 01/10] add nested enums discovered during operation processing to code model --- .../emitter/src/lib/client-model-builder.ts | 27 ++++-- .../test/Unit/operation-paging.test.ts | 92 +++++++++++++++++++ 2 files changed, 110 insertions(+), 9 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts index ff321be9d12..dc7d9429089 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts @@ -62,17 +62,26 @@ export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, reado enums.push(type as InputEnumType); } } - // Include models discovered only via operation processing (e.g., anonymous response models - // for protocol-only paging operations where TCGC does not include the response model in - // sdkPackage.models). See https://github.com/microsoft/typespec/issues/9391. Dedupe by - // name to avoid duplicates when TCGC produces a different reference for the same model. + // Include models and enums discovered only via operation processing (e.g., anonymous + // response models for protocol-only paging operations where TCGC does not include the + // response model in sdkPackage.models, or enums only reachable through nested property + // types of such models). See https://github.com/microsoft/typespec/issues/9391. Dedupe + // models by name to avoid duplicates when TCGC produces a different reference for the + // same model. + const existingEnumNames = new Set(enums.map((e) => e.name)); for (const type of sdkContext.__typeCache.types.values()) { if (typesBeforeClients.has(type)) continue; - if (type.kind !== "model") continue; - const model = type as InputModelType; - if (existingModelNames.has(model.name)) continue; - models.push(model); - existingModelNames.add(model.name); + if (type.kind === "model") { + const model = type as InputModelType; + if (existingModelNames.has(model.name)) continue; + models.push(model); + existingModelNames.add(model.name); + } else if (type.kind === "enum") { + const enumType = type as InputEnumType; + if (existingEnumNames.has(enumType.name)) continue; + enums.push(enumType); + existingEnumNames.add(enumType.name); + } } // TODO -- TCGC now does not have constants field in its sdkPackage, they might add it in the future. diff --git a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts index 44392435309..495c724bbae 100644 --- a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts @@ -263,6 +263,98 @@ describe("Next link operations", () => { `Expected response model 'LinkResponse' to be present in code model. Found: ${root.models.map((m) => m.name).join(", ")}`, ); }); + + it("includes nested enum from protocol-only response model in code model", async () => { + const program = await typeSpecCompile( + ` + @convenientAPI(false) + @list + op link(): { + @pageItems + items: Foo[]; + + @nextLink + next?: url; + }; + enum Status { running, completed, failed }; + model Foo { + name: string; + status: Status; + }; + `, + runner, + { IsTCGCNeeded: true }, + ); + const context = createEmitterContext(program); + const sdkContext = await createCSharpSdkContext(context); + const [root] = createModel(sdkContext); + + // The enum used as a property type of a model only reachable via a + // protocol-only paging operation must be included in the code model's + // enums list, not just the models list. + const statusEnum = root.enums.find((e) => e.name === "Status"); + ok( + statusEnum, + `Expected enum 'Status' to be present in code model enums. Found: ${root.enums.map((e) => e.name).join(", ")}`, + ); + + // The model itself should also be present + const fooModel = root.models.find((m) => m.name === "Foo"); + ok( + fooModel, + `Expected model 'Foo' to be present in code model models. Found: ${root.models.map((m) => m.name).join(", ")}`, + ); + }); + + it("includes deeply nested enum from protocol-only response model in code model", async () => { + const program = await typeSpecCompile( + ` + @convenientAPI(false) + @list + op link(): { + @pageItems + items: Foo[]; + + @nextLink + next?: url; + }; + enum Priority { low, medium, high }; + model Bar { + priority: Priority; + }; + model Foo { + name: string; + bar: Bar; + }; + `, + runner, + { IsTCGCNeeded: true }, + ); + const context = createEmitterContext(program); + const sdkContext = await createCSharpSdkContext(context); + const [root] = createModel(sdkContext); + + // An enum nested two levels deep (Foo -> Bar -> Priority) must still + // be captured when the entire graph is only reachable via a + // protocol-only paging operation. + const priorityEnum = root.enums.find((e) => e.name === "Priority"); + ok( + priorityEnum, + `Expected enum 'Priority' to be present in code model enums. Found: ${root.enums.map((e) => e.name).join(", ")}`, + ); + + const barModel = root.models.find((m) => m.name === "Bar"); + ok( + barModel, + `Expected model 'Bar' to be present in code model models. Found: ${root.models.map((m) => m.name).join(", ")}`, + ); + + const fooModel = root.models.find((m) => m.name === "Foo"); + ok( + fooModel, + `Expected model 'Foo' to be present in code model models. Found: ${root.models.map((m) => m.name).join(", ")}`, + ); + }); }); describe("Continuation token operations", () => { From 29c3f4f6ef017b7adceae9d413566dc7ad1d5cce Mon Sep 17 00:00:00 2001 From: Linh Phan Date: Fri, 1 May 2026 15:03:58 -0700 Subject: [PATCH 02/10] Update code model after running Generate.ps1. Also made model/enul key to be namespace aware --- .../emitter/src/lib/client-model-builder.ts | 23 +- .../Local/Sample-TypeSpec/tspCodeModel.json | 2193 +++++++++-------- 2 files changed, 1111 insertions(+), 1105 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts index dc7d9429089..45cb32627a6 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts @@ -53,11 +53,12 @@ export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, reado const models: InputModelType[] = []; const enums: InputEnumType[] = []; - const existingModelNames = new Set(); + const existingModelKeys = new Set(); for (const type of typesBeforeClients) { if (type.kind === "model") { - models.push(type as InputModelType); - existingModelNames.add((type as InputModelType).name); + const model = type as InputModelType; + models.push(model); + existingModelKeys.add(`${model.namespace}.${model.name}`); } else if (type.kind === "enum") { enums.push(type as InputEnumType); } @@ -66,21 +67,23 @@ export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, reado // response models for protocol-only paging operations where TCGC does not include the // response model in sdkPackage.models, or enums only reachable through nested property // types of such models). See https://github.com/microsoft/typespec/issues/9391. Dedupe - // models by name to avoid duplicates when TCGC produces a different reference for the - // same model. - const existingEnumNames = new Set(enums.map((e) => e.name)); + // by namespace + name to avoid duplicates when TCGC produces a different reference for + // the same type, while still allowing distinct types that share a name across namespaces. + const existingEnumKeys = new Set(enums.map((e) => `${e.namespace}.${e.name}`)); for (const type of sdkContext.__typeCache.types.values()) { if (typesBeforeClients.has(type)) continue; if (type.kind === "model") { const model = type as InputModelType; - if (existingModelNames.has(model.name)) continue; + const key = `${model.namespace}.${model.name}`; + if (existingModelKeys.has(key)) continue; models.push(model); - existingModelNames.add(model.name); + existingModelKeys.add(key); } else if (type.kind === "enum") { const enumType = type as InputEnumType; - if (existingEnumNames.has(enumType.name)) continue; + const key = `${enumType.namespace}.${enumType.name}`; + if (existingEnumKeys.has(key)) continue; enums.push(enumType); - existingEnumNames.add(enumType.name); + existingEnumKeys.add(key); } } diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/tspCodeModel.json b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/tspCodeModel.json index 812373536f1..c50e98fc814 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/tspCodeModel.json +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/tspCodeModel.json @@ -793,17 +793,141 @@ "isFlags": false, "usage": "ApiVersionEnum", "decorators": [] + }, + { + "$id": "70", + "kind": "enum", + "name": "getUnknownValueContentType", + "crossLanguageDefinitionId": "SampleTypeSpec.getUnknownValue.accept", + "valueType": { + "$id": "71", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "values": [ + { + "$id": "72", + "kind": "enumvalue", + "name": "text/plain", + "value": "text/plain", + "valueType": { + "$ref": "71" + }, + "enumType": { + "$ref": "70" + }, + "decorators": [] + }, + { + "$id": "73", + "kind": "enumvalue", + "name": "text/plain", + "value": "text/plain", + "valueType": { + "$ref": "71" + }, + "enumType": { + "$ref": "70" + }, + "decorators": [] + }, + { + "$id": "74", + "kind": "enumvalue", + "name": "text/plain", + "value": "text/plain", + "valueType": { + "$ref": "71" + }, + "enumType": { + "$ref": "70" + }, + "decorators": [] + }, + { + "$id": "75", + "kind": "enumvalue", + "name": "text/plain", + "value": "text/plain", + "valueType": { + "$ref": "71" + }, + "enumType": { + "$ref": "70" + }, + "decorators": [] + }, + { + "$id": "76", + "kind": "enumvalue", + "name": "text/plain", + "value": "text/plain", + "valueType": { + "$ref": "71" + }, + "enumType": { + "$ref": "70" + }, + "decorators": [] + }, + { + "$id": "77", + "kind": "enumvalue", + "name": "text/plain", + "value": "text/plain", + "valueType": { + "$ref": "71" + }, + "enumType": { + "$ref": "70" + }, + "decorators": [] + }, + { + "$id": "78", + "kind": "enumvalue", + "name": "text/plain", + "value": "text/plain", + "valueType": { + "$ref": "71" + }, + "enumType": { + "$ref": "70" + }, + "decorators": [] + }, + { + "$id": "79", + "kind": "enumvalue", + "name": "text/plain", + "value": "text/plain", + "valueType": { + "$ref": "71" + }, + "enumType": { + "$ref": "70" + }, + "decorators": [] + } + ], + "namespace": "", + "isFixed": true, + "isFlags": false, + "usage": "None", + "decorators": [] } ], "constants": [ { - "$id": "70", + "$id": "80", "kind": "constant", "name": "ThingRequiredLiteralString", "namespace": "SampleTypeSpec", "usage": "Input,Output,Spread,Json", "valueType": { - "$id": "71", + "$id": "81", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -813,13 +937,13 @@ "decorators": [] }, { - "$id": "72", + "$id": "82", "kind": "constant", "name": "ThingRequiredLiteralInt", "namespace": "SampleTypeSpec", "usage": "Input,Output,Spread,Json", "valueType": { - "$id": "73", + "$id": "83", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -829,13 +953,13 @@ "decorators": [] }, { - "$id": "74", + "$id": "84", "kind": "constant", "name": "ThingRequiredLiteralFloat", "namespace": "SampleTypeSpec", "usage": "Input,Output,Spread,Json", "valueType": { - "$id": "75", + "$id": "85", "kind": "float32", "name": "float32", "crossLanguageDefinitionId": "TypeSpec.float32", @@ -845,13 +969,13 @@ "decorators": [] }, { - "$id": "76", + "$id": "86", "kind": "constant", "name": "ThingRequiredLiteralBool", "namespace": "SampleTypeSpec", "usage": "Input,Output,Spread,Json", "valueType": { - "$id": "77", + "$id": "87", "kind": "boolean", "name": "boolean", "crossLanguageDefinitionId": "TypeSpec.boolean", @@ -861,13 +985,13 @@ "decorators": [] }, { - "$id": "78", + "$id": "88", "kind": "constant", "name": "ThingOptionalLiteralBool", "namespace": "SampleTypeSpec", "usage": "Input,Output,Spread,Json", "valueType": { - "$id": "79", + "$id": "89", "kind": "boolean", "name": "boolean", "crossLanguageDefinitionId": "TypeSpec.boolean", @@ -877,13 +1001,13 @@ "decorators": [] }, { - "$id": "80", + "$id": "90", "kind": "constant", "name": "PetKind", "namespace": "SampleTypeSpec", "usage": "Input,Output,Json", "valueType": { - "$id": "81", + "$id": "91", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -893,13 +1017,13 @@ "decorators": [] }, { - "$id": "82", + "$id": "92", "kind": "constant", "name": "DogKind", "namespace": "SampleTypeSpec", "usage": "Input,Output,Json", "valueType": { - "$id": "83", + "$id": "93", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -909,13 +1033,13 @@ "decorators": [] }, { - "$id": "84", + "$id": "94", "kind": "constant", "name": "TreeSpecies", "namespace": "SampleTypeSpec", "usage": "Input,Output,Json,Xml", "valueType": { - "$id": "85", + "$id": "95", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -925,13 +1049,13 @@ "decorators": [] }, { - "$id": "86", + "$id": "96", "kind": "constant", "name": "sayHiContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "87", + "$id": "97", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -941,13 +1065,13 @@ "decorators": [] }, { - "$id": "88", + "$id": "98", "kind": "constant", "name": "HelloAgainRequestContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "89", + "$id": "99", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -957,13 +1081,13 @@ "decorators": [] }, { - "$id": "90", + "$id": "100", "kind": "constant", "name": "HelloAgainRequestContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "91", + "$id": "101", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -973,13 +1097,13 @@ "decorators": [] }, { - "$id": "92", + "$id": "102", "kind": "constant", "name": "helloAgainContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "93", + "$id": "103", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -989,13 +1113,13 @@ "decorators": [] }, { - "$id": "94", + "$id": "104", "kind": "constant", "name": "noContentTypeContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "95", + "$id": "105", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1005,13 +1129,13 @@ "decorators": [] }, { - "$id": "96", + "$id": "106", "kind": "constant", "name": "noContentTypeContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "97", + "$id": "107", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1021,13 +1145,13 @@ "decorators": [] }, { - "$id": "98", + "$id": "108", "kind": "constant", "name": "helloDemo2ContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "99", + "$id": "109", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1037,13 +1161,13 @@ "decorators": [] }, { - "$id": "100", + "$id": "110", "kind": "constant", "name": "createLiteralContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "101", + "$id": "111", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1053,13 +1177,13 @@ "decorators": [] }, { - "$id": "102", + "$id": "112", "kind": "constant", "name": "createLiteralContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "103", + "$id": "113", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1069,13 +1193,13 @@ "decorators": [] }, { - "$id": "104", + "$id": "114", "kind": "constant", "name": "HelloLiteralRequestP1", "namespace": "", "usage": "None", "valueType": { - "$id": "105", + "$id": "115", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1085,13 +1209,13 @@ "decorators": [] }, { - "$id": "106", + "$id": "116", "kind": "constant", "name": "HelloLiteralRequestP11", "namespace": "", "usage": "None", "valueType": { - "$id": "107", + "$id": "117", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1101,13 +1225,13 @@ "decorators": [] }, { - "$id": "108", + "$id": "118", "kind": "constant", "name": "ThingRequiredLiteralInt1", "namespace": "", "usage": "None", "valueType": { - "$id": "109", + "$id": "119", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -1117,13 +1241,13 @@ "decorators": [] }, { - "$id": "110", + "$id": "120", "kind": "constant", "name": "ThingRequiredLiteralInt2", "namespace": "", "usage": "None", "valueType": { - "$id": "111", + "$id": "121", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -1133,13 +1257,13 @@ "decorators": [] }, { - "$id": "112", + "$id": "122", "kind": "constant", "name": "ThingOptionalLiteralBool1", "namespace": "", "usage": "None", "valueType": { - "$id": "113", + "$id": "123", "kind": "boolean", "name": "boolean", "crossLanguageDefinitionId": "TypeSpec.boolean", @@ -1149,13 +1273,13 @@ "decorators": [] }, { - "$id": "114", + "$id": "124", "kind": "constant", "name": "ThingOptionalLiteralBool2", "namespace": "", "usage": "None", "valueType": { - "$id": "115", + "$id": "125", "kind": "boolean", "name": "boolean", "crossLanguageDefinitionId": "TypeSpec.boolean", @@ -1165,13 +1289,13 @@ "decorators": [] }, { - "$id": "116", + "$id": "126", "kind": "constant", "name": "helloLiteralContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "117", + "$id": "127", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1181,13 +1305,13 @@ "decorators": [] }, { - "$id": "118", + "$id": "128", "kind": "constant", "name": "topActionContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "119", + "$id": "129", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1197,13 +1321,13 @@ "decorators": [] }, { - "$id": "120", + "$id": "130", "kind": "constant", "name": "topAction2ContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "121", + "$id": "131", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1213,13 +1337,13 @@ "decorators": [] }, { - "$id": "122", + "$id": "132", "kind": "constant", "name": "patchActionContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "123", + "$id": "133", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1229,13 +1353,13 @@ "decorators": [] }, { - "$id": "124", + "$id": "134", "kind": "constant", "name": "patchActionContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "125", + "$id": "135", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1245,13 +1369,13 @@ "decorators": [] }, { - "$id": "126", + "$id": "136", "kind": "constant", "name": "AnonymousBodyRequestRequiredQueryParam", "namespace": "", "usage": "None", "valueType": { - "$id": "127", + "$id": "137", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1261,13 +1385,13 @@ "decorators": [] }, { - "$id": "128", + "$id": "138", "kind": "constant", "name": "AnonymousBodyRequestRequiredQueryParam1", "namespace": "", "usage": "None", "valueType": { - "$id": "129", + "$id": "139", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1277,13 +1401,13 @@ "decorators": [] }, { - "$id": "130", + "$id": "140", "kind": "constant", "name": "AnonymousBodyRequestRequiredHeader", "namespace": "", "usage": "None", "valueType": { - "$id": "131", + "$id": "141", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1293,13 +1417,13 @@ "decorators": [] }, { - "$id": "132", + "$id": "142", "kind": "constant", "name": "AnonymousBodyRequestRequiredHeader1", "namespace": "", "usage": "None", "valueType": { - "$id": "133", + "$id": "143", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1309,13 +1433,13 @@ "decorators": [] }, { - "$id": "134", + "$id": "144", "kind": "constant", "name": "anonymousBodyContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "135", + "$id": "145", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1325,13 +1449,13 @@ "decorators": [] }, { - "$id": "136", + "$id": "146", "kind": "constant", "name": "anonymousBodyContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "137", + "$id": "147", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1341,13 +1465,13 @@ "decorators": [] }, { - "$id": "138", + "$id": "148", "kind": "constant", "name": "ThingRequiredLiteralString1", "namespace": "", "usage": "None", "valueType": { - "$id": "139", + "$id": "149", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1357,13 +1481,13 @@ "decorators": [] }, { - "$id": "140", + "$id": "150", "kind": "constant", "name": "ThingRequiredLiteralInt3", "namespace": "", "usage": "None", "valueType": { - "$id": "141", + "$id": "151", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -1373,13 +1497,13 @@ "decorators": [] }, { - "$id": "142", + "$id": "152", "kind": "constant", "name": "ThingRequiredLiteralFloat1", "namespace": "", "usage": "None", "valueType": { - "$id": "143", + "$id": "153", "kind": "float32", "name": "float32", "crossLanguageDefinitionId": "TypeSpec.float32", @@ -1389,13 +1513,13 @@ "decorators": [] }, { - "$id": "144", + "$id": "154", "kind": "constant", "name": "ThingRequiredLiteralBool1", "namespace": "", "usage": "None", "valueType": { - "$id": "145", + "$id": "155", "kind": "boolean", "name": "boolean", "crossLanguageDefinitionId": "TypeSpec.boolean", @@ -1405,13 +1529,13 @@ "decorators": [] }, { - "$id": "146", + "$id": "156", "kind": "constant", "name": "ThingOptionalLiteralBool3", "namespace": "", "usage": "None", "valueType": { - "$id": "147", + "$id": "157", "kind": "boolean", "name": "boolean", "crossLanguageDefinitionId": "TypeSpec.boolean", @@ -1421,13 +1545,13 @@ "decorators": [] }, { - "$id": "148", + "$id": "158", "kind": "constant", "name": "friendlyModelContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "149", + "$id": "159", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1437,13 +1561,13 @@ "decorators": [] }, { - "$id": "150", + "$id": "160", "kind": "constant", "name": "friendlyModelContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "151", + "$id": "161", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1453,13 +1577,13 @@ "decorators": [] }, { - "$id": "152", + "$id": "162", "kind": "constant", "name": "projectedNameModelContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "153", + "$id": "163", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1469,13 +1593,13 @@ "decorators": [] }, { - "$id": "154", + "$id": "164", "kind": "constant", "name": "projectedNameModelContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "155", + "$id": "165", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1485,13 +1609,13 @@ "decorators": [] }, { - "$id": "156", + "$id": "166", "kind": "constant", "name": "returnsAnonymousModelContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "157", + "$id": "167", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1501,13 +1625,13 @@ "decorators": [] }, { - "$id": "158", + "$id": "168", "kind": "constant", "name": "internalProtocolContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "159", + "$id": "169", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1517,13 +1641,13 @@ "decorators": [] }, { - "$id": "160", + "$id": "170", "kind": "constant", "name": "internalProtocolContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "161", + "$id": "171", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1533,13 +1657,13 @@ "decorators": [] }, { - "$id": "162", + "$id": "172", "kind": "constant", "name": "ListWithNextLinkContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "163", + "$id": "173", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1549,13 +1673,13 @@ "decorators": [] }, { - "$id": "164", + "$id": "174", "kind": "constant", "name": "ListWithStringNextLinkContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "165", + "$id": "175", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1565,13 +1689,13 @@ "decorators": [] }, { - "$id": "166", + "$id": "176", "kind": "constant", "name": "ListWithContinuationTokenContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "167", + "$id": "177", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1581,13 +1705,13 @@ "decorators": [] }, { - "$id": "168", + "$id": "178", "kind": "constant", "name": "ListWithContinuationTokenHeaderResponseContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "169", + "$id": "179", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1597,13 +1721,13 @@ "decorators": [] }, { - "$id": "170", + "$id": "180", "kind": "constant", "name": "ListWithPagingContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "171", + "$id": "181", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1613,13 +1737,13 @@ "decorators": [] }, { - "$id": "172", + "$id": "182", "kind": "constant", "name": "EmbeddedParametersContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "173", + "$id": "183", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1629,13 +1753,13 @@ "decorators": [] }, { - "$id": "174", + "$id": "184", "kind": "constant", "name": "DynamicModelOperationContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "175", + "$id": "185", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1645,13 +1769,13 @@ "decorators": [] }, { - "$id": "176", + "$id": "186", "kind": "constant", "name": "GetXmlAdvancedModelContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "177", + "$id": "187", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1661,13 +1785,13 @@ "decorators": [] }, { - "$id": "178", + "$id": "188", "kind": "constant", "name": "GetXmlAdvancedModelResponseContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "179", + "$id": "189", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1677,13 +1801,13 @@ "decorators": [] }, { - "$id": "180", + "$id": "190", "kind": "constant", "name": "GetXmlAdvancedModelResponseContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "181", + "$id": "191", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1693,13 +1817,13 @@ "decorators": [] }, { - "$id": "182", + "$id": "192", "kind": "constant", "name": "GetXmlAdvancedModelResponseContentType2", "namespace": "", "usage": "None", "valueType": { - "$id": "183", + "$id": "193", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1709,13 +1833,13 @@ "decorators": [] }, { - "$id": "184", + "$id": "194", "kind": "constant", "name": "UpdateXmlAdvancedModelContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "185", + "$id": "195", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1725,13 +1849,13 @@ "decorators": [] }, { - "$id": "186", + "$id": "196", "kind": "constant", "name": "GetXmlAdvancedModelResponseContentType3", "namespace": "", "usage": "None", "valueType": { - "$id": "187", + "$id": "197", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1741,13 +1865,13 @@ "decorators": [] }, { - "$id": "188", + "$id": "198", "kind": "constant", "name": "updatePetAsAnimalContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "189", + "$id": "199", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1757,13 +1881,13 @@ "decorators": [] }, { - "$id": "190", + "$id": "200", "kind": "constant", "name": "updatePetAsAnimalContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "191", + "$id": "201", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1773,13 +1897,13 @@ "decorators": [] }, { - "$id": "192", + "$id": "202", "kind": "constant", "name": "updateDogAsAnimalContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "193", + "$id": "203", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1789,13 +1913,13 @@ "decorators": [] }, { - "$id": "194", + "$id": "204", "kind": "constant", "name": "updateDogAsAnimalContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "195", + "$id": "205", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1805,13 +1929,13 @@ "decorators": [] }, { - "$id": "196", + "$id": "206", "kind": "constant", "name": "updatePetAsPetContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "197", + "$id": "207", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1821,13 +1945,13 @@ "decorators": [] }, { - "$id": "198", + "$id": "208", "kind": "constant", "name": "updatePetAsPetContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "199", + "$id": "209", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1837,13 +1961,13 @@ "decorators": [] }, { - "$id": "200", + "$id": "210", "kind": "constant", "name": "updateDogAsPetContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "201", + "$id": "211", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1853,13 +1977,13 @@ "decorators": [] }, { - "$id": "202", + "$id": "212", "kind": "constant", "name": "updateDogAsPetContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "203", + "$id": "213", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1869,13 +1993,13 @@ "decorators": [] }, { - "$id": "204", + "$id": "214", "kind": "constant", "name": "updateDogAsDogContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "205", + "$id": "215", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1885,13 +2009,13 @@ "decorators": [] }, { - "$id": "206", + "$id": "216", "kind": "constant", "name": "updateDogAsDogContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "207", + "$id": "217", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1901,13 +2025,13 @@ "decorators": [] }, { - "$id": "208", + "$id": "218", "kind": "constant", "name": "getTreeContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "209", + "$id": "219", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1917,13 +2041,13 @@ "decorators": [] }, { - "$id": "210", + "$id": "220", "kind": "constant", "name": "GetXmlAdvancedModelResponseContentType4", "namespace": "", "usage": "None", "valueType": { - "$id": "211", + "$id": "221", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1933,13 +2057,13 @@ "decorators": [] }, { - "$id": "212", + "$id": "222", "kind": "constant", "name": "getTreeAsJsonContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "213", + "$id": "223", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1949,13 +2073,13 @@ "decorators": [] }, { - "$id": "214", + "$id": "224", "kind": "constant", "name": "GetTreeAsJsonResponseContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "215", + "$id": "225", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1965,13 +2089,13 @@ "decorators": [] }, { - "$id": "216", + "$id": "226", "kind": "constant", "name": "GetXmlAdvancedModelResponseContentType5", "namespace": "", "usage": "None", "valueType": { - "$id": "217", + "$id": "227", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1981,13 +2105,13 @@ "decorators": [] }, { - "$id": "218", + "$id": "228", "kind": "constant", "name": "GetXmlAdvancedModelResponseContentType6", "namespace": "", "usage": "None", "valueType": { - "$id": "219", + "$id": "229", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -1997,13 +2121,13 @@ "decorators": [] }, { - "$id": "220", + "$id": "230", "kind": "constant", "name": "updateTreeContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "221", + "$id": "231", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2013,13 +2137,13 @@ "decorators": [] }, { - "$id": "222", + "$id": "232", "kind": "constant", "name": "GetXmlAdvancedModelResponseContentType7", "namespace": "", "usage": "None", "valueType": { - "$id": "223", + "$id": "233", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2029,13 +2153,13 @@ "decorators": [] }, { - "$id": "224", + "$id": "234", "kind": "constant", "name": "GetTreeAsJsonResponseContentType1", "namespace": "", "usage": "None", "valueType": { - "$id": "225", + "$id": "235", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2045,13 +2169,13 @@ "decorators": [] }, { - "$id": "226", + "$id": "236", "kind": "constant", "name": "GetTreeAsJsonResponseContentType2", "namespace": "", "usage": "None", "valueType": { - "$id": "227", + "$id": "237", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2061,13 +2185,13 @@ "decorators": [] }, { - "$id": "228", + "$id": "238", "kind": "constant", "name": "updateTreeAsJsonContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "229", + "$id": "239", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2077,13 +2201,13 @@ "decorators": [] }, { - "$id": "230", + "$id": "240", "kind": "constant", "name": "GetTreeAsJsonResponseContentType3", "namespace": "", "usage": "None", "valueType": { - "$id": "231", + "$id": "241", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2093,13 +2217,13 @@ "decorators": [] }, { - "$id": "232", + "$id": "242", "kind": "constant", "name": "getWidgetMetricsContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "233", + "$id": "243", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2109,13 +2233,13 @@ "decorators": [] }, { - "$id": "234", + "$id": "244", "kind": "constant", "name": "getNotebookContentType", "namespace": "", "usage": "None", "valueType": { - "$id": "235", + "$id": "245", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2127,7 +2251,7 @@ ], "models": [ { - "$id": "236", + "$id": "246", "kind": "model", "name": "Thing", "namespace": "SampleTypeSpec", @@ -2142,13 +2266,13 @@ }, "properties": [ { - "$id": "237", + "$id": "247", "kind": "property", "name": "name", "serializedName": "name", "doc": "name of the Thing", "type": { - "$id": "238", + "$id": "248", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2168,29 +2292,29 @@ "isHttpMetadata": false }, { - "$id": "239", + "$id": "249", "kind": "property", "name": "requiredUnion", "serializedName": "requiredUnion", "doc": "required Union", "type": { - "$id": "240", + "$id": "250", "kind": "union", "name": "ThingRequiredUnion", "variantTypes": [ { - "$id": "241", + "$id": "251", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", "decorators": [] }, { - "$id": "242", + "$id": "252", "kind": "array", "name": "Array", "valueType": { - "$id": "243", + "$id": "253", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2200,7 +2324,7 @@ "decorators": [] }, { - "$id": "244", + "$id": "254", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -2224,13 +2348,13 @@ "isHttpMetadata": false }, { - "$id": "245", + "$id": "255", "kind": "property", "name": "requiredLiteralString", "serializedName": "requiredLiteralString", "doc": "required literal string", "type": { - "$ref": "70" + "$ref": "80" }, "optional": false, "readOnly": false, @@ -2246,16 +2370,16 @@ "isHttpMetadata": false }, { - "$id": "246", + "$id": "256", "kind": "property", "name": "requiredNullableString", "serializedName": "requiredNullableString", "doc": "required nullable string", "type": { - "$id": "247", + "$id": "257", "kind": "nullable", "type": { - "$id": "248", + "$id": "258", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2277,16 +2401,16 @@ "isHttpMetadata": false }, { - "$id": "249", + "$id": "259", "kind": "property", "name": "optionalNullableString", "serializedName": "optionalNullableString", "doc": "required optional string", "type": { - "$id": "250", + "$id": "260", "kind": "nullable", "type": { - "$id": "251", + "$id": "261", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2308,13 +2432,13 @@ "isHttpMetadata": false }, { - "$id": "252", + "$id": "262", "kind": "property", "name": "requiredLiteralInt", "serializedName": "requiredLiteralInt", "doc": "required literal int", "type": { - "$ref": "72" + "$ref": "82" }, "optional": false, "readOnly": false, @@ -2330,13 +2454,13 @@ "isHttpMetadata": false }, { - "$id": "253", + "$id": "263", "kind": "property", "name": "requiredLiteralFloat", "serializedName": "requiredLiteralFloat", "doc": "required literal float", "type": { - "$ref": "74" + "$ref": "84" }, "optional": false, "readOnly": false, @@ -2352,13 +2476,13 @@ "isHttpMetadata": false }, { - "$id": "254", + "$id": "264", "kind": "property", "name": "requiredLiteralBool", "serializedName": "requiredLiteralBool", "doc": "required literal bool", "type": { - "$ref": "76" + "$ref": "86" }, "optional": false, "readOnly": false, @@ -2374,7 +2498,7 @@ "isHttpMetadata": false }, { - "$id": "255", + "$id": "265", "kind": "property", "name": "optionalLiteralString", "serializedName": "optionalLiteralString", @@ -2396,13 +2520,13 @@ "isHttpMetadata": false }, { - "$id": "256", + "$id": "266", "kind": "property", "name": "requiredNullableLiteralString", "serializedName": "requiredNullableLiteralString", "doc": "required nullable literal string", "type": { - "$id": "257", + "$id": "267", "kind": "nullable", "type": { "$ref": "5" @@ -2423,7 +2547,7 @@ "isHttpMetadata": false }, { - "$id": "258", + "$id": "268", "kind": "property", "name": "optionalLiteralInt", "serializedName": "optionalLiteralInt", @@ -2445,7 +2569,7 @@ "isHttpMetadata": false }, { - "$id": "259", + "$id": "269", "kind": "property", "name": "optionalLiteralFloat", "serializedName": "optionalLiteralFloat", @@ -2467,13 +2591,13 @@ "isHttpMetadata": false }, { - "$id": "260", + "$id": "270", "kind": "property", "name": "optionalLiteralBool", "serializedName": "optionalLiteralBool", "doc": "optional literal bool", "type": { - "$ref": "78" + "$ref": "88" }, "optional": true, "readOnly": false, @@ -2489,13 +2613,13 @@ "isHttpMetadata": false }, { - "$id": "261", + "$id": "271", "kind": "property", "name": "requiredBadDescription", "serializedName": "requiredBadDescription", "doc": "description with xml <|endoftext|>", "type": { - "$id": "262", + "$id": "272", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2515,20 +2639,20 @@ "isHttpMetadata": false }, { - "$id": "263", + "$id": "273", "kind": "property", "name": "optionalNullableList", "serializedName": "optionalNullableList", "doc": "optional nullable collection", "type": { - "$id": "264", + "$id": "274", "kind": "nullable", "type": { - "$id": "265", + "$id": "275", "kind": "array", "name": "Array1", "valueType": { - "$id": "266", + "$id": "276", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -2553,16 +2677,16 @@ "isHttpMetadata": false }, { - "$id": "267", + "$id": "277", "kind": "property", "name": "requiredNullableList", "serializedName": "requiredNullableList", "doc": "required nullable collection", "type": { - "$id": "268", + "$id": "278", "kind": "nullable", "type": { - "$ref": "265" + "$ref": "275" }, "namespace": "SampleTypeSpec" }, @@ -2580,13 +2704,13 @@ "isHttpMetadata": false }, { - "$id": "269", + "$id": "279", "kind": "property", "name": "propertyWithSpecialDocs", "serializedName": "propertyWithSpecialDocs", "doc": "This tests:\n- Simple bullet point. This bullet point is going to be very long to test how text wrapping is handled in bullet points within documentation comments. It should properly indent the wrapped lines.\n- Another bullet point with **bold text**. This bullet point is also intentionally long to see how the formatting is preserved when the text wraps onto multiple lines in the generated documentation.\n- Third bullet point with *italic text*. Similar to the previous points, this one is extended to ensure that the wrapping and formatting are correctly applied in the output.\n- Complex bullet point with **bold** and *italic* combined. This bullet point combines both bold and italic formatting and is long enough to test the wrapping behavior in such cases.\n- **Bold bullet point**: A bullet point that is entirely bolded. This point is also made lengthy to observe how the bold formatting is maintained across wrapped lines.\n- *Italic bullet point*: A bullet point that is entirely italicized. This final point is extended to verify that italic formatting is correctly applied even when the text spans multiple lines.", "type": { - "$id": "270", + "$id": "280", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2608,7 +2732,7 @@ ] }, { - "$id": "271", + "$id": "281", "kind": "model", "name": "RoundTripModel", "namespace": "SampleTypeSpec", @@ -2623,13 +2747,13 @@ }, "properties": [ { - "$id": "272", + "$id": "282", "kind": "property", "name": "requiredString", "serializedName": "requiredString", "doc": "Required string, illustrating a reference type property.", "type": { - "$id": "273", + "$id": "283", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2649,13 +2773,13 @@ "isHttpMetadata": false }, { - "$id": "274", + "$id": "284", "kind": "property", "name": "requiredInt", "serializedName": "requiredInt", "doc": "Required int, illustrating a value type property.", "type": { - "$id": "275", + "$id": "285", "kind": "int32", "name": "int32", "encode": "string", @@ -2676,13 +2800,13 @@ "isHttpMetadata": false }, { - "$id": "276", + "$id": "286", "kind": "property", "name": "requiredCollection", "serializedName": "requiredCollection", "doc": "Required collection of enums", "type": { - "$id": "277", + "$id": "287", "kind": "array", "name": "ArrayStringFixedEnum", "valueType": { @@ -2705,16 +2829,16 @@ "isHttpMetadata": false }, { - "$id": "278", + "$id": "288", "kind": "property", "name": "requiredDictionary", "serializedName": "requiredDictionary", "doc": "Required dictionary of enums", "type": { - "$id": "279", + "$id": "289", "kind": "dict", "keyType": { - "$id": "280", + "$id": "290", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -2739,13 +2863,13 @@ "isHttpMetadata": false }, { - "$id": "281", + "$id": "291", "kind": "property", "name": "requiredModel", "serializedName": "requiredModel", "doc": "Required model", "type": { - "$ref": "236" + "$ref": "246" }, "optional": false, "readOnly": false, @@ -2761,7 +2885,7 @@ "isHttpMetadata": false }, { - "$id": "282", + "$id": "292", "kind": "property", "name": "intExtensibleEnum", "serializedName": "intExtensibleEnum", @@ -2783,13 +2907,13 @@ "isHttpMetadata": false }, { - "$id": "283", + "$id": "293", "kind": "property", "name": "intExtensibleEnumCollection", "serializedName": "intExtensibleEnumCollection", "doc": "this is a collection of int based extensible enum", "type": { - "$id": "284", + "$id": "294", "kind": "array", "name": "ArrayIntExtensibleEnum", "valueType": { @@ -2812,7 +2936,7 @@ "isHttpMetadata": false }, { - "$id": "285", + "$id": "295", "kind": "property", "name": "floatExtensibleEnum", "serializedName": "floatExtensibleEnum", @@ -2834,7 +2958,7 @@ "isHttpMetadata": false }, { - "$id": "286", + "$id": "296", "kind": "property", "name": "floatExtensibleEnumWithIntValue", "serializedName": "floatExtensibleEnumWithIntValue", @@ -2856,13 +2980,13 @@ "isHttpMetadata": false }, { - "$id": "287", + "$id": "297", "kind": "property", "name": "floatExtensibleEnumCollection", "serializedName": "floatExtensibleEnumCollection", "doc": "this is a collection of float based extensible enum", "type": { - "$id": "288", + "$id": "298", "kind": "array", "name": "ArrayFloatExtensibleEnum", "valueType": { @@ -2885,7 +3009,7 @@ "isHttpMetadata": false }, { - "$id": "289", + "$id": "299", "kind": "property", "name": "floatFixedEnum", "serializedName": "floatFixedEnum", @@ -2907,7 +3031,7 @@ "isHttpMetadata": false }, { - "$id": "290", + "$id": "300", "kind": "property", "name": "floatFixedEnumWithIntValue", "serializedName": "floatFixedEnumWithIntValue", @@ -2929,13 +3053,13 @@ "isHttpMetadata": false }, { - "$id": "291", + "$id": "301", "kind": "property", "name": "floatFixedEnumCollection", "serializedName": "floatFixedEnumCollection", "doc": "this is a collection of float based fixed enum", "type": { - "$id": "292", + "$id": "302", "kind": "array", "name": "ArrayFloatFixedEnum", "valueType": { @@ -2958,7 +3082,7 @@ "isHttpMetadata": false }, { - "$id": "293", + "$id": "303", "kind": "property", "name": "intFixedEnum", "serializedName": "intFixedEnum", @@ -2980,13 +3104,13 @@ "isHttpMetadata": false }, { - "$id": "294", + "$id": "304", "kind": "property", "name": "intFixedEnumCollection", "serializedName": "intFixedEnumCollection", "doc": "this is a collection of int based fixed enum", "type": { - "$id": "295", + "$id": "305", "kind": "array", "name": "ArrayIntFixedEnum", "valueType": { @@ -3009,7 +3133,7 @@ "isHttpMetadata": false }, { - "$id": "296", + "$id": "306", "kind": "property", "name": "stringFixedEnum", "serializedName": "stringFixedEnum", @@ -3031,13 +3155,13 @@ "isHttpMetadata": false }, { - "$id": "297", + "$id": "307", "kind": "property", "name": "requiredUnknown", "serializedName": "requiredUnknown", "doc": "required unknown", "type": { - "$id": "298", + "$id": "308", "kind": "unknown", "name": "unknown", "crossLanguageDefinitionId": "", @@ -3057,13 +3181,13 @@ "isHttpMetadata": false }, { - "$id": "299", + "$id": "309", "kind": "property", "name": "optionalUnknown", "serializedName": "optionalUnknown", "doc": "optional unknown", "type": { - "$id": "300", + "$id": "310", "kind": "unknown", "name": "unknown", "crossLanguageDefinitionId": "", @@ -3083,23 +3207,23 @@ "isHttpMetadata": false }, { - "$id": "301", + "$id": "311", "kind": "property", "name": "requiredRecordUnknown", "serializedName": "requiredRecordUnknown", "doc": "required record of unknown", "type": { - "$id": "302", + "$id": "312", "kind": "dict", "keyType": { - "$id": "303", + "$id": "313", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", "decorators": [] }, "valueType": { - "$id": "304", + "$id": "314", "kind": "unknown", "name": "unknown", "crossLanguageDefinitionId": "", @@ -3121,13 +3245,13 @@ "isHttpMetadata": false }, { - "$id": "305", + "$id": "315", "kind": "property", "name": "optionalRecordUnknown", "serializedName": "optionalRecordUnknown", "doc": "optional record of unknown", "type": { - "$ref": "302" + "$ref": "312" }, "optional": true, "readOnly": false, @@ -3143,13 +3267,13 @@ "isHttpMetadata": false }, { - "$id": "306", + "$id": "316", "kind": "property", "name": "readOnlyRequiredRecordUnknown", "serializedName": "readOnlyRequiredRecordUnknown", "doc": "required readonly record of unknown", "type": { - "$ref": "302" + "$ref": "312" }, "optional": false, "readOnly": true, @@ -3165,13 +3289,13 @@ "isHttpMetadata": false }, { - "$id": "307", + "$id": "317", "kind": "property", "name": "readOnlyOptionalRecordUnknown", "serializedName": "readOnlyOptionalRecordUnknown", "doc": "optional readonly record of unknown", "type": { - "$ref": "302" + "$ref": "312" }, "optional": true, "readOnly": true, @@ -3187,13 +3311,13 @@ "isHttpMetadata": false }, { - "$id": "308", + "$id": "318", "kind": "property", "name": "modelWithRequiredNullable", "serializedName": "modelWithRequiredNullable", "doc": "this is a model with required nullable properties", "type": { - "$id": "309", + "$id": "319", "kind": "model", "name": "ModelWithRequiredNullableProperties", "namespace": "SampleTypeSpec", @@ -3208,16 +3332,16 @@ }, "properties": [ { - "$id": "310", + "$id": "320", "kind": "property", "name": "requiredNullablePrimitive", "serializedName": "requiredNullablePrimitive", "doc": "required nullable primitive type", "type": { - "$id": "311", + "$id": "321", "kind": "nullable", "type": { - "$id": "312", + "$id": "322", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -3239,13 +3363,13 @@ "isHttpMetadata": false }, { - "$id": "313", + "$id": "323", "kind": "property", "name": "requiredExtensibleEnum", "serializedName": "requiredExtensibleEnum", "doc": "required nullable extensible enum type", "type": { - "$id": "314", + "$id": "324", "kind": "nullable", "type": { "$ref": "22" @@ -3266,13 +3390,13 @@ "isHttpMetadata": false }, { - "$id": "315", + "$id": "325", "kind": "property", "name": "requiredFixedEnum", "serializedName": "requiredFixedEnum", "doc": "required nullable fixed enum type", "type": { - "$id": "316", + "$id": "326", "kind": "nullable", "type": { "$ref": "17" @@ -3308,13 +3432,13 @@ "isHttpMetadata": false }, { - "$id": "317", + "$id": "327", "kind": "property", "name": "requiredBytes", "serializedName": "requiredBytes", "doc": "Required bytes", "type": { - "$id": "318", + "$id": "328", "kind": "bytes", "name": "bytes", "encode": "base64", @@ -3337,10 +3461,10 @@ ] }, { - "$ref": "309" + "$ref": "319" }, { - "$id": "319", + "$id": "329", "kind": "model", "name": "Wrapper", "namespace": "SampleTypeSpec", @@ -3350,12 +3474,12 @@ "serializationOptions": {}, "properties": [ { - "$id": "320", + "$id": "330", "kind": "property", "name": "p1", "doc": "header parameter", "type": { - "$id": "321", + "$id": "331", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -3371,12 +3495,12 @@ "isHttpMetadata": true }, { - "$id": "322", + "$id": "332", "kind": "property", "name": "action", "doc": "body parameter", "type": { - "$ref": "271" + "$ref": "281" }, "optional": false, "readOnly": false, @@ -3388,12 +3512,12 @@ "isHttpMetadata": false }, { - "$id": "323", + "$id": "333", "kind": "property", "name": "p2", "doc": "path parameter", "type": { - "$id": "324", + "$id": "334", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -3411,7 +3535,7 @@ ] }, { - "$id": "325", + "$id": "335", "kind": "model", "name": "Friend", "namespace": "SampleTypeSpec", @@ -3426,13 +3550,13 @@ }, "properties": [ { - "$id": "326", + "$id": "336", "kind": "property", "name": "name", "serializedName": "name", "doc": "name of the NotFriend", "type": { - "$id": "327", + "$id": "337", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -3454,7 +3578,7 @@ ] }, { - "$id": "328", + "$id": "338", "kind": "model", "name": "RenamedModel", "namespace": "SampleTypeSpec", @@ -3469,13 +3593,13 @@ }, "properties": [ { - "$id": "329", + "$id": "339", "kind": "property", "name": "otherName", "serializedName": "otherName", "doc": "name of the ModelWithClientName", "type": { - "$id": "330", + "$id": "340", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -3497,7 +3621,7 @@ ] }, { - "$id": "331", + "$id": "341", "kind": "model", "name": "ReturnsAnonymousModelResponse", "namespace": "SampleTypeSpec", @@ -3512,7 +3636,7 @@ "properties": [] }, { - "$id": "332", + "$id": "342", "kind": "model", "name": "ListWithNextLinkResponse", "namespace": "SampleTypeSpec", @@ -3526,16 +3650,16 @@ }, "properties": [ { - "$id": "333", + "$id": "343", "kind": "property", "name": "things", "serializedName": "things", "type": { - "$id": "334", + "$id": "344", "kind": "array", "name": "ArrayThing", "valueType": { - "$ref": "236" + "$ref": "246" }, "crossLanguageDefinitionId": "TypeSpec.Array", "decorators": [] @@ -3554,12 +3678,12 @@ "isHttpMetadata": false }, { - "$id": "335", + "$id": "345", "kind": "property", "name": "next", "serializedName": "next", "type": { - "$id": "336", + "$id": "346", "kind": "url", "name": "url", "crossLanguageDefinitionId": "TypeSpec.url", @@ -3581,7 +3705,7 @@ ] }, { - "$id": "337", + "$id": "347", "kind": "model", "name": "ListWithStringNextLinkResponse", "namespace": "SampleTypeSpec", @@ -3595,12 +3719,12 @@ }, "properties": [ { - "$id": "338", + "$id": "348", "kind": "property", "name": "things", "serializedName": "things", "type": { - "$ref": "334" + "$ref": "344" }, "optional": false, "readOnly": false, @@ -3616,12 +3740,12 @@ "isHttpMetadata": false }, { - "$id": "339", + "$id": "349", "kind": "property", "name": "next", "serializedName": "next", "type": { - "$id": "340", + "$id": "350", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -3643,7 +3767,7 @@ ] }, { - "$id": "341", + "$id": "351", "kind": "model", "name": "ListWithContinuationTokenResponse", "namespace": "SampleTypeSpec", @@ -3657,12 +3781,12 @@ }, "properties": [ { - "$id": "342", + "$id": "352", "kind": "property", "name": "things", "serializedName": "things", "type": { - "$ref": "334" + "$ref": "344" }, "optional": false, "readOnly": false, @@ -3678,12 +3802,12 @@ "isHttpMetadata": false }, { - "$id": "343", + "$id": "353", "kind": "property", "name": "nextToken", "serializedName": "nextToken", "type": { - "$id": "344", + "$id": "354", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -3705,7 +3829,7 @@ ] }, { - "$id": "345", + "$id": "355", "kind": "model", "name": "ListWithContinuationTokenHeaderResponseResponse", "namespace": "", @@ -3719,12 +3843,12 @@ }, "properties": [ { - "$id": "346", + "$id": "356", "kind": "property", "name": "things", "serializedName": "things", "type": { - "$ref": "334" + "$ref": "344" }, "optional": false, "readOnly": false, @@ -3742,7 +3866,7 @@ ] }, { - "$id": "347", + "$id": "357", "kind": "model", "name": "PageThing", "namespace": "SampleTypeSpec", @@ -3756,12 +3880,12 @@ }, "properties": [ { - "$id": "348", + "$id": "358", "kind": "property", "name": "items", "serializedName": "items", "type": { - "$ref": "334" + "$ref": "344" }, "optional": false, "readOnly": false, @@ -3779,7 +3903,7 @@ ] }, { - "$id": "349", + "$id": "359", "kind": "model", "name": "ModelWithEmbeddedNonBodyParameters", "namespace": "SampleTypeSpec", @@ -3793,13 +3917,13 @@ }, "properties": [ { - "$id": "350", + "$id": "360", "kind": "property", "name": "name", "serializedName": "name", "doc": "name of the ModelWithEmbeddedNonBodyParameters", "type": { - "$id": "351", + "$id": "361", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -3819,13 +3943,13 @@ "isHttpMetadata": false }, { - "$id": "352", + "$id": "362", "kind": "property", "name": "requiredHeader", "serializedName": "requiredHeader", "doc": "required header parameter", "type": { - "$id": "353", + "$id": "363", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -3845,13 +3969,13 @@ "isHttpMetadata": true }, { - "$id": "354", + "$id": "364", "kind": "property", "name": "optionalHeader", "serializedName": "optionalHeader", "doc": "optional header parameter", "type": { - "$id": "355", + "$id": "365", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -3871,13 +3995,13 @@ "isHttpMetadata": true }, { - "$id": "356", + "$id": "366", "kind": "property", "name": "requiredQuery", "serializedName": "requiredQuery", "doc": "required query parameter", "type": { - "$id": "357", + "$id": "367", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -3897,13 +4021,13 @@ "isHttpMetadata": true }, { - "$id": "358", + "$id": "368", "kind": "property", "name": "optionalQuery", "serializedName": "optionalQuery", "doc": "optional query parameter", "type": { - "$id": "359", + "$id": "369", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -3925,7 +4049,7 @@ ] }, { - "$id": "360", + "$id": "370", "kind": "model", "name": "DynamicModel", "namespace": "SampleTypeSpec", @@ -3945,12 +4069,12 @@ }, "properties": [ { - "$id": "361", + "$id": "371", "kind": "property", "name": "name", "serializedName": "name", "type": { - "$id": "362", + "$id": "372", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -3970,12 +4094,12 @@ "isHttpMetadata": false }, { - "$id": "363", + "$id": "373", "kind": "property", "name": "optionalUnknown", "serializedName": "optionalUnknown", "type": { - "$id": "364", + "$id": "374", "kind": "unknown", "name": "unknown", "crossLanguageDefinitionId": "", @@ -3995,12 +4119,12 @@ "isHttpMetadata": false }, { - "$id": "365", + "$id": "375", "kind": "property", "name": "optionalInt", "serializedName": "optionalInt", "type": { - "$id": "366", + "$id": "376", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -4020,15 +4144,15 @@ "isHttpMetadata": false }, { - "$id": "367", + "$id": "377", "kind": "property", "name": "optionalNullableList", "serializedName": "optionalNullableList", "type": { - "$id": "368", + "$id": "378", "kind": "nullable", "type": { - "$ref": "265" + "$ref": "275" }, "namespace": "SampleTypeSpec" }, @@ -4046,15 +4170,15 @@ "isHttpMetadata": false }, { - "$id": "369", + "$id": "379", "kind": "property", "name": "requiredNullableList", "serializedName": "requiredNullableList", "type": { - "$id": "370", + "$id": "380", "kind": "nullable", "type": { - "$ref": "265" + "$ref": "275" }, "namespace": "SampleTypeSpec" }, @@ -4072,25 +4196,25 @@ "isHttpMetadata": false }, { - "$id": "371", + "$id": "381", "kind": "property", "name": "optionalNullableDictionary", "serializedName": "optionalNullableDictionary", "type": { - "$id": "372", + "$id": "382", "kind": "nullable", "type": { - "$id": "373", + "$id": "383", "kind": "dict", "keyType": { - "$id": "374", + "$id": "384", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", "decorators": [] }, "valueType": { - "$id": "375", + "$id": "385", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -4114,15 +4238,15 @@ "isHttpMetadata": false }, { - "$id": "376", + "$id": "386", "kind": "property", "name": "requiredNullableDictionary", "serializedName": "requiredNullableDictionary", "type": { - "$id": "377", + "$id": "387", "kind": "nullable", "type": { - "$ref": "373" + "$ref": "383" }, "namespace": "SampleTypeSpec" }, @@ -4140,12 +4264,12 @@ "isHttpMetadata": false }, { - "$id": "378", + "$id": "388", "kind": "property", "name": "primitiveDictionary", "serializedName": "primitiveDictionary", "type": { - "$ref": "373" + "$ref": "383" }, "optional": false, "readOnly": false, @@ -4161,12 +4285,12 @@ "isHttpMetadata": false }, { - "$id": "379", + "$id": "389", "kind": "property", "name": "foo", "serializedName": "foo", "type": { - "$id": "380", + "$id": "390", "kind": "model", "name": "AnotherDynamicModel", "namespace": "SampleTypeSpec", @@ -4186,12 +4310,12 @@ }, "properties": [ { - "$id": "381", + "$id": "391", "kind": "property", "name": "bar", "serializedName": "bar", "type": { - "$id": "382", + "$id": "392", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -4226,16 +4350,16 @@ "isHttpMetadata": false }, { - "$id": "383", + "$id": "393", "kind": "property", "name": "listFoo", "serializedName": "listFoo", "type": { - "$id": "384", + "$id": "394", "kind": "array", "name": "ArrayAnotherDynamicModel", "valueType": { - "$ref": "380" + "$ref": "390" }, "crossLanguageDefinitionId": "TypeSpec.Array", "decorators": [] @@ -4254,16 +4378,16 @@ "isHttpMetadata": false }, { - "$id": "385", + "$id": "395", "kind": "property", "name": "listOfListFoo", "serializedName": "listOfListFoo", "type": { - "$id": "386", + "$id": "396", "kind": "array", "name": "ArrayArray", "valueType": { - "$ref": "384" + "$ref": "394" }, "crossLanguageDefinitionId": "TypeSpec.Array", "decorators": [] @@ -4282,22 +4406,22 @@ "isHttpMetadata": false }, { - "$id": "387", + "$id": "397", "kind": "property", "name": "dictionaryFoo", "serializedName": "dictionaryFoo", "type": { - "$id": "388", + "$id": "398", "kind": "dict", "keyType": { - "$id": "389", + "$id": "399", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", "decorators": [] }, "valueType": { - "$ref": "380" + "$ref": "390" }, "decorators": [] }, @@ -4315,22 +4439,22 @@ "isHttpMetadata": false }, { - "$id": "390", + "$id": "400", "kind": "property", "name": "dictionaryOfDictionaryFoo", "serializedName": "dictionaryOfDictionaryFoo", "type": { - "$id": "391", + "$id": "401", "kind": "dict", "keyType": { - "$id": "392", + "$id": "402", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", "decorators": [] }, "valueType": { - "$ref": "388" + "$ref": "398" }, "decorators": [] }, @@ -4348,22 +4472,22 @@ "isHttpMetadata": false }, { - "$id": "393", + "$id": "403", "kind": "property", "name": "dictionaryListFoo", "serializedName": "dictionaryListFoo", "type": { - "$id": "394", + "$id": "404", "kind": "dict", "keyType": { - "$id": "395", + "$id": "405", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", "decorators": [] }, "valueType": { - "$ref": "384" + "$ref": "394" }, "decorators": [] }, @@ -4381,16 +4505,16 @@ "isHttpMetadata": false }, { - "$id": "396", + "$id": "406", "kind": "property", "name": "listOfDictionaryFoo", "serializedName": "listOfDictionaryFoo", "type": { - "$id": "397", + "$id": "407", "kind": "array", "name": "ArrayRecord", "valueType": { - "$ref": "388" + "$ref": "398" }, "crossLanguageDefinitionId": "TypeSpec.Array", "decorators": [] @@ -4411,10 +4535,10 @@ ] }, { - "$ref": "380" + "$ref": "390" }, { - "$id": "398", + "$id": "408", "kind": "model", "name": "XmlAdvancedModel", "namespace": "SampleTypeSpec", @@ -4438,13 +4562,13 @@ }, "properties": [ { - "$id": "399", + "$id": "409", "kind": "property", "name": "name", "serializedName": "name", "doc": "A simple string property", "type": { - "$id": "400", + "$id": "410", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -4466,13 +4590,13 @@ "isHttpMetadata": false }, { - "$id": "401", + "$id": "411", "kind": "property", "name": "age", "serializedName": "age", "doc": "An integer property", "type": { - "$id": "402", + "$id": "412", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -4494,13 +4618,13 @@ "isHttpMetadata": false }, { - "$id": "403", + "$id": "413", "kind": "property", "name": "enabled", "serializedName": "enabled", "doc": "A boolean property", "type": { - "$id": "404", + "$id": "414", "kind": "boolean", "name": "boolean", "crossLanguageDefinitionId": "TypeSpec.boolean", @@ -4522,13 +4646,13 @@ "isHttpMetadata": false }, { - "$id": "405", + "$id": "415", "kind": "property", "name": "score", "serializedName": "score", "doc": "A float property", "type": { - "$id": "406", + "$id": "416", "kind": "float32", "name": "float32", "crossLanguageDefinitionId": "TypeSpec.float32", @@ -4550,13 +4674,13 @@ "isHttpMetadata": false }, { - "$id": "407", + "$id": "417", "kind": "property", "name": "optionalString", "serializedName": "optionalString", "doc": "An optional string", "type": { - "$id": "408", + "$id": "418", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -4578,13 +4702,13 @@ "isHttpMetadata": false }, { - "$id": "409", + "$id": "419", "kind": "property", "name": "optionalInt", "serializedName": "optionalInt", "doc": "An optional integer", "type": { - "$id": "410", + "$id": "420", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -4606,16 +4730,16 @@ "isHttpMetadata": false }, { - "$id": "411", + "$id": "421", "kind": "property", "name": "nullableString", "serializedName": "nullableString", "doc": "A nullable string", "type": { - "$id": "412", + "$id": "422", "kind": "nullable", "type": { - "$id": "413", + "$id": "423", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -4639,13 +4763,13 @@ "isHttpMetadata": false }, { - "$id": "414", + "$id": "424", "kind": "property", "name": "id", "serializedName": "id", "doc": "A string as XML attribute", "type": { - "$id": "415", + "$id": "425", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -4672,13 +4796,13 @@ "isHttpMetadata": false }, { - "$id": "416", + "$id": "426", "kind": "property", "name": "version", "serializedName": "version", "doc": "An integer as XML attribute", "type": { - "$id": "417", + "$id": "427", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -4705,13 +4829,13 @@ "isHttpMetadata": false }, { - "$id": "418", + "$id": "428", "kind": "property", "name": "isActive", "serializedName": "isActive", "doc": "A boolean as XML attribute", "type": { - "$id": "419", + "$id": "429", "kind": "boolean", "name": "boolean", "crossLanguageDefinitionId": "TypeSpec.boolean", @@ -4738,13 +4862,13 @@ "isHttpMetadata": false }, { - "$id": "420", + "$id": "430", "kind": "property", "name": "originalName", "serializedName": "RenamedProperty", "doc": "A property with a custom XML element name", "type": { - "$id": "421", + "$id": "431", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -4773,13 +4897,13 @@ "isHttpMetadata": false }, { - "$id": "422", + "$id": "432", "kind": "property", "name": "xmlIdentifier", "serializedName": "xml-id", "doc": "An attribute with a custom XML name", "type": { - "$id": "423", + "$id": "433", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -4812,13 +4936,13 @@ "isHttpMetadata": false }, { - "$id": "424", + "$id": "434", "kind": "property", "name": "content", "serializedName": "content", "doc": "Text content in the element (unwrapped string)", "type": { - "$id": "425", + "$id": "435", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -4845,13 +4969,13 @@ "isHttpMetadata": false }, { - "$id": "426", + "$id": "436", "kind": "property", "name": "unwrappedStrings", "serializedName": "unwrappedStrings", "doc": "An unwrapped array of strings - items appear directly without wrapper", "type": { - "$ref": "242" + "$ref": "252" }, "optional": false, "readOnly": false, @@ -4875,13 +4999,13 @@ "isHttpMetadata": false }, { - "$id": "427", + "$id": "437", "kind": "property", "name": "unwrappedCounts", "serializedName": "unwrappedCounts", "doc": "An unwrapped array of integers", "type": { - "$ref": "265" + "$ref": "275" }, "optional": false, "readOnly": false, @@ -4905,17 +5029,17 @@ "isHttpMetadata": false }, { - "$id": "428", + "$id": "438", "kind": "property", "name": "unwrappedItems", "serializedName": "unwrappedItems", "doc": "An unwrapped array of models", "type": { - "$id": "429", + "$id": "439", "kind": "array", "name": "ArrayXmlItem", "valueType": { - "$id": "430", + "$id": "440", "kind": "model", "name": "XmlItem", "namespace": "SampleTypeSpec", @@ -4939,13 +5063,13 @@ }, "properties": [ { - "$id": "431", + "$id": "441", "kind": "property", "name": "itemName", "serializedName": "itemName", "doc": "The item name", "type": { - "$id": "432", + "$id": "442", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -4967,13 +5091,13 @@ "isHttpMetadata": false }, { - "$id": "433", + "$id": "443", "kind": "property", "name": "itemValue", "serializedName": "itemValue", "doc": "The item value", "type": { - "$id": "434", + "$id": "444", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -4995,13 +5119,13 @@ "isHttpMetadata": false }, { - "$id": "435", + "$id": "445", "kind": "property", "name": "itemId", "serializedName": "itemId", "doc": "Item ID as attribute", "type": { - "$id": "436", + "$id": "446", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -5054,13 +5178,13 @@ "isHttpMetadata": false }, { - "$id": "437", + "$id": "447", "kind": "property", "name": "wrappedColors", "serializedName": "wrappedColors", "doc": "A wrapped array of strings (default)", "type": { - "$ref": "242" + "$ref": "252" }, "optional": false, "readOnly": false, @@ -5079,13 +5203,13 @@ "isHttpMetadata": false }, { - "$id": "438", + "$id": "448", "kind": "property", "name": "items", "serializedName": "ItemCollection", "doc": "A wrapped array with custom wrapper name", "type": { - "$ref": "429" + "$ref": "439" }, "optional": false, "readOnly": false, @@ -5111,13 +5235,13 @@ "isHttpMetadata": false }, { - "$id": "439", + "$id": "449", "kind": "property", "name": "nestedModel", "serializedName": "nestedModel", "doc": "A nested model property", "type": { - "$id": "440", + "$id": "450", "kind": "model", "name": "XmlNestedModel", "namespace": "SampleTypeSpec", @@ -5134,13 +5258,13 @@ }, "properties": [ { - "$id": "441", + "$id": "451", "kind": "property", "name": "value", "serializedName": "value", "doc": "The value of the nested model", "type": { - "$id": "442", + "$id": "452", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -5162,13 +5286,13 @@ "isHttpMetadata": false }, { - "$id": "443", + "$id": "453", "kind": "property", "name": "nestedId", "serializedName": "nestedId", "doc": "An attribute on the nested model", "type": { - "$id": "444", + "$id": "454", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -5212,13 +5336,13 @@ "isHttpMetadata": false }, { - "$id": "445", + "$id": "455", "kind": "property", "name": "optionalNestedModel", "serializedName": "optionalNestedModel", "doc": "An optional nested model", "type": { - "$ref": "440" + "$ref": "450" }, "optional": true, "readOnly": false, @@ -5236,23 +5360,23 @@ "isHttpMetadata": false }, { - "$id": "446", + "$id": "456", "kind": "property", "name": "metadata", "serializedName": "metadata", "doc": "A dictionary property", "type": { - "$id": "447", + "$id": "457", "kind": "dict", "keyType": { - "$id": "448", + "$id": "458", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", "decorators": [] }, "valueType": { - "$id": "449", + "$id": "459", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -5276,18 +5400,18 @@ "isHttpMetadata": false }, { - "$id": "450", + "$id": "460", "kind": "property", "name": "createdAt", "serializedName": "createdAt", "doc": "A date-time property", "type": { - "$id": "451", + "$id": "461", "kind": "utcDateTime", "name": "utcDateTime", "encode": "rfc3339", "wireType": { - "$id": "452", + "$id": "462", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -5312,18 +5436,18 @@ "isHttpMetadata": false }, { - "$id": "453", + "$id": "463", "kind": "property", "name": "duration", "serializedName": "duration", "doc": "A duration property", "type": { - "$id": "454", + "$id": "464", "kind": "duration", "name": "duration", "encode": "ISO8601", "wireType": { - "$id": "455", + "$id": "465", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -5348,13 +5472,13 @@ "isHttpMetadata": false }, { - "$id": "456", + "$id": "466", "kind": "property", "name": "data", "serializedName": "data", "doc": "A bytes property", "type": { - "$id": "457", + "$id": "467", "kind": "bytes", "name": "bytes", "encode": "base64", @@ -5377,13 +5501,13 @@ "isHttpMetadata": false }, { - "$id": "458", + "$id": "468", "kind": "property", "name": "optionalRecordUnknown", "serializedName": "optionalRecordUnknown", "doc": "optional record of unknown", "type": { - "$ref": "302" + "$ref": "312" }, "optional": true, "readOnly": false, @@ -5401,7 +5525,7 @@ "isHttpMetadata": false }, { - "$id": "459", + "$id": "469", "kind": "property", "name": "fixedEnum", "serializedName": "fixedEnum", @@ -5425,7 +5549,7 @@ "isHttpMetadata": false }, { - "$id": "460", + "$id": "470", "kind": "property", "name": "extensibleEnum", "serializedName": "extensibleEnum", @@ -5449,7 +5573,7 @@ "isHttpMetadata": false }, { - "$id": "461", + "$id": "471", "kind": "property", "name": "optionalFixedEnum", "serializedName": "optionalFixedEnum", @@ -5473,7 +5597,7 @@ "isHttpMetadata": false }, { - "$id": "462", + "$id": "472", "kind": "property", "name": "optionalExtensibleEnum", "serializedName": "optionalExtensibleEnum", @@ -5497,12 +5621,12 @@ "isHttpMetadata": false }, { - "$id": "463", + "$id": "473", "kind": "property", "name": "label", "serializedName": "label", "type": { - "$id": "464", + "$id": "474", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -5517,13 +5641,13 @@ "name": "TypeSpec.Xml.@ns", "arguments": { "ns": { - "$id": "465", + "$id": "475", "kind": "enumvalue", "decorators": [], "name": "ns1", "value": "https://example.com/ns1", "enumType": { - "$id": "466", + "$id": "476", "kind": "enum", "decorators": [ { @@ -5535,7 +5659,7 @@ "isGeneratedName": false, "namespace": "SampleTypeSpec", "valueType": { - "$id": "467", + "$id": "477", "kind": "string", "decorators": [], "doc": "A sequence of textual characters.", @@ -5544,30 +5668,30 @@ }, "values": [ { - "$id": "468", + "$id": "478", "kind": "enumvalue", "decorators": [], "name": "ns1", "value": "https://example.com/ns1", "enumType": { - "$ref": "466" + "$ref": "476" }, "valueType": { - "$ref": "467" + "$ref": "477" }, "crossLanguageDefinitionId": "SampleTypeSpec.XmlNamespaces.ns1" }, { - "$id": "469", + "$id": "479", "kind": "enumvalue", "decorators": [], "name": "ns2", "value": "https://example.com/ns2", "enumType": { - "$ref": "466" + "$ref": "476" }, "valueType": { - "$ref": "467" + "$ref": "477" }, "crossLanguageDefinitionId": "SampleTypeSpec.XmlNamespaces.ns2" } @@ -5585,7 +5709,7 @@ "__accessSet": true }, "valueType": { - "$ref": "467" + "$ref": "477" }, "crossLanguageDefinitionId": "SampleTypeSpec.XmlNamespaces.ns1" } @@ -5611,12 +5735,12 @@ "isHttpMetadata": false }, { - "$id": "470", + "$id": "480", "kind": "property", "name": "daysUsed", "serializedName": "daysUsed", "type": { - "$id": "471", + "$id": "481", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -5631,16 +5755,16 @@ "name": "TypeSpec.Xml.@ns", "arguments": { "ns": { - "$id": "472", + "$id": "482", "kind": "enumvalue", "decorators": [], "name": "ns2", "value": "https://example.com/ns2", "enumType": { - "$ref": "466" + "$ref": "476" }, "valueType": { - "$ref": "467" + "$ref": "477" }, "crossLanguageDefinitionId": "SampleTypeSpec.XmlNamespaces.ns2" } @@ -5662,12 +5786,12 @@ "isHttpMetadata": false }, { - "$id": "473", + "$id": "483", "kind": "property", "name": "fooItems", "serializedName": "fooItems", "type": { - "$ref": "242" + "$ref": "252" }, "optional": false, "readOnly": false, @@ -5698,12 +5822,12 @@ "isHttpMetadata": false }, { - "$id": "474", + "$id": "484", "kind": "property", "name": "anotherModel", "serializedName": "anotherModel", "type": { - "$ref": "440" + "$ref": "450" }, "optional": false, "readOnly": false, @@ -5733,16 +5857,16 @@ "isHttpMetadata": false }, { - "$id": "475", + "$id": "485", "kind": "property", "name": "modelsWithNamespaces", "serializedName": "modelsWithNamespaces", "type": { - "$id": "476", + "$id": "486", "kind": "array", "name": "ArrayXmlModelWithNamespace", "valueType": { - "$id": "477", + "$id": "487", "kind": "model", "name": "XmlModelWithNamespace", "namespace": "SampleTypeSpec", @@ -5770,12 +5894,12 @@ }, "properties": [ { - "$id": "478", + "$id": "488", "kind": "property", "name": "foo", "serializedName": "foo", "type": { - "$id": "479", + "$id": "489", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -5822,12 +5946,12 @@ "isHttpMetadata": false }, { - "$id": "480", + "$id": "490", "kind": "property", "name": "unwrappedModelsWithNamespaces", "serializedName": "unwrappedModelsWithNamespaces", "type": { - "$ref": "476" + "$ref": "486" }, "optional": false, "readOnly": false, @@ -5851,16 +5975,16 @@ "isHttpMetadata": false }, { - "$id": "481", + "$id": "491", "kind": "property", "name": "listOfListFoo", "serializedName": "listOfListFoo", "type": { - "$id": "482", + "$id": "492", "kind": "array", "name": "ArrayArray1", "valueType": { - "$ref": "429" + "$ref": "439" }, "crossLanguageDefinitionId": "TypeSpec.Array", "decorators": [] @@ -5882,22 +6006,22 @@ "isHttpMetadata": false }, { - "$id": "483", + "$id": "493", "kind": "property", "name": "dictionaryFoo", "serializedName": "dictionaryFoo", "type": { - "$id": "484", + "$id": "494", "kind": "dict", "keyType": { - "$id": "485", + "$id": "495", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", "decorators": [] }, "valueType": { - "$ref": "430" + "$ref": "440" }, "decorators": [] }, @@ -5917,22 +6041,22 @@ "isHttpMetadata": false }, { - "$id": "486", + "$id": "496", "kind": "property", "name": "dictionaryOfDictionaryFoo", "serializedName": "dictionaryOfDictionaryFoo", "type": { - "$id": "487", + "$id": "497", "kind": "dict", "keyType": { - "$id": "488", + "$id": "498", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", "decorators": [] }, "valueType": { - "$ref": "484" + "$ref": "494" }, "decorators": [] }, @@ -5952,22 +6076,22 @@ "isHttpMetadata": false }, { - "$id": "489", + "$id": "499", "kind": "property", "name": "dictionaryListFoo", "serializedName": "dictionaryListFoo", "type": { - "$id": "490", + "$id": "500", "kind": "dict", "keyType": { - "$id": "491", + "$id": "501", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", "decorators": [] }, "valueType": { - "$ref": "429" + "$ref": "439" }, "decorators": [] }, @@ -5987,16 +6111,16 @@ "isHttpMetadata": false }, { - "$id": "492", + "$id": "502", "kind": "property", "name": "listOfDictionaryFoo", "serializedName": "listOfDictionaryFoo", "type": { - "$id": "493", + "$id": "503", "kind": "array", "name": "ArrayRecord1", "valueType": { - "$ref": "484" + "$ref": "494" }, "crossLanguageDefinitionId": "TypeSpec.Array", "decorators": [] @@ -6020,16 +6144,16 @@ ] }, { - "$ref": "430" + "$ref": "440" }, { - "$ref": "440" + "$ref": "450" }, { - "$ref": "477" + "$ref": "487" }, { - "$id": "494", + "$id": "504", "kind": "model", "name": "Animal", "namespace": "SampleTypeSpec", @@ -6043,13 +6167,13 @@ } }, "discriminatorProperty": { - "$id": "495", + "$id": "505", "kind": "property", "name": "kind", "serializedName": "kind", "doc": "The kind of animal", "type": { - "$id": "496", + "$id": "506", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6070,16 +6194,16 @@ }, "properties": [ { - "$ref": "495" + "$ref": "505" }, { - "$id": "497", + "$id": "507", "kind": "property", "name": "name", "serializedName": "name", "doc": "Name of the animal", "type": { - "$id": "498", + "$id": "508", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6101,7 +6225,7 @@ ], "discriminatedSubtypes": { "pet": { - "$id": "499", + "$id": "509", "kind": "model", "name": "Pet", "namespace": "SampleTypeSpec", @@ -6116,12 +6240,12 @@ } }, "discriminatorProperty": { - "$id": "500", + "$id": "510", "kind": "property", "name": "kind", "serializedName": "kind", "type": { - "$ref": "80" + "$ref": "90" }, "optional": false, "readOnly": false, @@ -6137,20 +6261,20 @@ "isHttpMetadata": false }, "baseModel": { - "$ref": "494" + "$ref": "504" }, "properties": [ { - "$ref": "500" + "$ref": "510" }, { - "$id": "501", + "$id": "511", "kind": "property", "name": "trained", "serializedName": "trained", "doc": "Whether the pet is trained", "type": { - "$id": "502", + "$id": "512", "kind": "boolean", "name": "boolean", "crossLanguageDefinitionId": "TypeSpec.boolean", @@ -6172,7 +6296,7 @@ ], "discriminatedSubtypes": { "dog": { - "$id": "503", + "$id": "513", "kind": "model", "name": "Dog", "namespace": "SampleTypeSpec", @@ -6187,16 +6311,16 @@ } }, "baseModel": { - "$ref": "499" + "$ref": "509" }, "properties": [ { - "$id": "504", + "$id": "514", "kind": "property", "name": "kind", "serializedName": "kind", "type": { - "$ref": "82" + "$ref": "92" }, "optional": false, "readOnly": false, @@ -6212,13 +6336,13 @@ "isHttpMetadata": false }, { - "$id": "505", + "$id": "515", "kind": "property", "name": "breed", "serializedName": "breed", "doc": "The breed of the dog", "type": { - "$id": "506", + "$id": "516", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6242,18 +6366,18 @@ } }, "dog": { - "$ref": "503" + "$ref": "513" } } }, { - "$ref": "499" + "$ref": "509" }, { - "$ref": "503" + "$ref": "513" }, { - "$id": "507", + "$id": "517", "kind": "model", "name": "Tree", "namespace": "SampleTypeSpec", @@ -6273,7 +6397,7 @@ } }, "baseModel": { - "$id": "508", + "$id": "518", "kind": "model", "name": "Plant", "namespace": "SampleTypeSpec", @@ -6292,13 +6416,13 @@ } }, "discriminatorProperty": { - "$id": "509", + "$id": "519", "kind": "property", "name": "species", "serializedName": "species", "doc": "The species of plant", "type": { - "$id": "510", + "$id": "520", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6324,16 +6448,16 @@ }, "properties": [ { - "$ref": "509" + "$ref": "519" }, { - "$id": "511", + "$id": "521", "kind": "property", "name": "id", "serializedName": "id", "doc": "The unique identifier of the plant", "type": { - "$id": "512", + "$id": "522", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6358,13 +6482,13 @@ "isHttpMetadata": false }, { - "$id": "513", + "$id": "523", "kind": "property", "name": "height", "serializedName": "height", "doc": "The height of the plant in centimeters", "type": { - "$id": "514", + "$id": "524", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -6391,18 +6515,18 @@ ], "discriminatedSubtypes": { "tree": { - "$ref": "507" + "$ref": "517" } } }, "properties": [ { - "$id": "515", + "$id": "525", "kind": "property", "name": "species", "serializedName": "species", "type": { - "$ref": "84" + "$ref": "94" }, "optional": false, "readOnly": false, @@ -6423,13 +6547,13 @@ "isHttpMetadata": false }, { - "$id": "516", + "$id": "526", "kind": "property", "name": "age", "serializedName": "age", "doc": "The age of the tree in years", "type": { - "$id": "517", + "$id": "527", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -6456,10 +6580,10 @@ ] }, { - "$ref": "508" + "$ref": "518" }, { - "$id": "518", + "$id": "528", "kind": "model", "name": "GetWidgetMetricsResponse", "namespace": "SampleTypeSpec", @@ -6473,12 +6597,12 @@ }, "properties": [ { - "$id": "519", + "$id": "529", "kind": "property", "name": "numSold", "serializedName": "numSold", "type": { - "$id": "520", + "$id": "530", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -6498,12 +6622,12 @@ "isHttpMetadata": false }, { - "$id": "521", + "$id": "531", "kind": "property", "name": "averagePrice", "serializedName": "averagePrice", "type": { - "$id": "522", + "$id": "532", "kind": "float32", "name": "float32", "crossLanguageDefinitionId": "TypeSpec.float32", @@ -6525,7 +6649,7 @@ ] }, { - "$id": "523", + "$id": "533", "kind": "model", "name": "GetNotebookResponse", "namespace": "SampleTypeSpec", @@ -6539,12 +6663,12 @@ }, "properties": [ { - "$id": "524", + "$id": "534", "kind": "property", "name": "name", "serializedName": "name", "type": { - "$id": "525", + "$id": "535", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6564,12 +6688,12 @@ "isHttpMetadata": false }, { - "$id": "526", + "$id": "536", "kind": "property", "name": "content", "serializedName": "content", "type": { - "$id": "527", + "$id": "537", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6593,14 +6717,14 @@ ], "clients": [ { - "$id": "528", + "$id": "538", "kind": "client", "name": "SampleTypeSpecClient", "namespace": "SampleTypeSpec", "doc": "This is a sample typespec project.", "methods": [ { - "$id": "529", + "$id": "539", "kind": "basic", "name": "sayHi", "accessibility": "public", @@ -6610,19 +6734,19 @@ ], "doc": "Return hi", "operation": { - "$id": "530", + "$id": "540", "name": "sayHi", "resourceName": "SampleTypeSpec", "doc": "Return hi", "accessibility": "public", "parameters": [ { - "$id": "531", + "$id": "541", "kind": "header", "name": "headParameter", "serializedName": "head-parameter", "type": { - "$id": "532", + "$id": "542", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6637,12 +6761,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.sayHi.headParameter", "methodParameterSegments": [ { - "$id": "533", + "$id": "543", "kind": "method", "name": "headParameter", "serializedName": "head-parameter", "type": { - "$id": "534", + "$id": "544", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6660,12 +6784,12 @@ ] }, { - "$id": "535", + "$id": "545", "kind": "query", "name": "queryParameter", "serializedName": "queryParameter", "type": { - "$id": "536", + "$id": "546", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6680,12 +6804,12 @@ "readOnly": false, "methodParameterSegments": [ { - "$id": "537", + "$id": "547", "kind": "method", "name": "queryParameter", "serializedName": "queryParameter", "type": { - "$id": "538", + "$id": "548", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6703,12 +6827,12 @@ ] }, { - "$id": "539", + "$id": "549", "kind": "query", "name": "optionalQuery", "serializedName": "optionalQuery", "type": { - "$id": "540", + "$id": "550", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6723,12 +6847,12 @@ "readOnly": false, "methodParameterSegments": [ { - "$id": "541", + "$id": "551", "kind": "method", "name": "optionalQuery", "serializedName": "optionalQuery", "type": { - "$id": "542", + "$id": "552", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6746,12 +6870,12 @@ ] }, { - "$id": "543", + "$id": "553", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "86" + "$ref": "96" }, "isApiVersion": false, "optional": false, @@ -6762,12 +6886,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.sayHi.accept", "methodParameterSegments": [ { - "$id": "544", + "$id": "554", "kind": "method", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "86" + "$ref": "96" }, "location": "Header", "isApiVersion": false, @@ -6787,7 +6911,7 @@ 200 ], "bodyType": { - "$ref": "236" + "$ref": "246" }, "headers": [], "isErrorResponse": false, @@ -6808,21 +6932,21 @@ }, "parameters": [ { - "$ref": "533" + "$ref": "543" }, { - "$ref": "537" + "$ref": "547" }, { - "$ref": "541" + "$ref": "551" }, { - "$ref": "544" + "$ref": "554" } ], "response": { "type": { - "$ref": "236" + "$ref": "246" } }, "isOverride": false, @@ -6831,7 +6955,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.sayHi" }, { - "$id": "545", + "$id": "555", "kind": "basic", "name": "helloAgain", "accessibility": "public", @@ -6841,19 +6965,19 @@ ], "doc": "Return hi again", "operation": { - "$id": "546", + "$id": "556", "name": "helloAgain", "resourceName": "SampleTypeSpec", "doc": "Return hi again", "accessibility": "public", "parameters": [ { - "$id": "547", + "$id": "557", "kind": "header", "name": "p1", "serializedName": "p1", "type": { - "$id": "548", + "$id": "558", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6868,12 +6992,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloAgain.p1", "methodParameterSegments": [ { - "$id": "549", + "$id": "559", "kind": "method", "name": "p1", "serializedName": "p1", "type": { - "$id": "550", + "$id": "560", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6891,12 +7015,12 @@ ] }, { - "$id": "551", + "$id": "561", "kind": "header", "name": "contentType", "serializedName": "Content-Type", "type": { - "$ref": "88" + "$ref": "98" }, "isApiVersion": false, "optional": false, @@ -6907,12 +7031,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloAgain.contentType", "methodParameterSegments": [ { - "$id": "552", + "$id": "562", "kind": "method", "name": "contentType", "serializedName": "Content-Type", "type": { - "$ref": "88" + "$ref": "98" }, "location": "Header", "isApiVersion": false, @@ -6926,12 +7050,12 @@ ] }, { - "$id": "553", + "$id": "563", "kind": "path", "name": "p2", "serializedName": "p2", "type": { - "$id": "554", + "$id": "564", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6949,12 +7073,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloAgain.p2", "methodParameterSegments": [ { - "$id": "555", + "$id": "565", "kind": "method", "name": "p2", "serializedName": "p2", "type": { - "$id": "556", + "$id": "566", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -6972,12 +7096,12 @@ ] }, { - "$id": "557", + "$id": "567", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "92" + "$ref": "102" }, "isApiVersion": false, "optional": false, @@ -6988,12 +7112,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloAgain.accept", "methodParameterSegments": [ { - "$id": "558", + "$id": "568", "kind": "method", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "92" + "$ref": "102" }, "location": "Header", "isApiVersion": false, @@ -7007,12 +7131,12 @@ ] }, { - "$id": "559", + "$id": "569", "kind": "body", "name": "action", "serializedName": "action", "type": { - "$ref": "271" + "$ref": "281" }, "isApiVersion": false, "contentTypes": [ @@ -7026,12 +7150,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloAgain.action", "methodParameterSegments": [ { - "$id": "560", + "$id": "570", "kind": "method", "name": "action", "serializedName": "action", "type": { - "$ref": "271" + "$ref": "281" }, "location": "Body", "isApiVersion": false, @@ -7051,7 +7175,7 @@ 200 ], "bodyType": { - "$ref": "271" + "$ref": "281" }, "headers": [], "isErrorResponse": false, @@ -7075,24 +7199,24 @@ }, "parameters": [ { - "$ref": "549" + "$ref": "559" }, { - "$ref": "560" + "$ref": "570" }, { - "$ref": "552" + "$ref": "562" }, { - "$ref": "555" + "$ref": "565" }, { - "$ref": "558" + "$ref": "568" } ], "response": { "type": { - "$ref": "271" + "$ref": "281" } }, "isOverride": false, @@ -7101,7 +7225,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloAgain" }, { - "$id": "561", + "$id": "571", "kind": "basic", "name": "noContentType", "accessibility": "public", @@ -7111,19 +7235,19 @@ ], "doc": "Return hi again", "operation": { - "$id": "562", + "$id": "572", "name": "noContentType", "resourceName": "SampleTypeSpec", "doc": "Return hi again", "accessibility": "public", "parameters": [ { - "$id": "563", + "$id": "573", "kind": "header", "name": "p1", "serializedName": "p1", "type": { - "$id": "564", + "$id": "574", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -7138,12 +7262,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.noContentType.p1", "methodParameterSegments": [ { - "$id": "565", + "$id": "575", "kind": "method", "name": "info", "serializedName": "info", "type": { - "$ref": "319" + "$ref": "329" }, "location": "", "isApiVersion": false, @@ -7155,13 +7279,13 @@ "decorators": [] }, { - "$id": "566", + "$id": "576", "kind": "method", "name": "p1", "serializedName": "p1", "doc": "header parameter", "type": { - "$ref": "321" + "$ref": "331" }, "location": "", "isApiVersion": false, @@ -7175,12 +7299,12 @@ ] }, { - "$id": "567", + "$id": "577", "kind": "path", "name": "p2", "serializedName": "p2", "type": { - "$id": "568", + "$id": "578", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -7198,16 +7322,16 @@ "crossLanguageDefinitionId": "SampleTypeSpec.noContentType.p2", "methodParameterSegments": [ { - "$ref": "565" + "$ref": "575" }, { - "$id": "569", + "$id": "579", "kind": "method", "name": "p2", "serializedName": "p2", "doc": "path parameter", "type": { - "$ref": "324" + "$ref": "334" }, "location": "", "isApiVersion": false, @@ -7221,13 +7345,13 @@ ] }, { - "$id": "570", + "$id": "580", "kind": "header", "name": "contentType", "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "94" + "$ref": "104" }, "isApiVersion": false, "optional": false, @@ -7238,13 +7362,13 @@ "crossLanguageDefinitionId": "SampleTypeSpec.noContentType.contentType", "methodParameterSegments": [ { - "$id": "571", + "$id": "581", "kind": "method", "name": "contentType", "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "94" + "$ref": "104" }, "location": "Header", "isApiVersion": false, @@ -7258,12 +7382,12 @@ ] }, { - "$id": "572", + "$id": "582", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "96" + "$ref": "106" }, "isApiVersion": false, "optional": false, @@ -7274,12 +7398,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.noContentType.accept", "methodParameterSegments": [ { - "$id": "573", + "$id": "583", "kind": "method", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "96" + "$ref": "106" }, "location": "Header", "isApiVersion": false, @@ -7293,12 +7417,12 @@ ] }, { - "$id": "574", + "$id": "584", "kind": "body", "name": "action", "serializedName": "action", "type": { - "$ref": "271" + "$ref": "281" }, "isApiVersion": false, "contentTypes": [ @@ -7312,16 +7436,16 @@ "crossLanguageDefinitionId": "SampleTypeSpec.noContentType.action", "methodParameterSegments": [ { - "$ref": "565" + "$ref": "575" }, { - "$id": "575", + "$id": "585", "kind": "method", "name": "action", "serializedName": "action", "doc": "body parameter", "type": { - "$ref": "271" + "$ref": "281" }, "location": "", "isApiVersion": false, @@ -7341,7 +7465,7 @@ 200 ], "bodyType": { - "$ref": "271" + "$ref": "281" }, "headers": [], "isErrorResponse": false, @@ -7365,18 +7489,18 @@ }, "parameters": [ { - "$ref": "565" + "$ref": "575" }, { - "$ref": "571" + "$ref": "581" }, { - "$ref": "573" + "$ref": "583" } ], "response": { "type": { - "$ref": "271" + "$ref": "281" } }, "isOverride": true, @@ -7385,7 +7509,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.noContentType" }, { - "$id": "576", + "$id": "586", "kind": "basic", "name": "helloDemo2", "accessibility": "public", @@ -7395,19 +7519,19 @@ ], "doc": "Return hi in demo2", "operation": { - "$id": "577", + "$id": "587", "name": "helloDemo2", "resourceName": "SampleTypeSpec", "doc": "Return hi in demo2", "accessibility": "public", "parameters": [ { - "$id": "578", + "$id": "588", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "98" + "$ref": "108" }, "isApiVersion": false, "optional": false, @@ -7418,12 +7542,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloDemo2.accept", "methodParameterSegments": [ { - "$id": "579", + "$id": "589", "kind": "method", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "98" + "$ref": "108" }, "location": "Header", "isApiVersion": false, @@ -7443,7 +7567,7 @@ 200 ], "bodyType": { - "$ref": "236" + "$ref": "246" }, "headers": [], "isErrorResponse": false, @@ -7464,12 +7588,12 @@ }, "parameters": [ { - "$ref": "579" + "$ref": "589" } ], "response": { "type": { - "$ref": "236" + "$ref": "246" } }, "isOverride": false, @@ -7478,7 +7602,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloDemo2" }, { - "$id": "580", + "$id": "590", "kind": "basic", "name": "createLiteral", "accessibility": "public", @@ -7488,20 +7612,20 @@ ], "doc": "Create with literal value", "operation": { - "$id": "581", + "$id": "591", "name": "createLiteral", "resourceName": "SampleTypeSpec", "doc": "Create with literal value", "accessibility": "public", "parameters": [ { - "$id": "582", + "$id": "592", "kind": "header", "name": "contentType", "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "100" + "$ref": "110" }, "isApiVersion": false, "optional": false, @@ -7512,13 +7636,13 @@ "crossLanguageDefinitionId": "SampleTypeSpec.createLiteral.contentType", "methodParameterSegments": [ { - "$id": "583", + "$id": "593", "kind": "method", "name": "contentType", "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "100" + "$ref": "110" }, "location": "Header", "isApiVersion": false, @@ -7532,12 +7656,12 @@ ] }, { - "$id": "584", + "$id": "594", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "102" + "$ref": "112" }, "isApiVersion": false, "optional": false, @@ -7548,12 +7672,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.createLiteral.accept", "methodParameterSegments": [ { - "$id": "585", + "$id": "595", "kind": "method", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "102" + "$ref": "112" }, "location": "Header", "isApiVersion": false, @@ -7567,12 +7691,12 @@ ] }, { - "$id": "586", + "$id": "596", "kind": "body", "name": "body", "serializedName": "body", "type": { - "$ref": "236" + "$ref": "246" }, "isApiVersion": false, "contentTypes": [ @@ -7586,12 +7710,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.createLiteral.body", "methodParameterSegments": [ { - "$id": "587", + "$id": "597", "kind": "method", "name": "body", "serializedName": "body", "type": { - "$ref": "236" + "$ref": "246" }, "location": "Body", "isApiVersion": false, @@ -7611,7 +7735,7 @@ 200 ], "bodyType": { - "$ref": "236" + "$ref": "246" }, "headers": [], "isErrorResponse": false, @@ -7635,18 +7759,18 @@ }, "parameters": [ { - "$ref": "587" + "$ref": "597" }, { - "$ref": "583" + "$ref": "593" }, { - "$ref": "585" + "$ref": "595" } ], "response": { "type": { - "$ref": "236" + "$ref": "246" } }, "isOverride": false, @@ -7655,7 +7779,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.createLiteral" }, { - "$id": "588", + "$id": "598", "kind": "basic", "name": "helloLiteral", "accessibility": "public", @@ -7665,19 +7789,19 @@ ], "doc": "Send literal parameters", "operation": { - "$id": "589", + "$id": "599", "name": "helloLiteral", "resourceName": "SampleTypeSpec", "doc": "Send literal parameters", "accessibility": "public", "parameters": [ { - "$id": "590", + "$id": "600", "kind": "header", "name": "p1", "serializedName": "p1", "type": { - "$ref": "104" + "$ref": "114" }, "isApiVersion": false, "optional": false, @@ -7688,12 +7812,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloLiteral.p1", "methodParameterSegments": [ { - "$id": "591", + "$id": "601", "kind": "method", "name": "p1", "serializedName": "p1", "type": { - "$ref": "106" + "$ref": "116" }, "location": "Header", "isApiVersion": false, @@ -7707,12 +7831,12 @@ ] }, { - "$id": "592", + "$id": "602", "kind": "path", "name": "p2", "serializedName": "p2", "type": { - "$ref": "108" + "$ref": "118" }, "isApiVersion": false, "explode": false, @@ -7726,12 +7850,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloLiteral.p2", "methodParameterSegments": [ { - "$id": "593", + "$id": "603", "kind": "method", "name": "p2", "serializedName": "p2", "type": { - "$ref": "110" + "$ref": "120" }, "location": "Path", "isApiVersion": false, @@ -7745,12 +7869,12 @@ ] }, { - "$id": "594", + "$id": "604", "kind": "query", "name": "p3", "serializedName": "p3", "type": { - "$ref": "112" + "$ref": "122" }, "isApiVersion": false, "explode": false, @@ -7761,12 +7885,12 @@ "readOnly": false, "methodParameterSegments": [ { - "$id": "595", + "$id": "605", "kind": "method", "name": "p3", "serializedName": "p3", "type": { - "$ref": "114" + "$ref": "124" }, "location": "Query", "isApiVersion": false, @@ -7780,12 +7904,12 @@ ] }, { - "$id": "596", + "$id": "606", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "116" + "$ref": "126" }, "isApiVersion": false, "optional": false, @@ -7796,12 +7920,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloLiteral.accept", "methodParameterSegments": [ { - "$id": "597", + "$id": "607", "kind": "method", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "116" + "$ref": "126" }, "location": "Header", "isApiVersion": false, @@ -7821,7 +7945,7 @@ 200 ], "bodyType": { - "$ref": "236" + "$ref": "246" }, "headers": [], "isErrorResponse": false, @@ -7842,21 +7966,21 @@ }, "parameters": [ { - "$ref": "591" + "$ref": "601" }, { - "$ref": "593" + "$ref": "603" }, { - "$ref": "595" + "$ref": "605" }, { - "$ref": "597" + "$ref": "607" } ], "response": { "type": { - "$ref": "236" + "$ref": "246" } }, "isOverride": false, @@ -7865,7 +7989,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.helloLiteral" }, { - "$id": "598", + "$id": "608", "kind": "basic", "name": "topAction", "accessibility": "public", @@ -7875,24 +7999,24 @@ ], "doc": "top level method", "operation": { - "$id": "599", + "$id": "609", "name": "topAction", "resourceName": "SampleTypeSpec", "doc": "top level method", "accessibility": "public", "parameters": [ { - "$id": "600", + "$id": "610", "kind": "path", "name": "action", "serializedName": "action", "type": { - "$id": "601", + "$id": "611", "kind": "utcDateTime", "name": "utcDateTime", "encode": "rfc3339", "wireType": { - "$id": "602", + "$id": "612", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -7913,17 +8037,17 @@ "crossLanguageDefinitionId": "SampleTypeSpec.topAction.action", "methodParameterSegments": [ { - "$id": "603", + "$id": "613", "kind": "method", "name": "action", "serializedName": "action", "type": { - "$id": "604", + "$id": "614", "kind": "utcDateTime", "name": "utcDateTime", "encode": "rfc3339", "wireType": { - "$id": "605", + "$id": "615", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -7944,12 +8068,12 @@ ] }, { - "$id": "606", + "$id": "616", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "118" + "$ref": "128" }, "isApiVersion": false, "optional": false, @@ -7960,12 +8084,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.topAction.accept", "methodParameterSegments": [ { - "$id": "607", + "$id": "617", "kind": "method", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "118" + "$ref": "128" }, "location": "Header", "isApiVersion": false, @@ -7985,7 +8109,7 @@ 200 ], "bodyType": { - "$ref": "236" + "$ref": "246" }, "headers": [], "isErrorResponse": false, @@ -8006,15 +8130,15 @@ }, "parameters": [ { - "$ref": "603" + "$ref": "613" }, { - "$ref": "607" + "$ref": "617" } ], "response": { "type": { - "$ref": "236" + "$ref": "246" } }, "isOverride": false, @@ -8023,7 +8147,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.topAction" }, { - "$id": "608", + "$id": "618", "kind": "basic", "name": "topAction2", "accessibility": "public", @@ -8033,19 +8157,19 @@ ], "doc": "top level method2", "operation": { - "$id": "609", + "$id": "619", "name": "topAction2", "resourceName": "SampleTypeSpec", "doc": "top level method2", "accessibility": "public", "parameters": [ { - "$id": "610", + "$id": "620", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "120" + "$ref": "130" }, "isApiVersion": false, "optional": false, @@ -8056,12 +8180,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.topAction2.accept", "methodParameterSegments": [ { - "$id": "611", + "$id": "621", "kind": "method", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "120" + "$ref": "130" }, "location": "Header", "isApiVersion": false, @@ -8081,7 +8205,7 @@ 200 ], "bodyType": { - "$ref": "236" + "$ref": "246" }, "headers": [], "isErrorResponse": false, @@ -8102,12 +8226,12 @@ }, "parameters": [ { - "$ref": "611" + "$ref": "621" } ], "response": { "type": { - "$ref": "236" + "$ref": "246" } }, "isOverride": false, @@ -8116,7 +8240,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.topAction2" }, { - "$id": "612", + "$id": "622", "kind": "basic", "name": "patchAction", "accessibility": "public", @@ -8126,20 +8250,20 @@ ], "doc": "top level patch", "operation": { - "$id": "613", + "$id": "623", "name": "patchAction", "resourceName": "SampleTypeSpec", "doc": "top level patch", "accessibility": "public", "parameters": [ { - "$id": "614", + "$id": "624", "kind": "header", "name": "contentType", "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "122" + "$ref": "132" }, "isApiVersion": false, "optional": false, @@ -8150,13 +8274,13 @@ "crossLanguageDefinitionId": "SampleTypeSpec.patchAction.contentType", "methodParameterSegments": [ { - "$id": "615", + "$id": "625", "kind": "method", "name": "contentType", "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "122" + "$ref": "132" }, "location": "Header", "isApiVersion": false, @@ -8170,12 +8294,12 @@ ] }, { - "$id": "616", + "$id": "626", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "124" + "$ref": "134" }, "isApiVersion": false, "optional": false, @@ -8186,12 +8310,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.patchAction.accept", "methodParameterSegments": [ { - "$id": "617", + "$id": "627", "kind": "method", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "124" + "$ref": "134" }, "location": "Header", "isApiVersion": false, @@ -8205,12 +8329,12 @@ ] }, { - "$id": "618", + "$id": "628", "kind": "body", "name": "body", "serializedName": "body", "type": { - "$ref": "236" + "$ref": "246" }, "isApiVersion": false, "contentTypes": [ @@ -8224,12 +8348,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.patchAction.body", "methodParameterSegments": [ { - "$id": "619", + "$id": "629", "kind": "method", "name": "body", "serializedName": "body", "type": { - "$ref": "236" + "$ref": "246" }, "location": "Body", "isApiVersion": false, @@ -8249,7 +8373,7 @@ 200 ], "bodyType": { - "$ref": "236" + "$ref": "246" }, "headers": [], "isErrorResponse": false, @@ -8273,18 +8397,18 @@ }, "parameters": [ { - "$ref": "619" + "$ref": "629" }, { - "$ref": "615" + "$ref": "625" }, { - "$ref": "617" + "$ref": "627" } ], "response": { "type": { - "$ref": "236" + "$ref": "246" } }, "isOverride": false, @@ -8293,7 +8417,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.patchAction" }, { - "$id": "620", + "$id": "630", "kind": "basic", "name": "anonymousBody", "accessibility": "public", @@ -8303,19 +8427,19 @@ ], "doc": "body parameter without body decorator", "operation": { - "$id": "621", + "$id": "631", "name": "anonymousBody", "resourceName": "SampleTypeSpec", "doc": "body parameter without body decorator", "accessibility": "public", "parameters": [ { - "$id": "622", + "$id": "632", "kind": "query", "name": "requiredQueryParam", "serializedName": "requiredQueryParam", "type": { - "$ref": "126" + "$ref": "136" }, "isApiVersion": false, "explode": false, @@ -8326,12 +8450,12 @@ "readOnly": false, "methodParameterSegments": [ { - "$id": "623", + "$id": "633", "kind": "method", "name": "requiredQueryParam", "serializedName": "requiredQueryParam", "type": { - "$ref": "128" + "$ref": "138" }, "location": "Query", "isApiVersion": false, @@ -8345,12 +8469,12 @@ ] }, { - "$id": "624", + "$id": "634", "kind": "header", "name": "requiredHeader", "serializedName": "required-header", "type": { - "$ref": "130" + "$ref": "140" }, "isApiVersion": false, "optional": false, @@ -8361,12 +8485,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.anonymousBody.requiredHeader", "methodParameterSegments": [ { - "$id": "625", + "$id": "635", "kind": "method", "name": "requiredHeader", "serializedName": "required-header", "type": { - "$ref": "132" + "$ref": "142" }, "location": "Header", "isApiVersion": false, @@ -8380,13 +8504,13 @@ ] }, { - "$id": "626", + "$id": "636", "kind": "header", "name": "contentType", "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "134" + "$ref": "144" }, "isApiVersion": false, "optional": false, @@ -8397,13 +8521,13 @@ "crossLanguageDefinitionId": "SampleTypeSpec.anonymousBody.contentType", "methodParameterSegments": [ { - "$id": "627", + "$id": "637", "kind": "method", "name": "contentType", "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "134" + "$ref": "144" }, "location": "Header", "isApiVersion": false, @@ -8417,12 +8541,12 @@ ] }, { - "$id": "628", + "$id": "638", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "136" + "$ref": "146" }, "isApiVersion": false, "optional": false, @@ -8433,12 +8557,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.anonymousBody.accept", "methodParameterSegments": [ { - "$id": "629", + "$id": "639", "kind": "method", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "136" + "$ref": "146" }, "location": "Header", "isApiVersion": false, @@ -8452,12 +8576,12 @@ ] }, { - "$id": "630", + "$id": "640", "kind": "body", "name": "thing", "serializedName": "thing", "type": { - "$ref": "236" + "$ref": "246" }, "isApiVersion": false, "contentTypes": [ @@ -8471,13 +8595,13 @@ "crossLanguageDefinitionId": "SampleTypeSpec.anonymousBody.body", "methodParameterSegments": [ { - "$id": "631", + "$id": "641", "kind": "method", "name": "name", "serializedName": "name", "doc": "name of the Thing", "type": { - "$id": "632", + "$id": "642", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -8501,7 +8625,7 @@ 200 ], "bodyType": { - "$ref": "236" + "$ref": "246" }, "headers": [], "isErrorResponse": false, @@ -8525,16 +8649,16 @@ }, "parameters": [ { - "$ref": "631" + "$ref": "641" }, { - "$id": "633", + "$id": "643", "kind": "method", "name": "requiredUnion", "serializedName": "requiredUnion", "doc": "required Union", "type": { - "$ref": "240" + "$ref": "250" }, "location": "Body", "isApiVersion": false, @@ -8546,13 +8670,13 @@ "decorators": [] }, { - "$id": "634", + "$id": "644", "kind": "method", "name": "requiredLiteralString", "serializedName": "requiredLiteralString", "doc": "required literal string", "type": { - "$ref": "70" + "$ref": "80" }, "location": "Body", "isApiVersion": false, @@ -8564,13 +8688,13 @@ "decorators": [] }, { - "$id": "635", + "$id": "645", "kind": "method", "name": "requiredNullableString", "serializedName": "requiredNullableString", "doc": "required nullable string", "type": { - "$ref": "247" + "$ref": "257" }, "location": "Body", "isApiVersion": false, @@ -8582,13 +8706,13 @@ "decorators": [] }, { - "$id": "636", + "$id": "646", "kind": "method", "name": "optionalNullableString", "serializedName": "optionalNullableString", "doc": "required optional string", "type": { - "$ref": "250" + "$ref": "260" }, "location": "Body", "isApiVersion": false, @@ -8600,13 +8724,13 @@ "decorators": [] }, { - "$id": "637", + "$id": "647", "kind": "method", "name": "requiredLiteralInt", "serializedName": "requiredLiteralInt", "doc": "required literal int", "type": { - "$ref": "72" + "$ref": "82" }, "location": "Body", "isApiVersion": false, @@ -8618,13 +8742,13 @@ "decorators": [] }, { - "$id": "638", + "$id": "648", "kind": "method", "name": "requiredLiteralFloat", "serializedName": "requiredLiteralFloat", "doc": "required literal float", "type": { - "$ref": "74" + "$ref": "84" }, "location": "Body", "isApiVersion": false, @@ -8636,13 +8760,13 @@ "decorators": [] }, { - "$id": "639", + "$id": "649", "kind": "method", "name": "requiredLiteralBool", "serializedName": "requiredLiteralBool", "doc": "required literal bool", "type": { - "$ref": "76" + "$ref": "86" }, "location": "Body", "isApiVersion": false, @@ -8654,18 +8778,18 @@ "decorators": [] }, { - "$id": "640", + "$id": "650", "kind": "method", "name": "optionalLiteralString", "serializedName": "optionalLiteralString", "doc": "optional literal string", "type": { - "$id": "641", + "$id": "651", "kind": "enum", "name": "ThingOptionalLiteralString", "crossLanguageDefinitionId": "", "valueType": { - "$id": "642", + "$id": "652", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -8673,12 +8797,12 @@ }, "values": [ { - "$id": "643", + "$id": "653", "kind": "enumvalue", "name": "reject", "value": "reject", "valueType": { - "$id": "644", + "$id": "654", "kind": "string", "decorators": [], "doc": "A sequence of textual characters.", @@ -8686,7 +8810,7 @@ "crossLanguageDefinitionId": "TypeSpec.string" }, "enumType": { - "$ref": "641" + "$ref": "651" }, "decorators": [] } @@ -8707,13 +8831,13 @@ "decorators": [] }, { - "$id": "645", + "$id": "655", "kind": "method", "name": "requiredNullableLiteralString", "serializedName": "requiredNullableLiteralString", "doc": "required nullable literal string", "type": { - "$ref": "257" + "$ref": "267" }, "location": "Body", "isApiVersion": false, @@ -8725,18 +8849,18 @@ "decorators": [] }, { - "$id": "646", + "$id": "656", "kind": "method", "name": "optionalLiteralInt", "serializedName": "optionalLiteralInt", "doc": "optional literal int", "type": { - "$id": "647", + "$id": "657", "kind": "enum", "name": "ThingOptionalLiteralInt", "crossLanguageDefinitionId": "", "valueType": { - "$id": "648", + "$id": "658", "kind": "int32", "name": "int32", "crossLanguageDefinitionId": "TypeSpec.int32", @@ -8744,12 +8868,12 @@ }, "values": [ { - "$id": "649", + "$id": "659", "kind": "enumvalue", "name": "456", "value": 456, "valueType": { - "$id": "650", + "$id": "660", "kind": "int32", "decorators": [], "doc": "A 32-bit integer. (`-2,147,483,648` to `2,147,483,647`)", @@ -8757,7 +8881,7 @@ "crossLanguageDefinitionId": "TypeSpec.int32" }, "enumType": { - "$ref": "647" + "$ref": "657" }, "decorators": [] } @@ -8778,18 +8902,18 @@ "decorators": [] }, { - "$id": "651", + "$id": "661", "kind": "method", "name": "optionalLiteralFloat", "serializedName": "optionalLiteralFloat", "doc": "optional literal float", "type": { - "$id": "652", + "$id": "662", "kind": "enum", "name": "ThingOptionalLiteralFloat", "crossLanguageDefinitionId": "", "valueType": { - "$id": "653", + "$id": "663", "kind": "float32", "name": "float32", "crossLanguageDefinitionId": "TypeSpec.float32", @@ -8797,12 +8921,12 @@ }, "values": [ { - "$id": "654", + "$id": "664", "kind": "enumvalue", "name": "4.56", "value": 4.56, "valueType": { - "$id": "655", + "$id": "665", "kind": "float32", "decorators": [], "doc": "A 32 bit floating point number. (`±1.5 x 10^−45` to `±3.4 x 10^38`)", @@ -8810,7 +8934,7 @@ "crossLanguageDefinitionId": "TypeSpec.float32" }, "enumType": { - "$ref": "652" + "$ref": "662" }, "decorators": [] } @@ -8831,13 +8955,13 @@ "decorators": [] }, { - "$id": "656", + "$id": "666", "kind": "method", "name": "optionalLiteralBool", "serializedName": "optionalLiteralBool", "doc": "optional literal bool", "type": { - "$ref": "78" + "$ref": "88" }, "location": "Body", "isApiVersion": false, @@ -8849,13 +8973,13 @@ "decorators": [] }, { - "$id": "657", + "$id": "667", "kind": "method", "name": "requiredBadDescription", "serializedName": "requiredBadDescription", "doc": "description with xml <|endoftext|>", "type": { - "$id": "658", + "$id": "668", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -8871,13 +8995,13 @@ "decorators": [] }, { - "$id": "659", + "$id": "669", "kind": "method", "name": "optionalNullableList", "serializedName": "optionalNullableList", "doc": "optional nullable collection", "type": { - "$ref": "264" + "$ref": "274" }, "location": "Body", "isApiVersion": false, @@ -8889,13 +9013,13 @@ "decorators": [] }, { - "$id": "660", + "$id": "670", "kind": "method", "name": "requiredNullableList", "serializedName": "requiredNullableList", "doc": "required nullable collection", "type": { - "$ref": "268" + "$ref": "278" }, "location": "Body", "isApiVersion": false, @@ -8907,13 +9031,13 @@ "decorators": [] }, { - "$id": "661", + "$id": "671", "kind": "method", "name": "propertyWithSpecialDocs", "serializedName": "propertyWithSpecialDocs", "doc": "This tests:\n- Simple bullet point. This bullet point is going to be very long to test how text wrapping is handled in bullet points within documentation comments. It should properly indent the wrapped lines.\n- Another bullet point with **bold text**. This bullet point is also intentionally long to see how the formatting is preserved when the text wraps onto multiple lines in the generated documentation.\n- Third bullet point with *italic text*. Similar to the previous points, this one is extended to ensure that the wrapping and formatting are correctly applied in the output.\n- Complex bullet point with **bold** and *italic* combined. This bullet point combines both bold and italic formatting and is long enough to test the wrapping behavior in such cases.\n- **Bold bullet point**: A bullet point that is entirely bolded. This point is also made lengthy to observe how the bold formatting is maintained across wrapped lines.\n- *Italic bullet point*: A bullet point that is entirely italicized. This final point is extended to verify that italic formatting is correctly applied even when the text spans multiple lines.", "type": { - "$id": "662", + "$id": "672", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -8929,21 +9053,21 @@ "decorators": [] }, { - "$ref": "623" + "$ref": "633" }, { - "$ref": "625" + "$ref": "635" }, { - "$ref": "627" + "$ref": "637" }, { - "$ref": "629" + "$ref": "639" } ], "response": { "type": { - "$ref": "236" + "$ref": "246" } }, "isOverride": false, @@ -8952,7 +9076,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.anonymousBody" }, { - "$id": "663", + "$id": "673", "kind": "basic", "name": "friendlyModel", "accessibility": "public", @@ -8962,20 +9086,20 @@ ], "doc": "Model can have its friendly name", "operation": { - "$id": "664", + "$id": "674", "name": "friendlyModel", "resourceName": "SampleTypeSpec", "doc": "Model can have its friendly name", "accessibility": "public", "parameters": [ { - "$id": "665", + "$id": "675", "kind": "header", "name": "contentType", "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "148" + "$ref": "158" }, "isApiVersion": false, "optional": false, @@ -8986,13 +9110,13 @@ "crossLanguageDefinitionId": "SampleTypeSpec.friendlyModel.contentType", "methodParameterSegments": [ { - "$id": "666", + "$id": "676", "kind": "method", "name": "contentType", "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "148" + "$ref": "158" }, "location": "Header", "isApiVersion": false, @@ -9006,12 +9130,12 @@ ] }, { - "$id": "667", + "$id": "677", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "150" + "$ref": "160" }, "isApiVersion": false, "optional": false, @@ -9022,12 +9146,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.friendlyModel.accept", "methodParameterSegments": [ { - "$id": "668", + "$id": "678", "kind": "method", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "150" + "$ref": "160" }, "location": "Header", "isApiVersion": false, @@ -9041,12 +9165,12 @@ ] }, { - "$id": "669", + "$id": "679", "kind": "body", "name": "friend", "serializedName": "friend", "type": { - "$ref": "325" + "$ref": "335" }, "isApiVersion": false, "contentTypes": [ @@ -9060,13 +9184,13 @@ "crossLanguageDefinitionId": "SampleTypeSpec.friendlyModel.body", "methodParameterSegments": [ { - "$id": "670", + "$id": "680", "kind": "method", "name": "name", "serializedName": "name", "doc": "name of the NotFriend", "type": { - "$id": "671", + "$id": "681", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9090,7 +9214,7 @@ 200 ], "bodyType": { - "$ref": "325" + "$ref": "335" }, "headers": [], "isErrorResponse": false, @@ -9114,18 +9238,18 @@ }, "parameters": [ { - "$ref": "670" + "$ref": "680" }, { - "$ref": "666" + "$ref": "676" }, { - "$ref": "668" + "$ref": "678" } ], "response": { "type": { - "$ref": "325" + "$ref": "335" } }, "isOverride": false, @@ -9134,7 +9258,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.friendlyModel" }, { - "$id": "672", + "$id": "682", "kind": "basic", "name": "addTimeHeader", "accessibility": "public", @@ -9143,23 +9267,23 @@ "2024-08-16-preview" ], "operation": { - "$id": "673", + "$id": "683", "name": "addTimeHeader", "resourceName": "SampleTypeSpec", "accessibility": "public", "parameters": [ { - "$id": "674", + "$id": "684", "kind": "header", "name": "repeatabilityFirstSent", "serializedName": "Repeatability-First-Sent", "type": { - "$id": "675", + "$id": "685", "kind": "utcDateTime", "name": "utcDateTime", "encode": "rfc7231", "wireType": { - "$id": "676", + "$id": "686", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9177,17 +9301,17 @@ "crossLanguageDefinitionId": "SampleTypeSpec.addTimeHeader.repeatabilityFirstSent", "methodParameterSegments": [ { - "$id": "677", + "$id": "687", "kind": "method", "name": "repeatabilityFirstSent", "serializedName": "Repeatability-First-Sent", "type": { - "$id": "678", + "$id": "688", "kind": "utcDateTime", "name": "utcDateTime", "encode": "rfc7231", "wireType": { - "$id": "679", + "$id": "689", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9229,7 +9353,7 @@ }, "parameters": [ { - "$ref": "677" + "$ref": "687" } ], "response": {}, @@ -9239,7 +9363,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.addTimeHeader" }, { - "$id": "680", + "$id": "690", "kind": "basic", "name": "projectedNameModel", "accessibility": "public", @@ -9249,20 +9373,20 @@ ], "doc": "Model can have its projected name", "operation": { - "$id": "681", + "$id": "691", "name": "projectedNameModel", "resourceName": "SampleTypeSpec", "doc": "Model can have its projected name", "accessibility": "public", "parameters": [ { - "$id": "682", + "$id": "692", "kind": "header", "name": "contentType", "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "152" + "$ref": "162" }, "isApiVersion": false, "optional": false, @@ -9273,13 +9397,13 @@ "crossLanguageDefinitionId": "SampleTypeSpec.projectedNameModel.contentType", "methodParameterSegments": [ { - "$id": "683", + "$id": "693", "kind": "method", "name": "contentType", "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "152" + "$ref": "162" }, "location": "Header", "isApiVersion": false, @@ -9293,12 +9417,12 @@ ] }, { - "$id": "684", + "$id": "694", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "154" + "$ref": "164" }, "isApiVersion": false, "optional": false, @@ -9309,12 +9433,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.projectedNameModel.accept", "methodParameterSegments": [ { - "$id": "685", + "$id": "695", "kind": "method", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "154" + "$ref": "164" }, "location": "Header", "isApiVersion": false, @@ -9328,12 +9452,12 @@ ] }, { - "$id": "686", + "$id": "696", "kind": "body", "name": "renamedModel", "serializedName": "renamedModel", "type": { - "$ref": "328" + "$ref": "338" }, "isApiVersion": false, "contentTypes": [ @@ -9347,13 +9471,13 @@ "crossLanguageDefinitionId": "SampleTypeSpec.projectedNameModel.body", "methodParameterSegments": [ { - "$id": "687", + "$id": "697", "kind": "method", "name": "otherName", "serializedName": "otherName", "doc": "name of the ModelWithClientName", "type": { - "$id": "688", + "$id": "698", "kind": "string", "name": "string", "crossLanguageDefinitionId": "TypeSpec.string", @@ -9377,7 +9501,7 @@ 200 ], "bodyType": { - "$ref": "328" + "$ref": "338" }, "headers": [], "isErrorResponse": false, @@ -9401,18 +9525,18 @@ }, "parameters": [ { - "$ref": "687" + "$ref": "697" }, { - "$ref": "683" + "$ref": "693" }, { - "$ref": "685" + "$ref": "695" } ], "response": { "type": { - "$ref": "328" + "$ref": "338" } }, "isOverride": false, @@ -9421,7 +9545,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.projectedNameModel" }, { - "$id": "689", + "$id": "699", "kind": "basic", "name": "returnsAnonymousModel", "accessibility": "public", @@ -9431,19 +9555,19 @@ ], "doc": "return anonymous model", "operation": { - "$id": "690", + "$id": "700", "name": "returnsAnonymousModel", "resourceName": "SampleTypeSpec", "doc": "return anonymous model", "accessibility": "public", "parameters": [ { - "$id": "691", + "$id": "701", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "156" + "$ref": "166" }, "isApiVersion": false, "optional": false, @@ -9454,12 +9578,12 @@ "crossLanguageDefinitionId": "SampleTypeSpec.returnsAnonymousModel.accept", "methodParameterSegments": [ { - "$id": "692", + "$id": "702", "kind": "method", "name": "accept", "serializedName": "Accept", "type": { - "$ref": "156" + "$ref": "166" }, "location": "Header", "isApiVersion": false, @@ -9479,7 +9603,7 @@ 200 ], "bodyType": { - "$ref": "331" + "$ref": "341" }, "headers": [], "isErrorResponse": false, @@ -9500,12 +9624,12 @@ }, "parameters": [ { - "$ref": "692" + "$ref": "702" } ], "response": { "type": { - "$ref": "331" + "$ref": "341" } }, "isOverride": false, @@ -9514,7 +9638,7 @@ "crossLanguageDefinitionId": "SampleTypeSpec.returnsAnonymousModel" }, { - "$id": "693", + "$id": "703", "kind": "basic", "name": "getUnknownValue", "accessibility": "public", @@ -9524,140 +9648,19 @@ ], "doc": "get extensible enum", "operation": { - "$id": "694", + "$id": "704", "name": "getUnknownValue", "resourceName": "SampleTypeSpec", "doc": "get extensible enum", "accessibility": "public", "parameters": [ { - "$id": "695", + "$id": "705", "kind": "header", "name": "accept", "serializedName": "Accept", "type": { - "$id": "696", - "kind": "enum", - "name": "getUnknownValueContentType", - "crossLanguageDefinitionId": "SampleTypeSpec.getUnknownValue.accept", - "valueType": { - "$id": "697", - "kind": "string", - "name": "string", - "crossLanguageDefinitionId": "TypeSpec.string", - "decorators": [] - }, - "values": [ - { - "$id": "698", - "kind": "enumvalue", - "name": "text/plain", - "value": "text/plain", - "valueType": { - "$ref": "697" - }, - "enumType": { - "$ref": "696" - }, - "decorators": [] - }, - { - "$id": "699", - "kind": "enumvalue", - "name": "text/plain", - "value": "text/plain", - "valueType": { - "$ref": "697" - }, - "enumType": { - "$ref": "696" - }, - "decorators": [] - }, - { - "$id": "700", - "kind": "enumvalue", - "name": "text/plain", - "value": "text/plain", - "valueType": { - "$ref": "697" - }, - "enumType": { - "$ref": "696" - }, - "decorators": [] - }, - { - "$id": "701", - "kind": "enumvalue", - "name": "text/plain", - "value": "text/plain", - "valueType": { - "$ref": "697" - }, - "enumType": { - "$ref": "696" - }, - "decorators": [] - }, - { - "$id": "702", - "kind": "enumvalue", - "name": "text/plain", - "value": "text/plain", - "valueType": { - "$ref": "697" - }, - "enumType": { - "$ref": "696" - }, - "decorators": [] - }, - { - "$id": "703", - "kind": "enumvalue", - "name": "text/plain", - "value": "text/plain", - "valueType": { - "$ref": "697" - }, - "enumType": { - "$ref": "696" - }, - "decorators": [] - }, - { - "$id": "704", - "kind": "enumvalue", - "name": "text/plain", - "value": "text/plain", - "valueType": { - "$ref": "697" - }, - "enumType": { - "$ref": "696" - }, - "decorators": [] - }, - { - "$id": "705", - "kind": "enumvalue", - "name": "text/plain", - "value": "text/plain", - "valueType": { - "$ref": "697" - }, - "enumType": { - "$ref": "696" - }, - "decorators": [] - } - ], - "namespace": "", - "isFixed": true, - "isFlags": false, - "usage": "None", - "decorators": [] + "$ref": "70" }, "isApiVersion": false, "optional": false, @@ -9673,7 +9676,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "696" + "$ref": "70" }, "location": "Header", "isApiVersion": false, @@ -9762,7 +9765,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "158" + "$ref": "168" }, "isApiVersion": false, "optional": false, @@ -9779,7 +9782,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "158" + "$ref": "168" }, "location": "Header", "isApiVersion": false, @@ -9798,7 +9801,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "160" + "$ref": "170" }, "isApiVersion": false, "optional": false, @@ -9814,7 +9817,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "160" + "$ref": "170" }, "location": "Header", "isApiVersion": false, @@ -9833,7 +9836,7 @@ "name": "body", "serializedName": "body", "type": { - "$ref": "236" + "$ref": "246" }, "isApiVersion": false, "contentTypes": [ @@ -9852,7 +9855,7 @@ "name": "body", "serializedName": "body", "type": { - "$ref": "236" + "$ref": "246" }, "location": "Body", "isApiVersion": false, @@ -9872,7 +9875,7 @@ 200 ], "bodyType": { - "$ref": "236" + "$ref": "246" }, "headers": [], "isErrorResponse": false, @@ -9907,7 +9910,7 @@ ], "response": { "type": { - "$ref": "236" + "$ref": "246" } }, "isOverride": false, @@ -10227,7 +10230,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "162" + "$ref": "172" }, "isApiVersion": false, "optional": false, @@ -10243,7 +10246,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "162" + "$ref": "172" }, "location": "Header", "isApiVersion": false, @@ -10263,7 +10266,7 @@ 200 ], "bodyType": { - "$ref": "332" + "$ref": "342" }, "headers": [], "isErrorResponse": false, @@ -10289,7 +10292,7 @@ ], "response": { "type": { - "$ref": "334" + "$ref": "344" }, "resultSegments": [ "things" @@ -10335,7 +10338,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "164" + "$ref": "174" }, "isApiVersion": false, "optional": false, @@ -10351,7 +10354,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "164" + "$ref": "174" }, "location": "Header", "isApiVersion": false, @@ -10371,7 +10374,7 @@ 200 ], "bodyType": { - "$ref": "337" + "$ref": "347" }, "headers": [], "isErrorResponse": false, @@ -10397,7 +10400,7 @@ ], "response": { "type": { - "$ref": "334" + "$ref": "344" }, "resultSegments": [ "things" @@ -10486,7 +10489,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "166" + "$ref": "176" }, "isApiVersion": false, "optional": false, @@ -10502,7 +10505,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "166" + "$ref": "176" }, "location": "Header", "isApiVersion": false, @@ -10522,7 +10525,7 @@ 200 ], "bodyType": { - "$ref": "341" + "$ref": "351" }, "headers": [], "isErrorResponse": false, @@ -10551,7 +10554,7 @@ ], "response": { "type": { - "$ref": "334" + "$ref": "344" }, "resultSegments": [ "things" @@ -10643,7 +10646,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "168" + "$ref": "178" }, "isApiVersion": false, "optional": false, @@ -10659,7 +10662,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "168" + "$ref": "178" }, "location": "Header", "isApiVersion": false, @@ -10679,7 +10682,7 @@ 200 ], "bodyType": { - "$ref": "345" + "$ref": "355" }, "headers": [ { @@ -10720,7 +10723,7 @@ ], "response": { "type": { - "$ref": "334" + "$ref": "344" }, "resultSegments": [ "things" @@ -10769,7 +10772,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "170" + "$ref": "180" }, "isApiVersion": false, "optional": false, @@ -10785,7 +10788,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "170" + "$ref": "180" }, "location": "Header", "isApiVersion": false, @@ -10805,7 +10808,7 @@ 200 ], "bodyType": { - "$ref": "347" + "$ref": "357" }, "headers": [], "isErrorResponse": false, @@ -10831,7 +10834,7 @@ ], "response": { "type": { - "$ref": "334" + "$ref": "344" }, "resultSegments": [ "items" @@ -10892,7 +10895,7 @@ "name": "body", "serializedName": "body", "type": { - "$ref": "349" + "$ref": "359" }, "location": "Body", "isApiVersion": false, @@ -10910,7 +10913,7 @@ "serializedName": "requiredHeader", "doc": "required header parameter", "type": { - "$ref": "353" + "$ref": "363" }, "location": "", "isApiVersion": false, @@ -10954,7 +10957,7 @@ "serializedName": "optionalHeader", "doc": "optional header parameter", "type": { - "$ref": "355" + "$ref": "365" }, "location": "", "isApiVersion": false, @@ -10998,7 +11001,7 @@ "serializedName": "requiredQuery", "doc": "required query parameter", "type": { - "$ref": "357" + "$ref": "367" }, "location": "", "isApiVersion": false, @@ -11042,7 +11045,7 @@ "serializedName": "optionalQuery", "doc": "optional query parameter", "type": { - "$ref": "359" + "$ref": "369" }, "location": "", "isApiVersion": false, @@ -11062,7 +11065,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "172" + "$ref": "182" }, "isApiVersion": false, "optional": false, @@ -11079,7 +11082,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "172" + "$ref": "182" }, "location": "Header", "isApiVersion": false, @@ -11098,7 +11101,7 @@ "name": "body", "serializedName": "body", "type": { - "$ref": "349" + "$ref": "359" }, "isApiVersion": false, "contentTypes": [ @@ -11177,7 +11180,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "174" + "$ref": "184" }, "isApiVersion": false, "optional": false, @@ -11194,7 +11197,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "174" + "$ref": "184" }, "location": "Header", "isApiVersion": false, @@ -11213,7 +11216,7 @@ "name": "body", "serializedName": "body", "type": { - "$ref": "360" + "$ref": "370" }, "isApiVersion": false, "contentTypes": [ @@ -11232,7 +11235,7 @@ "name": "body", "serializedName": "body", "type": { - "$ref": "360" + "$ref": "370" }, "location": "Body", "isApiVersion": false, @@ -11305,7 +11308,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "176" + "$ref": "186" }, "isApiVersion": false, "optional": false, @@ -11321,7 +11324,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "176" + "$ref": "186" }, "location": "Header", "isApiVersion": false, @@ -11341,14 +11344,14 @@ 200 ], "bodyType": { - "$ref": "398" + "$ref": "408" }, "headers": [ { "name": "contentType", "nameInResponse": "content-type", "type": { - "$ref": "178" + "$ref": "188" } } ], @@ -11375,7 +11378,7 @@ ], "response": { "type": { - "$ref": "398" + "$ref": "408" } }, "isOverride": false, @@ -11406,7 +11409,7 @@ "name": "contentType", "serializedName": "Content-Type", "type": { - "$ref": "180" + "$ref": "190" }, "isApiVersion": false, "optional": false, @@ -11422,7 +11425,7 @@ "name": "contentType", "serializedName": "Content-Type", "type": { - "$ref": "180" + "$ref": "190" }, "location": "Header", "isApiVersion": false, @@ -11441,7 +11444,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "184" + "$ref": "194" }, "isApiVersion": false, "optional": false, @@ -11457,7 +11460,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "184" + "$ref": "194" }, "location": "Header", "isApiVersion": false, @@ -11476,7 +11479,7 @@ "name": "body", "serializedName": "body", "type": { - "$ref": "398" + "$ref": "408" }, "isApiVersion": false, "contentTypes": [ @@ -11495,7 +11498,7 @@ "name": "body", "serializedName": "body", "type": { - "$ref": "398" + "$ref": "408" }, "location": "Body", "isApiVersion": false, @@ -11515,14 +11518,14 @@ 200 ], "bodyType": { - "$ref": "398" + "$ref": "408" }, "headers": [ { "name": "contentType", "nameInResponse": "content-type", "type": { - "$ref": "186" + "$ref": "196" } } ], @@ -11558,7 +11561,7 @@ ], "response": { "type": { - "$ref": "398" + "$ref": "408" } }, "isOverride": false, @@ -11630,7 +11633,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "188" + "$ref": "198" }, "isApiVersion": false, "optional": false, @@ -11647,7 +11650,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "188" + "$ref": "198" }, "location": "Header", "isApiVersion": false, @@ -11666,7 +11669,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "190" + "$ref": "200" }, "isApiVersion": false, "optional": false, @@ -11682,7 +11685,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "190" + "$ref": "200" }, "location": "Header", "isApiVersion": false, @@ -11701,7 +11704,7 @@ "name": "animal", "serializedName": "animal", "type": { - "$ref": "494" + "$ref": "504" }, "isApiVersion": false, "contentTypes": [ @@ -11720,7 +11723,7 @@ "name": "animal", "serializedName": "animal", "type": { - "$ref": "494" + "$ref": "504" }, "location": "Body", "isApiVersion": false, @@ -11740,7 +11743,7 @@ 200 ], "bodyType": { - "$ref": "494" + "$ref": "504" }, "headers": [], "isErrorResponse": false, @@ -11775,7 +11778,7 @@ ], "response": { "type": { - "$ref": "494" + "$ref": "504" } }, "isOverride": false, @@ -11807,7 +11810,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "192" + "$ref": "202" }, "isApiVersion": false, "optional": false, @@ -11824,7 +11827,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "192" + "$ref": "202" }, "location": "Header", "isApiVersion": false, @@ -11843,7 +11846,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "194" + "$ref": "204" }, "isApiVersion": false, "optional": false, @@ -11859,7 +11862,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "194" + "$ref": "204" }, "location": "Header", "isApiVersion": false, @@ -11878,7 +11881,7 @@ "name": "animal", "serializedName": "animal", "type": { - "$ref": "494" + "$ref": "504" }, "isApiVersion": false, "contentTypes": [ @@ -11897,7 +11900,7 @@ "name": "animal", "serializedName": "animal", "type": { - "$ref": "494" + "$ref": "504" }, "location": "Body", "isApiVersion": false, @@ -11917,7 +11920,7 @@ 200 ], "bodyType": { - "$ref": "494" + "$ref": "504" }, "headers": [], "isErrorResponse": false, @@ -11952,7 +11955,7 @@ ], "response": { "type": { - "$ref": "494" + "$ref": "504" } }, "isOverride": false, @@ -11991,7 +11994,7 @@ "2024-08-16-preview" ], "parent": { - "$ref": "528" + "$ref": "538" }, "isMultiServiceClient": false }, @@ -12025,7 +12028,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "196" + "$ref": "206" }, "isApiVersion": false, "optional": false, @@ -12042,7 +12045,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "196" + "$ref": "206" }, "location": "Header", "isApiVersion": false, @@ -12061,7 +12064,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "198" + "$ref": "208" }, "isApiVersion": false, "optional": false, @@ -12077,7 +12080,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "198" + "$ref": "208" }, "location": "Header", "isApiVersion": false, @@ -12096,7 +12099,7 @@ "name": "pet", "serializedName": "pet", "type": { - "$ref": "499" + "$ref": "509" }, "isApiVersion": false, "contentTypes": [ @@ -12115,7 +12118,7 @@ "name": "pet", "serializedName": "pet", "type": { - "$ref": "499" + "$ref": "509" }, "location": "Body", "isApiVersion": false, @@ -12135,7 +12138,7 @@ 200 ], "bodyType": { - "$ref": "499" + "$ref": "509" }, "headers": [], "isErrorResponse": false, @@ -12170,7 +12173,7 @@ ], "response": { "type": { - "$ref": "499" + "$ref": "509" } }, "isOverride": false, @@ -12202,7 +12205,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "200" + "$ref": "210" }, "isApiVersion": false, "optional": false, @@ -12219,7 +12222,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "200" + "$ref": "210" }, "location": "Header", "isApiVersion": false, @@ -12238,7 +12241,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "202" + "$ref": "212" }, "isApiVersion": false, "optional": false, @@ -12254,7 +12257,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "202" + "$ref": "212" }, "location": "Header", "isApiVersion": false, @@ -12273,7 +12276,7 @@ "name": "pet", "serializedName": "pet", "type": { - "$ref": "499" + "$ref": "509" }, "isApiVersion": false, "contentTypes": [ @@ -12292,7 +12295,7 @@ "name": "pet", "serializedName": "pet", "type": { - "$ref": "499" + "$ref": "509" }, "location": "Body", "isApiVersion": false, @@ -12312,7 +12315,7 @@ 200 ], "bodyType": { - "$ref": "499" + "$ref": "509" }, "headers": [], "isErrorResponse": false, @@ -12347,7 +12350,7 @@ ], "response": { "type": { - "$ref": "499" + "$ref": "509" } }, "isOverride": false, @@ -12386,7 +12389,7 @@ "2024-08-16-preview" ], "parent": { - "$ref": "528" + "$ref": "538" }, "isMultiServiceClient": false }, @@ -12420,7 +12423,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "204" + "$ref": "214" }, "isApiVersion": false, "optional": false, @@ -12437,7 +12440,7 @@ "serializedName": "Content-Type", "doc": "Body parameter's content type. Known values are application/json", "type": { - "$ref": "204" + "$ref": "214" }, "location": "Header", "isApiVersion": false, @@ -12456,7 +12459,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "206" + "$ref": "216" }, "isApiVersion": false, "optional": false, @@ -12472,7 +12475,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "206" + "$ref": "216" }, "location": "Header", "isApiVersion": false, @@ -12491,7 +12494,7 @@ "name": "dog", "serializedName": "dog", "type": { - "$ref": "503" + "$ref": "513" }, "isApiVersion": false, "contentTypes": [ @@ -12510,7 +12513,7 @@ "name": "dog", "serializedName": "dog", "type": { - "$ref": "503" + "$ref": "513" }, "location": "Body", "isApiVersion": false, @@ -12530,7 +12533,7 @@ 200 ], "bodyType": { - "$ref": "503" + "$ref": "513" }, "headers": [], "isErrorResponse": false, @@ -12565,7 +12568,7 @@ ], "response": { "type": { - "$ref": "503" + "$ref": "513" } }, "isOverride": false, @@ -12604,7 +12607,7 @@ "2024-08-16-preview" ], "parent": { - "$ref": "528" + "$ref": "538" }, "isMultiServiceClient": false }, @@ -12637,7 +12640,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "208" + "$ref": "218" }, "isApiVersion": false, "optional": false, @@ -12653,7 +12656,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "208" + "$ref": "218" }, "location": "Header", "isApiVersion": false, @@ -12673,14 +12676,14 @@ 200 ], "bodyType": { - "$ref": "507" + "$ref": "517" }, "headers": [ { "name": "contentType", "nameInResponse": "content-type", "type": { - "$ref": "210" + "$ref": "220" } } ], @@ -12707,7 +12710,7 @@ ], "response": { "type": { - "$ref": "507" + "$ref": "517" } }, "isOverride": false, @@ -12738,7 +12741,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "212" + "$ref": "222" }, "isApiVersion": false, "optional": false, @@ -12754,7 +12757,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "212" + "$ref": "222" }, "location": "Header", "isApiVersion": false, @@ -12774,14 +12777,14 @@ 200 ], "bodyType": { - "$ref": "507" + "$ref": "517" }, "headers": [ { "name": "contentType", "nameInResponse": "content-type", "type": { - "$ref": "214" + "$ref": "224" } } ], @@ -12808,7 +12811,7 @@ ], "response": { "type": { - "$ref": "507" + "$ref": "517" } }, "isOverride": false, @@ -12839,7 +12842,7 @@ "name": "contentType", "serializedName": "Content-Type", "type": { - "$ref": "216" + "$ref": "226" }, "isApiVersion": false, "optional": false, @@ -12855,7 +12858,7 @@ "name": "contentType", "serializedName": "Content-Type", "type": { - "$ref": "216" + "$ref": "226" }, "location": "Header", "isApiVersion": false, @@ -12874,7 +12877,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "220" + "$ref": "230" }, "isApiVersion": false, "optional": false, @@ -12890,7 +12893,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "220" + "$ref": "230" }, "location": "Header", "isApiVersion": false, @@ -12909,7 +12912,7 @@ "name": "tree", "serializedName": "tree", "type": { - "$ref": "507" + "$ref": "517" }, "isApiVersion": false, "contentTypes": [ @@ -12928,7 +12931,7 @@ "name": "tree", "serializedName": "tree", "type": { - "$ref": "507" + "$ref": "517" }, "location": "Body", "isApiVersion": false, @@ -12948,14 +12951,14 @@ 200 ], "bodyType": { - "$ref": "507" + "$ref": "517" }, "headers": [ { "name": "contentType", "nameInResponse": "content-type", "type": { - "$ref": "222" + "$ref": "232" } } ], @@ -12991,7 +12994,7 @@ ], "response": { "type": { - "$ref": "507" + "$ref": "517" } }, "isOverride": false, @@ -13022,7 +13025,7 @@ "name": "contentType", "serializedName": "Content-Type", "type": { - "$ref": "224" + "$ref": "234" }, "isApiVersion": false, "optional": false, @@ -13038,7 +13041,7 @@ "name": "contentType", "serializedName": "Content-Type", "type": { - "$ref": "224" + "$ref": "234" }, "location": "Header", "isApiVersion": false, @@ -13057,7 +13060,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "228" + "$ref": "238" }, "isApiVersion": false, "optional": false, @@ -13073,7 +13076,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "228" + "$ref": "238" }, "location": "Header", "isApiVersion": false, @@ -13092,7 +13095,7 @@ "name": "tree", "serializedName": "tree", "type": { - "$ref": "507" + "$ref": "517" }, "isApiVersion": false, "contentTypes": [ @@ -13111,7 +13114,7 @@ "name": "tree", "serializedName": "tree", "type": { - "$ref": "507" + "$ref": "517" }, "location": "Body", "isApiVersion": false, @@ -13131,14 +13134,14 @@ 200 ], "bodyType": { - "$ref": "507" + "$ref": "517" }, "headers": [ { "name": "contentType", "nameInResponse": "content-type", "type": { - "$ref": "230" + "$ref": "240" } } ], @@ -13174,7 +13177,7 @@ ], "response": { "type": { - "$ref": "507" + "$ref": "517" } }, "isOverride": false, @@ -13213,7 +13216,7 @@ "2024-08-16-preview" ], "parent": { - "$ref": "528" + "$ref": "538" }, "isMultiServiceClient": false }, @@ -13330,7 +13333,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "232" + "$ref": "242" }, "isApiVersion": false, "optional": false, @@ -13346,7 +13349,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "232" + "$ref": "242" }, "location": "Header", "isApiVersion": false, @@ -13366,7 +13369,7 @@ 200 ], "bodyType": { - "$ref": "518" + "$ref": "528" }, "headers": [], "isErrorResponse": false, @@ -13395,7 +13398,7 @@ ], "response": { "type": { - "$ref": "518" + "$ref": "528" } }, "isOverride": false, @@ -13437,7 +13440,7 @@ "2024-08-16-preview" ], "parent": { - "$ref": "528" + "$ref": "538" }, "isMultiServiceClient": false }, @@ -13517,7 +13520,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "234" + "$ref": "244" }, "isApiVersion": false, "optional": false, @@ -13533,7 +13536,7 @@ "name": "accept", "serializedName": "Accept", "type": { - "$ref": "234" + "$ref": "244" }, "location": "Header", "isApiVersion": false, @@ -13553,7 +13556,7 @@ 200 ], "bodyType": { - "$ref": "523" + "$ref": "533" }, "headers": [], "isErrorResponse": false, @@ -13579,7 +13582,7 @@ ], "response": { "type": { - "$ref": "523" + "$ref": "533" } }, "isOverride": false, @@ -13621,7 +13624,7 @@ "2024-08-16-preview" ], "parent": { - "$ref": "528" + "$ref": "538" }, "isMultiServiceClient": false } From 2ffa43bdcf46bba4931c2c373183b4f4d2760d05 Mon Sep 17 00:00:00 2001 From: Linh Phan Date: Fri, 1 May 2026 15:16:25 -0700 Subject: [PATCH 03/10] add test for same enum in different namespaces --- .../test/Unit/operation-paging.test.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts index 495c724bbae..fd2402b952d 100644 --- a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts @@ -355,6 +355,45 @@ describe("Next link operations", () => { `Expected model 'Foo' to be present in code model models. Found: ${root.models.map((m) => m.name).join(", ")}`, ); }); + + it("does not deduplicate enums with the same name but different namespaces", async () => { + const program = await typeSpecCompile( + ` + @convenientAPI(false) + @list + op link(): { + @pageItems + items: Foo[]; + + @nextLink + next?: url; + }; + enum Status { active, inactive }; + namespace Sub { + enum Status { open, closed }; + } + model Foo { + name: string; + a: Status; + b: Sub.Status; + }; + `, + runner, + { IsTCGCNeeded: true }, + ); + const context = createEmitterContext(program); + const sdkContext = await createCSharpSdkContext(context); + const [root] = createModel(sdkContext); + + // Two enums share the name "Status" but live in different namespaces. + // The namespace-aware dedup key must keep both in the code model. + const statusEnums = root.enums.filter((e) => e.name === "Status"); + strictEqual( + statusEnums.length, + 2, + `Expected 2 'Status' enums from different namespaces. Found ${statusEnums.length}: ${root.enums.map((e) => `${e.namespace}.${e.name}`).join(", ")}`, + ); + }); }); describe("Continuation token operations", () => { From dfbf3b09215d0b4d410a7001cdf873b70d858fec Mon Sep 17 00:00:00 2001 From: Linh Phan Date: Fri, 1 May 2026 15:25:39 -0700 Subject: [PATCH 04/10] Add tests for namespace aware enums/models --- .../test/Unit/operation-paging.test.ts | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts index fd2402b952d..7c2060148ae 100644 --- a/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/operation-paging.test.ts @@ -386,7 +386,69 @@ describe("Next link operations", () => { const [root] = createModel(sdkContext); // Two enums share the name "Status" but live in different namespaces. - // The namespace-aware dedup key must keep both in the code model. + // The namespace-aware key must keep both in the code model. + const statusEnums = root.enums.filter((e) => e.name === "Status"); + strictEqual( + statusEnums.length, + 2, + `Expected 2 'Status' enums from different namespaces. Found ${statusEnums.length}: ${root.enums.map((e) => `${e.namespace}.${e.name}`).join(", ")}`, + ); + }); + + it("preserves same-named models from different namespaces across two paging operations", async () => { + const program = await typeSpecCompile( + ` + namespace NsA { + enum Status { active, inactive }; + model Item { + name: string; + status: Status; + }; + } + namespace NsB { + enum Status { open, closed }; + model Item { + id: int32; + status: Status; + }; + } + + @convenientAPI(false) + @list + @route("/a") + op listA(): { + @pageItems + items: NsA.Item[]; + @nextLink + next?: url; + }; + + @convenientAPI(false) + @list + @route("/b") + op listB(): { + @pageItems + items: NsB.Item[]; + @nextLink + next?: url; + }; + `, + runner, + { IsTCGCNeeded: true }, + ); + const context = createEmitterContext(program); + const sdkContext = await createCSharpSdkContext(context); + const [root] = createModel(sdkContext); + + // Two paging operations reference models named "Item" and enums named + // "Status" from different namespaces. All four types must be present. + const itemModels = root.models.filter((m) => m.name === "Item"); + strictEqual( + itemModels.length, + 2, + `Expected 2 'Item' models from different namespaces. Found ${itemModels.length}: ${root.models.map((m) => `${m.namespace}.${m.name}`).join(", ")}`, + ); + const statusEnums = root.enums.filter((e) => e.name === "Status"); strictEqual( statusEnums.length, From 2dd230bb224dc39a04c286562d885920f4145abc Mon Sep 17 00:00:00 2001 From: Linh Phan Date: Fri, 1 May 2026 15:32:17 -0700 Subject: [PATCH 05/10] make key crossLanguageDefinitionId and fall back on namespace.name if empty --- .../emitter/src/lib/client-model-builder.ts | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts index 45cb32627a6..82b21899fdc 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts @@ -58,7 +58,7 @@ export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, reado if (type.kind === "model") { const model = type as InputModelType; models.push(model); - existingModelKeys.add(`${model.namespace}.${model.name}`); + existingModelKeys.add(typeDedupeKey(model)); } else if (type.kind === "enum") { enums.push(type as InputEnumType); } @@ -67,20 +67,22 @@ export function createModel(sdkContext: CSharpEmitterContext): [CodeModel, reado // response models for protocol-only paging operations where TCGC does not include the // response model in sdkPackage.models, or enums only reachable through nested property // types of such models). See https://github.com/microsoft/typespec/issues/9391. Dedupe - // by namespace + name to avoid duplicates when TCGC produces a different reference for - // the same type, while still allowing distinct types that share a name across namespaces. - const existingEnumKeys = new Set(enums.map((e) => `${e.namespace}.${e.name}`)); + // by crossLanguageDefinitionId when available, falling back to namespace + name for + // anonymous types (empty crossLanguageDefinitionId). This avoids duplicates when TCGC + // produces a different reference for the same logical type, while still preserving + // distinct types that share a name across different namespaces. + const existingEnumKeys = new Set(enums.map((e) => typeDedupeKey(e))); for (const type of sdkContext.__typeCache.types.values()) { if (typesBeforeClients.has(type)) continue; if (type.kind === "model") { const model = type as InputModelType; - const key = `${model.namespace}.${model.name}`; + const key = typeDedupeKey(model); if (existingModelKeys.has(key)) continue; models.push(model); existingModelKeys.add(key); } else if (type.kind === "enum") { const enumType = type as InputEnumType; - const key = `${enumType.namespace}.${enumType.name}`; + const key = typeDedupeKey(enumType); if (existingEnumKeys.has(key)) continue; enums.push(enumType); existingEnumKeys.add(key); @@ -187,6 +189,15 @@ function fixNamingConflicts(models: InputModelType[], constants: InputLiteralTyp } } +/** + * Returns a dedup key for a model or enum type. Prefers `crossLanguageDefinitionId` + * because it is the canonical identity TCGC assigns. Falls back to `namespace.name` + * for anonymous/constant-derived types whose `crossLanguageDefinitionId` is empty. + */ +function typeDedupeKey(type: InputModelType | InputEnumType): string { + return type.crossLanguageDefinitionId || `${type.namespace}.${type.name}`; +} + function navigateModels(sdkContext: CSharpEmitterContext): [void, readonly Diagnostic[]] { const diagnostics = createDiagnosticCollector(); for (const m of sdkContext.sdkPackage.models) { From e11ad58d745ae22b973aa90786561377af888d79 Mon Sep 17 00:00:00 2001 From: Linh Phan Date: Fri, 1 May 2026 15:36:04 -0700 Subject: [PATCH 06/10] spell check fix --- .../http-client-csharp/emitter/src/lib/client-model-builder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts index 82b21899fdc..14809260018 100644 --- a/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts +++ b/packages/http-client-csharp/emitter/src/lib/client-model-builder.ts @@ -190,7 +190,7 @@ function fixNamingConflicts(models: InputModelType[], constants: InputLiteralTyp } /** - * Returns a dedup key for a model or enum type. Prefers `crossLanguageDefinitionId` + * Returns a key for a model or enum type. Prefers `crossLanguageDefinitionId` * because it is the canonical identity TCGC assigns. Falls back to `namespace.name` * for anonymous/constant-derived types whose `crossLanguageDefinitionId` is empty. */ From 32457f0c03be7e1fb8045eb1b94a900769915421 Mon Sep 17 00:00:00 2001 From: Linh Phan Date: Tue, 12 May 2026 08:25:06 -0700 Subject: [PATCH 07/10] OperationSample --- .../Samples/ExampleParameterValue.cs | 55 ++ .../Samples/ExampleValueExpressionBuilder.cs | 314 ++++++++ .../src/Providers/Samples/OperationSample.cs | 708 ++++++++++++++++++ .../ExampleValueExpressionBuilderTests.cs | 624 +++++++++++++++ .../Providers/Samples/OperationSampleTests.cs | 368 +++++++++ .../src/Properties/AssemblyInfo.cs | 2 + 6 files changed, 2071 insertions(+) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleParameterValue.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleValueExpressionBuilder.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/ExampleValueExpressionBuilderTests.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleParameterValue.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleParameterValue.cs new file mode 100644 index 00000000000..5ff6aa62201 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleParameterValue.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.TypeSpec.Generator.Expressions; +using Microsoft.TypeSpec.Generator.Input; +using Microsoft.TypeSpec.Generator.Primitives; + +namespace Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples +{ + /// + /// Represents a parameter value in a sample. Supports two modes: + /// - : raw example data that will be converted to a C# expression later + /// - : a pre-built C# expression (used for known parameters like credentials, endpoints) + /// + public class ExampleParameterValue + { + public ExampleParameterValue(string name, CSharpType type, InputExampleValue value) + { + Name = name; + Type = type; + Value = value; + } + + public ExampleParameterValue(string name, CSharpType type, ValueExpression expression) + { + Name = name; + Type = type; + Expression = expression; + } + + /// + /// The parameter name. + /// + public string Name { get; } + + /// + /// The C# type of the parameter. + /// + public CSharpType Type { get; } + + /// + /// Raw example data from the spec or mock builder. Will be converted to a + /// via . + /// Mutually exclusive with . + /// + public InputExampleValue? Value { get; } + + /// + /// A pre-built C# expression. Used for known parameters (credentials, endpoints) + /// where the expression is fixed regardless of example data. + /// Mutually exclusive with . + /// + public ValueExpression? Expression { get; } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleValueExpressionBuilder.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleValueExpressionBuilder.cs new file mode 100644 index 00000000000..0b37e91f330 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleValueExpressionBuilder.cs @@ -0,0 +1,314 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using Microsoft.TypeSpec.Generator.Expressions; +using Microsoft.TypeSpec.Generator.Input; +using Microsoft.TypeSpec.Generator.Primitives; +using static Microsoft.TypeSpec.Generator.Snippets.Snippet; + +namespace Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples +{ + /// + /// Converts + into (C# AST nodes). + /// This is the bridge between raw mock/spec example data and generated C# code. + /// + public static class ExampleValueExpressionBuilder + { + /// + /// Converts an to a C# expression. + /// If the parameter has a pre-built expression, returns it directly. + /// Otherwise, converts the raw using type information. + /// + public static ValueExpression GetExpression(ExampleParameterValue parameterValue) + { + if (parameterValue.Expression != null) + return parameterValue.Expression; + + if (parameterValue.Value != null) + return GetExpression(parameterValue.Type, parameterValue.Value); + + return Default; + } + + /// + /// Converts an to a C# expression based on the target . + /// + public static ValueExpression GetExpression(CSharpType type, InputExampleValue exampleValue) + { + if (type.IsList) + return GetExpressionForList(type, exampleValue); + if (type.IsDictionary) + return GetExpressionForDictionary(type, exampleValue); + if (type.IsEnum) + return GetExpressionForEnum(type, exampleValue); + if (type is { IsFrameworkType: true }) + return GetExpressionForFrameworkType(type.FrameworkType, exampleValue); + + // For model types, fall back to default + return GetExpressionForModel(type, exampleValue); + } + + private static ValueExpression GetExpressionForFrameworkType(Type frameworkType, InputExampleValue exampleValue) + { + var rawValue = GetRawValue(exampleValue); + + // String + if (frameworkType == typeof(string)) + { + return rawValue is string s ? Literal(s) : Null; + } + + // Boolean + if (frameworkType == typeof(bool)) + { + return rawValue is bool b ? Literal(b) : Default; + } + + // Integer types + if (frameworkType == typeof(int)) + { + return rawValue != null ? Literal(Convert.ToInt32(rawValue)) : Default; + } + if (frameworkType == typeof(long)) + { + return rawValue != null ? Literal(Convert.ToInt64(rawValue)) : Default; + } + if (frameworkType == typeof(short)) + { + return rawValue != null ? new CastExpression(Literal(Convert.ToInt16(rawValue)), frameworkType) : Default; + } + if (frameworkType == typeof(sbyte)) + { + return rawValue != null ? new CastExpression(Literal(Convert.ToSByte(rawValue)), frameworkType) : Default; + } + if (frameworkType == typeof(byte)) + { + return rawValue != null ? new CastExpression(Literal(Convert.ToByte(rawValue)), frameworkType) : Default; + } + if (frameworkType == typeof(ushort)) + { + return rawValue != null ? new CastExpression(Literal(Convert.ToUInt16(rawValue)), frameworkType) : Default; + } + if (frameworkType == typeof(uint)) + { + return rawValue != null ? new CastExpression(Literal(Convert.ToUInt32(rawValue)), frameworkType) : Default; + } + if (frameworkType == typeof(ulong)) + { + return rawValue != null ? new CastExpression(Literal(Convert.ToUInt64(rawValue)), frameworkType) : Default; + } + + // Float types + if (frameworkType == typeof(float)) + { + return rawValue != null ? Literal(Convert.ToSingle(rawValue)) : Default; + } + if (frameworkType == typeof(double)) + { + return rawValue != null ? Literal(Convert.ToDouble(rawValue)) : Default; + } + if (frameworkType == typeof(decimal)) + { + return rawValue != null ? Literal(Convert.ToDecimal(rawValue)) : Default; + } + + // Guid + if (frameworkType == typeof(Guid)) + { + if (rawValue is string s) + return Static(typeof(Guid)).Invoke(nameof(Guid.Parse), Literal(s)); + return Default; + } + + // Uri + if (frameworkType == typeof(Uri)) + { + if (rawValue is string s) + return New.Instance(typeof(Uri), Literal(s)); + return Null; + } + + // DateTimeOffset + if (frameworkType == typeof(DateTimeOffset)) + { + if (rawValue is string s) + return Static(typeof(DateTimeOffset)).Invoke(nameof(DateTimeOffset.Parse), Literal(s)); + if (rawValue is int or long) + return Static(typeof(DateTimeOffset)).Invoke(nameof(DateTimeOffset.FromUnixTimeSeconds), Literal(Convert.ToInt64(rawValue))); + return Default; + } + + // TimeSpan + if (frameworkType == typeof(TimeSpan)) + { + if (rawValue is string s) + return Static(typeof(XmlConvert)).Invoke(nameof(XmlConvert.ToTimeSpan), Literal(s)); + if (rawValue is int or float or double) + return Static(typeof(TimeSpan)).Invoke(nameof(TimeSpan.FromSeconds), Literal(Convert.ToDouble(rawValue))); + return Default; + } + + // BinaryData + if (frameworkType == typeof(BinaryData)) + { + if (rawValue == null && exampleValue is not InputExampleValue) + return Null; + return GetExpressionForBinaryData(exampleValue); + } + + // byte[] + if (frameworkType == typeof(byte[])) + { + if (rawValue is string s) + return Static(typeof(Encoding)).Property(nameof(Encoding.UTF8)) + .Invoke(nameof(Encoding.GetBytes), Literal(s)); + return Null; + } + + // Stream + if (frameworkType == typeof(Stream)) + { + if (exampleValue is InputExampleStreamValue streamValue) + return Static(typeof(File)).Invoke(nameof(File.OpenRead), Literal(streamValue.Filename)); + return Null; + } + + // Fallback + return frameworkType.IsValueType ? Default : Null; + } + + private static ValueExpression GetExpressionForList(CSharpType listType, InputExampleValue exampleValue) + { + var elementType = listType.ElementType; + var items = new List(); + + if (exampleValue is InputExampleListValue listValue) + { + foreach (var itemValue in listValue.Values) + { + items.Add(GetExpression(elementType, itemValue)); + } + } + + return New.Array(elementType, items.ToArray()); + } + + private static ValueExpression GetExpressionForDictionary(CSharpType dictionaryType, InputExampleValue exampleValue) + { + var keyType = dictionaryType.Arguments[0]; + var valueType = dictionaryType.Arguments[1]; + var entries = new Dictionary(); + + if (exampleValue is InputExampleObjectValue objectValue) + { + foreach (var (key, value) in objectValue.Values) + { + var keyExpr = GetExpression(keyType, InputExampleValue.Value(new InputPrimitiveType(InputPrimitiveTypeKind.String, "string", "TypeSpec.string"), key)); + var valueExpr = GetExpression(valueType, value); + entries[keyExpr] = valueExpr; + } + } + + return New.Dictionary(keyType, valueType, entries); + } + + private static ValueExpression GetExpressionForEnum(CSharpType enumType, InputExampleValue exampleValue) + { + var rawValue = GetRawValue(exampleValue); + if (rawValue == null) + return Default; + + // Access the enum member by name using the type reference + var rawString = rawValue.ToString()!; + // Use the type name as a static access point: EnumType.MemberName + return new MemberExpression(Static(enumType), rawString); + } + + private static ValueExpression GetExpressionForModel(CSharpType type, InputExampleValue exampleValue) + { + // For model types, generate new ModelType() — will be enhanced in M3/M4 + // with constructor parameter analysis + if (type.IsValueType) + return Default; + return New.Instance(type); + } + + private static ValueExpression GetExpressionForBinaryData(InputExampleValue exampleValue) + { + // Build an anonymous object from the example value and wrap in BinaryData.FromObjectAsJson + var anonymousObj = GetExpressionForAnonymousObject(exampleValue); + return Static(typeof(BinaryData)).Invoke(nameof(BinaryData.FromObjectAsJson), anonymousObj); + } + + /// + /// Converts an example value to an anonymous object expression for use in + /// BinaryData.FromObjectAsJson() or BinaryContent.Create(). + /// + internal static ValueExpression GetExpressionForAnonymousObject(InputExampleValue exampleValue) + { + if (exampleValue is InputExampleObjectValue objectValue) + { + var properties = new Dictionary(); + foreach (var (key, value) in objectValue.Values) + { + var rawVal = GetRawValue(value); + // Skip null properties in anonymous objects (causes compilation errors) + if (rawVal == null && value is InputExampleRawValue) + continue; + + var valueExpr = GetExpressionForAnonymousObject(value); + properties[Identifier(key)] = valueExpr; + } + return properties.Count > 0 ? New.Anonymous(properties) : New.Instance(typeof(object)); + } + + if (exampleValue is InputExampleListValue listValue) + { + var items = new List(); + foreach (var item in listValue.Values) + { + items.Add(GetExpressionForAnonymousObject(item)); + } + return New.Array(new CSharpType(typeof(object)), items.ToArray()); + } + + if (exampleValue is InputExampleStreamValue streamValue) + { + return Static(typeof(File)).Invoke(nameof(File.OpenRead), Literal(streamValue.Filename)); + } + + // Raw value — convert to literal + var raw = GetRawValue(exampleValue); + if (raw == null) + return Null; + + return raw switch + { + string s => Literal(s), + bool b => Literal(b), + int i => Literal(i), + long l => Literal(l), + float f => Literal(f), + double d => Literal(d), + decimal m => Literal(m), + _ => Literal(raw.ToString()!) + }; + } + + /// + /// Extracts the raw value from an if it's a raw (primitive) value. + /// Returns null for non-raw values (lists, objects, streams). + /// + private static object? GetRawValue(InputExampleValue exampleValue) + { + if (exampleValue is InputExampleRawValue rawValue) + return rawValue.RawValue; + return null; + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs new file mode 100644 index 00000000000..9a1b11a8a02 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs @@ -0,0 +1,708 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using Microsoft.TypeSpec.Generator.ClientModel.Primitives; +using Microsoft.TypeSpec.Generator.Expressions; +using Microsoft.TypeSpec.Generator.Input; +using Microsoft.TypeSpec.Generator.Primitives; +using Microsoft.TypeSpec.Generator.Providers; +using Microsoft.TypeSpec.Generator.Statements; +using static Microsoft.TypeSpec.Generator.Snippets.Snippet; + +namespace Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples +{ + /// + /// Represents a single sample for an operation — the bridge between an + /// and the generated C# sample method. + /// Resolves client construction chains, parameter value mappings, and method invocation details. + /// Modeled after the autorest DpgOperationSample pattern. + /// + [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] + public class OperationSample + { + private readonly ClientProvider _client; + private readonly ScmMethodProviderCollection _methodCollection; + private readonly InputServiceMethod _serviceMethod; + private readonly InputOperationExample _example; + private readonly MethodSignature _operationMethodSignature; + + private IReadOnlyList? _clientInvocationChain; + private Dictionary? _parameterValueMapping; + private InputType? _resultType; + + public OperationSample( + ClientProvider client, + ScmMethodProviderCollection methodCollection, + InputServiceMethod serviceMethod, + InputOperationExample example, + bool isConvenienceSample, + string exampleKey) + { + _client = client; + _methodCollection = methodCollection; + _serviceMethod = serviceMethod; + _example = example; + IsConvenienceSample = isConvenienceSample; + ExampleKey = exampleKey; + IsAllParametersUsed = exampleKey == "AllParameters"; + _operationMethodSignature = ResolveOperationSignature(); + } + + // ------------------------------------------------------------------- + // Core identity + // ------------------------------------------------------------------- + + /// + /// The example key, e.g. "ShortVersion" or "AllParameters". + /// + public string ExampleKey { get; } + + /// + /// Whether this is a convenience method sample (true) or protocol method sample (false). + /// + public bool IsConvenienceSample { get; } + + /// + /// Whether this sample uses all parameters (including optional). + /// + public bool IsAllParametersUsed { get; } + + // ------------------------------------------------------------------- + // Method info + // ------------------------------------------------------------------- + + /// + /// The input operation name. + /// + public string InputOperationName => _serviceMethod.Operation.Name; + + /// + /// The resource name from the operation, or the client name as fallback. + /// Used for method naming. + /// + public string? ResourceName => _serviceMethod.Operation.ResourceName ?? _client.InputClient.Name; + + /// + /// Whether the operation uses paging. + /// + public bool IsPageable => _serviceMethod is InputPagingServiceMethod; + + /// + /// Whether the operation is long-running. + /// + public bool IsLongRunning => _serviceMethod is InputLongRunningServiceMethod; + + /// + /// The method signature for the operation being sampled (protocol or convenience). + /// + public MethodSignature OperationMethodSignature => _operationMethodSignature; + + /// + /// Whether there is a response body on the operation. + /// + public bool HasResponseBody => _serviceMethod.Response?.Type != null; + + /// + /// Whether the response is a stream. + /// + public bool IsResponseStream => + _serviceMethod.Response?.Type is InputPrimitiveType { Kind: InputPrimitiveTypeKind.Stream }; + + /// + /// The effective result type of the operation. + /// For paging, this is the item type; for all others this is the response type. + /// + public InputType? ResultType => _resultType ??= GetEffectiveResponseType(); + + private MethodSignature ResolveOperationSignature() + { + var kind = IsConvenienceSample ? ScmMethodKind.Convenience : ScmMethodKind.Protocol; + var method = _methodCollection.MethodProviders + .FirstOrDefault(m => m.Kind == kind && !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async)); + return method?.Signature ?? _methodCollection.MethodProviders.First().Signature; + } + + // ------------------------------------------------------------------- + // Client invocation chain + // ------------------------------------------------------------------- + + /// + /// Ordered list of method signatures to invoke to construct the client: + /// [root ctor, .GetSubClient(), .GetLeafClient()]. + /// + public IReadOnlyList ClientInvocationChain => + _clientInvocationChain ??= BuildClientInvocationChain(); + + /// + /// Walks from the current client up to the root, collecting factory methods, + /// then pushes the root constructor. Returns the chain in top-down order. + /// + private IReadOnlyList BuildClientInvocationChain() + { + var callChain = new Stack(); + + // Walk from current client up to root, collecting factory methods + var currentInputClient = _client.InputClient; + while (currentInputClient.Parent != null) + { + var parentProvider = ResolveClientProvider(currentInputClient.Parent); + if (parentProvider != null) + { + var factoryMethod = FindSubClientFactoryMethod(parentProvider, currentInputClient.Name); + if (factoryMethod != null) + { + callChain.Push(factoryMethod); + } + } + currentInputClient = currentInputClient.Parent; + } + + // At the root, push the primary public constructor + var rootProvider = ResolveClientProvider(currentInputClient); + if (rootProvider != null) + { + var ctor = GetPrimaryPublicConstructor(rootProvider); + if (ctor != null) + { + callChain.Push(ctor); + } + } + + return callChain.ToArray(); + } + + private static ClientProvider? ResolveClientProvider(InputClient inputClient) + { + return ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient); + } + + private static MethodSignature? FindSubClientFactoryMethod(ClientProvider parentProvider, string subClientName) + { + // Match the naming convention used by ClientProvider: + // If name ends with "Client" → "Get{name}", otherwise → "Get{name}Client" + var expectedMethodName = subClientName.EndsWith("Client", StringComparison.OrdinalIgnoreCase) + ? $"Get{subClientName}" + : $"Get{subClientName}Client"; + + foreach (var method in parentProvider.Methods) + { + if (method.Signature.Name.Equals(expectedMethodName, StringComparison.OrdinalIgnoreCase) && + !method.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async)) + { + return method.Signature; + } + } + return null; + } + + private static ConstructorSignature? GetPrimaryPublicConstructor(ClientProvider clientProvider) + { + ConstructorSignature? best = null; + foreach (var ctor in clientProvider.Constructors) + { + if (ctor.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public)) + { + if (best == null || ctor.Signature.Parameters.Count > best.Parameters.Count) + { + best = ctor.Signature; + } + } + } + return best; + } + + // ------------------------------------------------------------------- + // Parameter value mapping + // ------------------------------------------------------------------- + + /// + /// Maps parameter names to their example values (either pre-built expressions or raw data). + /// + public IReadOnlyDictionary ParameterValueMapping => + _parameterValueMapping ??= EnsureParameterValueMapping(); + + private Dictionary EnsureParameterValueMapping() + { + var result = new Dictionary(); + var parameters = GetAllParameters(); + var parameterExamples = GetAllParameterExamples(); + + foreach (var parameter in parameters) + { + if (result.ContainsKey(parameter.Name)) + continue; + + if (TryProcessKnownParameter(result, parameter)) + continue; + + // Find the corresponding example value + var exampleValue = FindExampleValueByName(parameterExamples, parameter.Name); + + if (exampleValue == null && parameter.WireInfo?.SerializedName != null) + { + exampleValue = FindExampleValueByName(parameterExamples, parameter.WireInfo.SerializedName); + } + + if (exampleValue != null) + { + result.Add(parameter.Name, new ExampleParameterValue(parameter.Name, parameter.Type, exampleValue)); + } + else if (parameter.DefaultValue == null) + { + // Required parameter with no example — use default + var expression = DefaultOf(parameter.Type); + result.Add(parameter.Name, new ExampleParameterValue(parameter.Name, parameter.Type, expression)); + } + // Optional parameters with no value are intentionally omitted + } + + return result; + } + + /// + /// Known parameters that receive special handling (endpoint, credentials, cancellation, etc). + /// + private bool TryProcessKnownParameter(Dictionary result, ParameterProvider parameter) + { + var type = parameter.Type; + + // CancellationToken — skip entirely (we don't set it in samples) + if (type.Equals(typeof(CancellationToken))) + { + return true; + } + + // Endpoint (Uri) — produce `new Uri("http://localhost:3000")` or use example value + if (type.Equals(typeof(Uri)) && parameter.InputParameter is InputEndpointParameter) + { + var endpointExpr = GetEndpointValue(parameter.Name); + result[parameter.Name] = new ExampleParameterValue(parameter.Name, type, endpointExpr); + return true; + } + + // Request content (BinaryContent) — handled specially via body parameter lookup + if (parameter.IsContentParameter) + { + result[parameter.Name] = new ExampleParameterValue(parameter.Name, type, GetBodyParameterValue()); + return true; + } + + // ApiKeyCredential — produce `new ApiKeyCredential("")` + if (type.Name == "ApiKeyCredential" || type.Name == "AzureKeyCredential") + { + result[parameter.Name] = new ExampleParameterValue( + parameter.Name, type, New.Instance(type, Literal(""))); + return true; + } + + // TokenCredential — produce `new DefaultAzureCredential()` + if (type.Name == "TokenCredential") + { + result[parameter.Name] = new ExampleParameterValue( + parameter.Name, type, New.Instance(type)); + return true; + } + + // ClientOptions — skip (optional, not needed in sample) + if (parameter.Name.EndsWith("Options", StringComparison.OrdinalIgnoreCase) && + parameter.DefaultValue != null) + { + return true; + } + + return false; + } + + /// + /// Returns all the parameters that should be used in this sample. + /// Only required parameters are included if is false. + /// + private IEnumerable GetAllParameters() + { + // Parameters from the client invocation chain (ctor + factory methods) + foreach (var method in ClientInvocationChain) + { + foreach (var parameter in method.Parameters) + yield return parameter; + } + + // Parameters from the operation method itself + var operationParams = IsAllParametersUsed + ? _operationMethodSignature.Parameters + : _operationMethodSignature.Parameters.Where(p => p.DefaultValue == null); + + foreach (var parameter in operationParams) + yield return parameter; + } + + /// + /// Returns all parameter examples, handling spread parameters by extracting + /// individual properties from the model type. + /// + private IEnumerable GetAllParameterExamples() + { + foreach (var parameterExample in _example.Parameters) + { + var inputParameter = parameterExample.Parameter; + + // For spread parameters, the example value contains properties of the model + if (inputParameter is InputMethodParameter { Scope: InputParameterScope.Spread } && + inputParameter.Type is InputModelType modelType && + parameterExample.ExampleValue is InputExampleObjectValue objectValue) + { + var properties = objectValue.Values; + foreach (var modelOrBase in GetSelfAndBaseModels(modelType)) + { + foreach (var property in modelOrBase.Properties) + { + if (properties.TryGetValue(property.SerializedName, out var propValue)) + { + // Create a synthetic parameter example for each spread property + var syntheticParam = new InputMethodParameter( + property.Name, + null, // summary + property.Doc, + property.Type, + property.IsRequired, + property.IsReadOnly, + null, // access + property.SerializedName, + false, // isApiVersion + null, // defaultValue + InputParameterScope.Method, + InputRequestLocation.Body); + yield return new InputParameterExample(syntheticParam, propValue); + } + } + } + } + else + { + yield return parameterExample; + } + } + } + + /// + /// Searches for an example value by parameter name (case-sensitive). + /// + private static InputExampleValue? FindExampleValueByName( + IEnumerable parameterExamples, + string name) + { + foreach (var parameterExample in parameterExamples) + { + if (parameterExample.Parameter.Name == name) + { + return parameterExample.ExampleValue; + } + } + return null; + } + + /// + /// Gets the endpoint value, preferring the example value if available. + /// + private InputExampleValue GetEndpointValue(string parameterName) + { + // Try to find an endpoint value from the examples + var endpointExampleValue = _example.Parameters + .FirstOrDefault(e => e.Parameter is InputEndpointParameter)?.ExampleValue; + + if (endpointExampleValue != null) + return endpointExampleValue; + + // Fallback: placeholder + return InputExampleValue.Value(InputPrimitiveType.String, $"http://localhost:3000"); + } + + /// + /// Gets the body parameter value from the examples. + /// If there's a single body parameter example, use it. Otherwise search by type. + /// + private InputExampleValue GetBodyParameterValue() + { + var bodyParameters = _example.Parameters + .Where(e => e.Parameter is InputBodyParameter) + .ToArray(); + + if (bodyParameters.Length == 1) + { + return bodyParameters[0].ExampleValue; + } + + // Check for any body-located method parameter + var bodyMethodParam = _example.Parameters + .FirstOrDefault(e => e.Parameter is InputMethodParameter { Location: InputRequestLocation.Body }); + + if (bodyMethodParam != null) + { + return bodyMethodParam.ExampleValue; + } + + return InputExampleValue.Null(InputPrimitiveType.Any); + } + + /// + /// Walks the model and all its base models. + /// + private static IEnumerable GetSelfAndBaseModels(InputModelType model) + { + var current = model; + while (current != null) + { + yield return current; + current = current.BaseModel; + } + } + + // ------------------------------------------------------------------- + // Expression generation + // ------------------------------------------------------------------- + + /// + /// Converts the parameter value mapping entries to instances + /// for a specific set of parameters. Complex types are declared as out-of-line variables, + /// while simple types are inlined directly. + /// + public IEnumerable GetValueExpressionsForParameters( + IEnumerable parameters, + List variableDeclarationStatements) + { + foreach (var parameter in parameters) + { + ValueExpression parameterExpression; + + if (ParameterValueMapping.TryGetValue(parameter.Name, out var exampleValue)) + { + parameterExpression = ExampleValueExpressionBuilder.GetExpression(exampleValue); + } + else + { + // No example value — skip optional, use default for required + if (parameter.DefaultValue != null) + continue; + + parameterExpression = DefaultOf(parameter.Type); + } + + if (IsInlineParameter(parameter)) + { + yield return parameterExpression; + } + else + { + // Declare variable out-of-line + var varRef = new VariableExpression(parameter.Type, parameter.Name); + var declaration = NeedsDispose(parameter) + ? UsingDeclare(varRef, parameterExpression) + : Declare(varRef, parameterExpression); + variableDeclarationStatements.Add(declaration); + yield return varRef; + } + } + } + + /// + /// Determines whether a parameter value should be inlined directly in the method call + /// or declared as a separate variable. + /// + private static bool IsInlineParameter(ParameterProvider parameter) + { + var type = parameter.Type; + + // Content parameters (BinaryContent/RequestContent) → out-of-line + if (parameter.IsContentParameter) + return false; + + // Endpoint → out-of-line + if (type.Equals(typeof(Uri))) + return false; + + // Credentials → out-of-line + if (type.Name == "ApiKeyCredential" || type.Name == "AzureKeyCredential" || + type.Name == "TokenCredential") + return false; + + // Model types (non-framework, non-enum, non-collection) → out-of-line + if (!type.IsFrameworkType && !type.IsEnum && !type.IsList && !type.IsDictionary) + return false; + + // Everything else (primitives, enums, collections) → inline + return true; + } + + /// + /// Determines whether a parameter needs a using declaration for disposal. + /// + private static bool NeedsDispose(ParameterProvider parameter) + { + return parameter.IsContentParameter; + } + + // ------------------------------------------------------------------- + // Response type resolution + // ------------------------------------------------------------------- + + /// + /// Returns the effective response type. + /// For paging operations, unwraps the item type from the response model. + /// For non-paging operations, returns the response type directly. + /// + private InputType? GetEffectiveResponseType() + { + var responseType = _serviceMethod.Response?.Type; + + if (_serviceMethod is not InputPagingServiceMethod pagingMethod) + return responseType; + + // For paging, try to unwrap the item type from the response model + var itemSegments = pagingMethod.PagingMetadata.ItemPropertySegments; + if (itemSegments.Count == 0 || responseType is not InputModelType responseModel) + return responseType; + + // Walk the item property path to find the items array + InputType currentType = responseModel; + foreach (var segment in itemSegments) + { + if (currentType is not InputModelType currentModel) + break; + + var property = currentModel.Properties + .FirstOrDefault(p => p.SerializedName == segment || p.Name == segment); + if (property == null) + break; + + currentType = property.Type; + } + + // If we found an array type at the end of the path, return the element type + if (currentType is InputArrayType arrayType) + return arrayType.ValueType; + + return responseType; + } + + // ------------------------------------------------------------------- + // Static helpers for sample generation decisions + // ------------------------------------------------------------------- + + /// + /// Determines whether samples should be generated for the given method. + /// + public static bool ShouldGenerateSample(ClientProvider client, MethodSignature protocolSignature) + { + if (!protocolSignature.Modifiers.HasFlag(MethodSignatureModifiers.Public)) + return false; + + // Check for obsolete + if (protocolSignature.Attributes.Any(a => a.Type.Equals(typeof(ObsoleteAttribute)))) + return false; + + // Subclients are always valid; root clients need a public constructor + bool isSubClient = client.InputClient.Parent != null; + if (isSubClient) + return true; + + return client.Constructors.Any(c => + c.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public)); + } + + /// + /// Determines whether the ShortVersion sample should be generated. + /// If protocol and convenience signatures are effectively identical, skip ShortVersion for protocol + /// to avoid duplicate samples. + /// + public static bool ShouldGenerateShortVersion(ScmMethodProviderCollection methodCollection) + { + var protocolMethod = methodCollection.MethodProviders + .FirstOrDefault(m => m.Kind == ScmMethodKind.Protocol && + !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async)); + var convenienceMethod = methodCollection.MethodProviders + .FirstOrDefault(m => m.Kind == ScmMethodKind.Convenience && + !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async)); + + if (protocolMethod == null || convenienceMethod == null) + return true; + + var protocolParams = protocolMethod.Signature.Parameters; + var convenienceParams = convenienceMethod.Signature.Parameters; + + // If the convenience method has one fewer parameter (e.g., no CancellationToken) + // and all overlapping parameter types match, skip — they're effectively identica. + if (convenienceParams.Count == protocolParams.Count - 1 && convenienceParams.Count > 0 && + !convenienceParams.Last().Type.Equals(typeof(CancellationToken))) + { + bool allEqual = true; + for (int i = 0; i < convenienceParams.Count; i++) + { + if (!convenienceParams[i].Type.Equals(protocolParams[i].Type)) + { + allEqual = false; + break; + } + } + + if (allEqual) + return false; + } + + return true; + } + + // ------------------------------------------------------------------- + // Sample information (human-readable descriptions) + // ------------------------------------------------------------------- + + /// + /// Gets a human-readable description of what the sample demonstrates. + /// + public string GetSampleInformation(bool isAsync) + { + var methodName = isAsync + ? _operationMethodSignature.Name + "Async" + : _operationMethodSignature.Name; + + return IsConvenienceSample + ? GetSampleInformationForConvenience(methodName) + : GetSampleInformationForProtocol(methodName); + } + + private string GetSampleInformationForConvenience(string methodName) + { + if (IsAllParametersUsed) + return $"This sample shows how to call {methodName} with all parameters."; + return $"This sample shows how to call {methodName}."; + } + + private string GetSampleInformationForProtocol(string methodName) + { + if (IsAllParametersUsed) + { + var desc = GenerateParameterAndRequestContentDescription(_operationMethodSignature.Parameters); + var parseResult = HasResponseBody ? " and parse the result" : ""; + return $"This sample shows how to call {methodName} with all {desc}{parseResult}."; + } + return $"This sample shows how to call {methodName}{(HasResponseBody ? " and parse the result" : "")}."; + } + + private static string GenerateParameterAndRequestContentDescription(IReadOnlyList parameters) + { + var hasNonBodyParameter = parameters.Any(p => + p.Location != ParameterLocation.Body && p.Name != "options"); + var hasBodyParameter = parameters.Any(p => p.Location == ParameterLocation.Body); + + if (hasNonBodyParameter) + return hasBodyParameter ? "parameters and request content" : "parameters"; + return "request content"; + } + + private string GetDebuggerDisplay() + => $"Sample (Client: {_client.Name}, Method: {_operationMethodSignature.Name})"; + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/ExampleValueExpressionBuilderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/ExampleValueExpressionBuilderTests.cs new file mode 100644 index 00000000000..8daea57ac77 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/ExampleValueExpressionBuilderTests.cs @@ -0,0 +1,624 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples; +using Microsoft.TypeSpec.Generator.Expressions; +using Microsoft.TypeSpec.Generator.Input; +using Microsoft.TypeSpec.Generator.Primitives; +using Microsoft.TypeSpec.Generator.Tests.Common; +using NUnit.Framework; + +namespace Microsoft.TypeSpec.Generator.ClientModel.Tests.Providers.Samples +{ + public class ExampleValueExpressionBuilderTests + { + // ----------------------------------------------------------------------- + // ExampleParameterValue — dual mode + // ----------------------------------------------------------------------- + + [Test] + public void ParameterValue_WithExpression_ReturnsExpression() + { + var expr = new LiteralExpression(42); + var paramValue = new ExampleParameterValue("count", new CSharpType(typeof(int)), expr); + + var result = ExampleValueExpressionBuilder.GetExpression(paramValue); + + Assert.AreSame(expr, result); + } + + [Test] + public void ParameterValue_WithValue_ConvertsToExpression() + { + var inputValue = InputExampleValue.Value(InputPrimitiveType.Int32, 1234); + var paramValue = new ExampleParameterValue("count", new CSharpType(typeof(int)), inputValue); + + var result = ExampleValueExpressionBuilder.GetExpression(paramValue); + + Assert.IsNotNull(result); + } + + [Test] + public void ParameterValue_WithNeither_ReturnsDefault() + { + // Edge case: both Value and Expression are null (shouldn't happen in practice) + var paramValue = new ExampleParameterValue("x", new CSharpType(typeof(int)), + (InputExampleValue)null!); + + // This will go through GetExpression with null value — should return Default + var result = ExampleValueExpressionBuilder.GetExpression(paramValue); + Assert.IsNotNull(result); + } + + // ----------------------------------------------------------------------- + // Framework type conversions — primitives + // ----------------------------------------------------------------------- + + [Test] + public void String_FromRawValue() + { + var result = BuildExpression(typeof(string), InputExampleValue.Value(InputPrimitiveType.String, "hello")); + Assert.IsNotNull(result); + // Should not be null/default + Assert.IsNotInstanceOf(result); + } + + [Test] + public void String_FromNull() + { + var result = BuildExpression(typeof(string), InputExampleValue.Null(InputPrimitiveType.String)); + // Null string → Null keyword + Assert.IsNotNull(result); + } + + [Test] + public void Bool_True() + { + var result = BuildExpression(typeof(bool), InputExampleValue.Value(InputPrimitiveType.Boolean, true)); + Assert.IsNotNull(result); + } + + [Test] + public void Int32_FromRawValue() + { + var result = BuildExpression(typeof(int), InputExampleValue.Value(InputPrimitiveType.Int32, 1234)); + Assert.IsNotNull(result); + } + + [Test] + public void Int64_FromRawValue() + { + var result = BuildExpression(typeof(long), InputExampleValue.Value(InputPrimitiveType.Int64, 1234L)); + Assert.IsNotNull(result); + } + + [Test] + public void Float_FromRawValue() + { + var result = BuildExpression(typeof(float), InputExampleValue.Value(InputPrimitiveType.Float32, 123.45f)); + Assert.IsNotNull(result); + } + + [Test] + public void Double_FromRawValue() + { + var result = BuildExpression(typeof(double), InputExampleValue.Value(InputPrimitiveType.Float64, 123.45)); + Assert.IsNotNull(result); + } + + [Test] + public void Decimal_FromRawValue() + { + var result = BuildExpression(typeof(decimal), InputExampleValue.Value( + new InputPrimitiveType(InputPrimitiveTypeKind.Decimal, "decimal", "TypeSpec.decimal"), 123.45m)); + Assert.IsNotNull(result); + } + + // ----------------------------------------------------------------------- + // Framework type conversions — complex types + // ----------------------------------------------------------------------- + + [Test] + public void Guid_FromString() + { + var result = BuildExpression(typeof(Guid), + InputExampleValue.Value(InputPrimitiveType.String, "73f411fe-4f43-4b4b-9cbd-6828d8f4cf9a")); + + // Should produce Guid.Parse("...") + Assert.IsInstanceOf(result); + } + + [Test] + public void Uri_FromString() + { + var result = BuildExpression(typeof(Uri), + InputExampleValue.Value(InputPrimitiveType.Url, "http://localhost:3000")); + + // Should produce new Uri("...") + Assert.IsNotNull(result); + } + + [Test] + public void DateTimeOffset_FromString() + { + var result = BuildExpression(typeof(DateTimeOffset), + InputExampleValue.Value(InputPrimitiveType.String, "2022-05-10T18:57:31.2311892Z")); + + // Should produce DateTimeOffset.Parse("...") + Assert.IsInstanceOf(result); + } + + [Test] + public void DateTimeOffset_FromUnixTimestamp() + { + var result = BuildExpression(typeof(DateTimeOffset), + InputExampleValue.Value(InputPrimitiveType.Int64, 1652209051L)); + + // Should produce DateTimeOffset.FromUnixTimeSeconds(...) + Assert.IsInstanceOf(result); + } + + [Test] + public void TimeSpan_FromIso8601String() + { + var result = BuildExpression(typeof(TimeSpan), + InputExampleValue.Value(InputPrimitiveType.String, "PT1H23M45S")); + + // Should produce XmlConvert.ToTimeSpan("...") + Assert.IsInstanceOf(result); + } + + [Test] + public void TimeSpan_FromSeconds() + { + var result = BuildExpression(typeof(TimeSpan), + InputExampleValue.Value(InputPrimitiveType.Float64, 10.5)); + + // Should produce TimeSpan.FromSeconds(...) + Assert.IsInstanceOf(result); + } + + [Test] + public void BinaryData_FromObject() + { + var objValue = InputExampleValue.Object( + InputFactory.Model("TestModel"), + new Dictionary + { + ["name"] = InputExampleValue.Value(InputPrimitiveType.String, "test") + }); + + var result = BuildExpression(typeof(BinaryData), objValue); + + // Should produce BinaryData.FromObjectAsJson(new { name = "test" }) + Assert.IsInstanceOf(result); + } + + [Test] + public void ByteArray_FromString() + { + var result = BuildExpression(typeof(byte[]), + InputExampleValue.Value(InputPrimitiveType.String, "dGVzdA==")); + + // Should produce Encoding.UTF8.GetBytes("...") + Assert.IsInstanceOf(result); + } + + [Test] + public void Stream_FromStreamValue() + { + var result = BuildExpression(typeof(Stream), + InputExampleValue.Stream(InputPrimitiveType.String, "")); + + // Should produce File.OpenRead("...") + Assert.IsInstanceOf(result); + } + + // ----------------------------------------------------------------------- + // Collections + // ----------------------------------------------------------------------- + + [Test] + public void List_FromListValue() + { + var listType = new CSharpType(typeof(IList<>), new CSharpType(typeof(int))); + var listValue = InputExampleValue.List( + new InputArrayType("list", "TypeSpec.Array", InputPrimitiveType.Int32), + new[] { InputExampleValue.Value(InputPrimitiveType.Int32, 1234) }); + + var result = ExampleValueExpressionBuilder.GetExpression(listType, listValue); + + // New.Array returns IndexableExpression wrapping NewArrayExpression + Assert.IsNotNull(result); + } + + [Test] + public void Dictionary_FromObjectValue() + { + var dictType = new CSharpType(typeof(IDictionary<,>), new CSharpType(typeof(string)), new CSharpType(typeof(int))); + var dictValue = InputExampleValue.Object( + new InputDictionaryType("dict", InputPrimitiveType.String, InputPrimitiveType.Int32), + new Dictionary + { + ["key"] = InputExampleValue.Value(InputPrimitiveType.Int32, 42) + }); + + var result = ExampleValueExpressionBuilder.GetExpression(dictType, dictValue); + + // New.Dictionary returns DictionaryExpression wrapping NewInstanceExpression + Assert.IsNotNull(result); + } + + // ----------------------------------------------------------------------- + // Enum + // ----------------------------------------------------------------------- + + [Test] + public void Enum_ProducesMemberAccess() + { + // Use a real framework enum type for testing + var enumType = new CSharpType(typeof(DayOfWeek)); + var value = InputExampleValue.Value( + InputFactory.StringEnum("DayOfWeek", [("Monday", "Monday")]), "Monday"); + + var result = ExampleValueExpressionBuilder.GetExpression(enumType, value); + + // Should produce a member expression like DayOfWeek.Monday + Assert.IsInstanceOf(result); + } + + // ----------------------------------------------------------------------- + // Model (basic) + // ----------------------------------------------------------------------- + + [Test] + public void Model_ProducesNewInstance() + { + var modelType = new CSharpType(typeof(object)); + var value = InputExampleValue.Object( + InputFactory.Model("Widget"), + new Dictionary()); + + var result = ExampleValueExpressionBuilder.GetExpression(modelType, value); + + // Non-collection, non-enum framework type falls through to framework handler + Assert.IsNotNull(result); + } + + // ----------------------------------------------------------------------- + // Anonymous object (for BinaryContent) + // ----------------------------------------------------------------------- + + [Test] + public void AnonymousObject_FromNestedValues() + { + var objValue = InputExampleValue.Object( + InputFactory.Model("Request"), + new Dictionary + { + ["name"] = InputExampleValue.Value(InputPrimitiveType.String, "test"), + ["count"] = InputExampleValue.Value(InputPrimitiveType.Int32, 5) + }); + + var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(objValue); + + // Should produce new { name = "test", count = 5 } + Assert.IsNotNull(result); + } + + [Test] + public void AnonymousObject_SkipsNullValues() + { + var objValue = InputExampleValue.Object( + InputFactory.Model("Request"), + new Dictionary + { + ["name"] = InputExampleValue.Value(InputPrimitiveType.String, "test"), + ["nullable"] = InputExampleValue.Null(InputPrimitiveType.String) + }); + + var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(objValue); + + // Should produce new { name = "test" } — nullable skipped + Assert.IsNotNull(result); + } + + [Test] + public void AnonymousObject_EmptyObject() + { + var objValue = InputExampleValue.Object( + InputFactory.Model("Empty"), + new Dictionary()); + + var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(objValue); + + // Empty object → new object() (wrapped in ScopedApi) + Assert.IsNotNull(result); + } + + // ----------------------------------------------------------------------- + // Null / default fallbacks + // ----------------------------------------------------------------------- + + [Test] + public void Null_ValueType_ReturnsDefault() + { + var result = BuildExpression(typeof(int), InputExampleValue.Null(InputPrimitiveType.Int32)); + Assert.IsInstanceOf(result); + } + + [Test] + public void Null_ReferenceType_ReturnsNull() + { + var result = BuildExpression(typeof(string), InputExampleValue.Null(InputPrimitiveType.String)); + Assert.IsInstanceOf(result); + } + + // ----------------------------------------------------------------------- + // Missing primitive types — cast expressions + // ----------------------------------------------------------------------- + + [Test] + public void Short_FromRawValue() + { + var result = BuildExpression(typeof(short), InputExampleValue.Value( + new InputPrimitiveType(InputPrimitiveTypeKind.Int16, "int16", "TypeSpec.int16"), (short)1234)); + Assert.IsNotNull(result); + Assert.IsInstanceOf(result); + } + + [Test] + public void SByte_FromRawValue() + { + var result = BuildExpression(typeof(sbyte), InputExampleValue.Value( + new InputPrimitiveType(InputPrimitiveTypeKind.Int8, "int8", "TypeSpec.int8"), (sbyte)123)); + Assert.IsNotNull(result); + Assert.IsInstanceOf(result); + } + + [Test] + public void Byte_FromRawValue() + { + var result = BuildExpression(typeof(byte), InputExampleValue.Value( + new InputPrimitiveType(InputPrimitiveTypeKind.UInt8, "uint8", "TypeSpec.uint8"), (byte)123)); + Assert.IsNotNull(result); + Assert.IsInstanceOf(result); + } + + [Test] + public void UShort_FromRawValue() + { + var result = BuildExpression(typeof(ushort), InputExampleValue.Value( + new InputPrimitiveType(InputPrimitiveTypeKind.UInt16, "uint16", "TypeSpec.uint16"), (ushort)1234)); + Assert.IsNotNull(result); + Assert.IsInstanceOf(result); + } + + [Test] + public void UInt_FromRawValue() + { + var result = BuildExpression(typeof(uint), InputExampleValue.Value( + new InputPrimitiveType(InputPrimitiveTypeKind.UInt32, "uint32", "TypeSpec.uint32"), (uint)1234)); + Assert.IsNotNull(result); + Assert.IsInstanceOf(result); + } + + [Test] + public void ULong_FromRawValue() + { + var result = BuildExpression(typeof(ulong), InputExampleValue.Value( + new InputPrimitiveType(InputPrimitiveTypeKind.UInt64, "uint64", "TypeSpec.uint64"), (ulong)1234)); + Assert.IsNotNull(result); + Assert.IsInstanceOf(result); + } + + // ----------------------------------------------------------------------- + // Null paths for complex types + // ----------------------------------------------------------------------- + + [Test] + public void Bool_Null_ReturnsDefault() + { + var result = BuildExpression(typeof(bool), InputExampleValue.Null(InputPrimitiveType.Boolean)); + Assert.IsInstanceOf(result); + } + + [Test] + public void Guid_Null_ReturnsDefault() + { + var result = BuildExpression(typeof(Guid), InputExampleValue.Null(InputPrimitiveType.String)); + Assert.IsInstanceOf(result); + } + + [Test] + public void Uri_Null_ReturnsNull() + { + var result = BuildExpression(typeof(Uri), InputExampleValue.Null(InputPrimitiveType.String)); + Assert.IsNotNull(result); + } + + [Test] + public void DateTimeOffset_Null_ReturnsDefault() + { + var result = BuildExpression(typeof(DateTimeOffset), InputExampleValue.Null(InputPrimitiveType.String)); + Assert.IsInstanceOf(result); + } + + [Test] + public void TimeSpan_Null_ReturnsDefault() + { + var result = BuildExpression(typeof(TimeSpan), InputExampleValue.Null(InputPrimitiveType.String)); + Assert.IsInstanceOf(result); + } + + [Test] + public void ByteArray_Null_ReturnsNull() + { + var result = BuildExpression(typeof(byte[]), InputExampleValue.Null(InputPrimitiveType.String)); + Assert.IsNotNull(result); + } + + [Test] + public void Stream_NonStreamValue_ReturnsNull() + { + var result = BuildExpression(typeof(Stream), InputExampleValue.Value(InputPrimitiveType.String, "notastream")); + Assert.IsNotNull(result); + } + + // ----------------------------------------------------------------------- + // Enum edge case + // ----------------------------------------------------------------------- + + [Test] + public void Enum_Null_ReturnsDefault() + { + var enumType = new CSharpType(typeof(DayOfWeek)); + var value = InputExampleValue.Null(InputFactory.StringEnum("DayOfWeek", [("Monday", "Monday")])); + + var result = ExampleValueExpressionBuilder.GetExpression(enumType, value); + + Assert.IsInstanceOf(result); + } + + // ----------------------------------------------------------------------- + // Collection edge cases + // ----------------------------------------------------------------------- + + [Test] + public void List_FromNonListValue_ReturnsEmptyArray() + { + var listType = new CSharpType(typeof(IList<>), new CSharpType(typeof(int))); + var nonListValue = InputExampleValue.Value(InputPrimitiveType.Int32, 1234); + + var result = ExampleValueExpressionBuilder.GetExpression(listType, nonListValue); + + // Should produce an empty array + Assert.IsNotNull(result); + } + + [Test] + public void Dictionary_FromNonObjectValue_ReturnsEmptyDictionary() + { + var dictType = new CSharpType(typeof(IDictionary<,>), new CSharpType(typeof(string)), new CSharpType(typeof(int))); + var nonObjValue = InputExampleValue.Value(InputPrimitiveType.Int32, 1234); + + var result = ExampleValueExpressionBuilder.GetExpression(dictType, nonObjValue); + + // Should produce an empty dictionary + Assert.IsNotNull(result); + } + + // ----------------------------------------------------------------------- + // Model edge case + // ----------------------------------------------------------------------- + + [Test] + public void Model_ValueType_ReturnsDefault() + { + var valueType = new CSharpType(typeof(int)); // value type but not enum/collection/known + var value = InputExampleValue.Object( + InputFactory.Model("Widget"), + new Dictionary()); + + // int is a framework type, so it goes to framework handler, not model handler + var result = ExampleValueExpressionBuilder.GetExpression(valueType, value); + Assert.IsNotNull(result); + } + + // ----------------------------------------------------------------------- + // Anonymous object edge cases + // ----------------------------------------------------------------------- + + [Test] + public void AnonymousObject_WithNestedList() + { + var listValue = InputExampleValue.List( + new InputArrayType("list", "TypeSpec.Array", InputPrimitiveType.String), + new[] { InputExampleValue.Value(InputPrimitiveType.String, "item1") }); + + var objValue = InputExampleValue.Object( + InputFactory.Model("Request"), + new Dictionary + { + ["items"] = listValue + }); + + var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(objValue); + Assert.IsNotNull(result); + } + + [Test] + public void AnonymousObject_WithNestedObject() + { + var innerObj = InputExampleValue.Object( + InputFactory.Model("Inner"), + new Dictionary + { + ["innerProp"] = InputExampleValue.Value(InputPrimitiveType.Int32, 42) + }); + + var outerObj = InputExampleValue.Object( + InputFactory.Model("Outer"), + new Dictionary + { + ["nested"] = innerObj + }); + + var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(outerObj); + Assert.IsNotNull(result); + } + + [Test] + public void AnonymousObject_FromRawPrimitive() + { + var rawValue = InputExampleValue.Value(InputPrimitiveType.String, "hello"); + + var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(rawValue); + Assert.IsNotNull(result); + } + + [Test] + public void AnonymousObject_FromNullRaw() + { + var nullValue = InputExampleValue.Null(InputPrimitiveType.String); + + var result = ExampleValueExpressionBuilder.GetExpressionForAnonymousObject(nullValue); + // Should return Null keyword + Assert.IsNotNull(result); + } + + // ----------------------------------------------------------------------- + // Fallback for unknown framework types + // ----------------------------------------------------------------------- + + [Test] + public void UnknownValueType_ReturnsDefault() + { + // A value type that doesn't match any specific handler + var result = BuildExpression(typeof(DateTime), InputExampleValue.Null(InputPrimitiveType.String)); + Assert.IsInstanceOf(result); + } + + [Test] + public void UnknownReferenceType_ReturnsNull() + { + // A reference type that doesn't match any specific handler + var result = BuildExpression(typeof(System.Text.StringBuilder), InputExampleValue.Null(InputPrimitiveType.String)); + Assert.IsNotNull(result); + } + + // ----------------------------------------------------------------------- + // Helpers + // ----------------------------------------------------------------------- + + private static ValueExpression BuildExpression(Type frameworkType, InputExampleValue value) + { + return ExampleValueExpressionBuilder.GetExpression(new CSharpType(frameworkType), value); + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs new file mode 100644 index 00000000000..9aa629dc7e4 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs @@ -0,0 +1,368 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.TypeSpec.Generator.ClientModel.Providers; +using Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples; +using Microsoft.TypeSpec.Generator.Expressions; +using Microsoft.TypeSpec.Generator.Input; +using Microsoft.TypeSpec.Generator.Primitives; +using Microsoft.TypeSpec.Generator.Statements; +using Microsoft.TypeSpec.Generator.Tests.Common; +using NUnit.Framework; + +namespace Microsoft.TypeSpec.Generator.ClientModel.Tests.Providers.Samples +{ + public class OperationSampleTests + { + [SetUp] + public void SetUp() + { + MockHelpers.LoadMockGenerator(); + } + + // ------------------------------------------------------------------- + // Core identity properties + // ------------------------------------------------------------------- + + [Test] + public void ExampleKey_PreservedFromInput() + { + var sample = CreateBasicSample("ShortVersion"); + Assert.AreEqual("ShortVersion", sample.ExampleKey); + } + + [Test] + public void IsAllParametersUsed_TrueForAllParameters() + { + var sample = CreateBasicSample("AllParameters"); + Assert.IsTrue(sample.IsAllParametersUsed); + } + + [Test] + public void IsAllParametersUsed_FalseForShortVersion() + { + var sample = CreateBasicSample("ShortVersion"); + Assert.IsFalse(sample.IsAllParametersUsed); + } + + [Test] + public void IsConvenienceSample_PreservedFromInput() + { + var sample = CreateBasicSample("ShortVersion", isConvenience: true); + Assert.IsTrue(sample.IsConvenienceSample); + } + + [Test] + public void InputOperationName_PreservedFromServiceMethod() + { + var sample = CreateBasicSample("ShortVersion"); + Assert.AreEqual("TestOperation", sample.InputOperationName); + } + + [Test] + public void ResourceName_FallsBackToClientName() + { + var sample = CreateBasicSample("ShortVersion"); + // InputFactory.Operation sets ResourceName to null, + // so falls back to client name + Assert.AreEqual("TestClient", sample.ResourceName); + } + + // ------------------------------------------------------------------- + // Operation type detection + // ------------------------------------------------------------------- + + [Test] + public void IsPageable_FalseForBasicMethod() + { + var sample = CreateBasicSample("ShortVersion"); + Assert.IsFalse(sample.IsPageable); + } + + [Test] + public void IsPageable_TrueForPagingMethod() + { + var operation = InputFactory.Operation("ListItems"); + var pagingMetadata = InputFactory.PagingMetadata(["items"], null, null); + var serviceMethod = InputFactory.PagingServiceMethod("ListItems", operation, pagingMetadata: pagingMetadata); + + // Verify the service method is recognized as paging + Assert.IsInstanceOf(serviceMethod); + } + + [Test] + public void IsLongRunning_FalseForBasicMethod() + { + var sample = CreateBasicSample("ShortVersion"); + Assert.IsFalse(sample.IsLongRunning); + } + + [Test] + public void HasResponseBody_FalseForVoidReturn() + { + var sample = CreateBasicSample("ShortVersion"); + // Basic operation with no response type → no body + Assert.IsFalse(sample.HasResponseBody); + } + + // ------------------------------------------------------------------- + // Client invocation chain — root client + // ------------------------------------------------------------------- + + [Test] + public void ClientInvocationChain_RootClient_HasSingleConstructor() + { + var sample = CreateBasicSample("ShortVersion"); + var chain = sample.ClientInvocationChain; + + Assert.IsTrue(chain.Count >= 1); + Assert.IsInstanceOf(chain[0]); + } + + // ------------------------------------------------------------------- + // Client invocation chain — subclient + // ------------------------------------------------------------------- + + [Test] + public void ClientInvocationChain_SubClient_HasConstructorAndFactory() + { + var (sample, _, _) = CreateSubClientSample("ShortVersion"); + var chain = sample.ClientInvocationChain; + + Assert.IsTrue(chain.Count >= 2); + Assert.IsInstanceOf(chain[0]); + Assert.IsInstanceOf(chain[1]); + } + + // ------------------------------------------------------------------- + // Parameter value mapping + // ------------------------------------------------------------------- + + [Test] + public void ParameterMapping_SpecProvidedValue_IsMapped() + { + var bodyParam = InputFactory.MethodParameter("message", InputPrimitiveType.String, isRequired: true); + var example = new InputOperationExample( + "ShortVersion", + null, + [new InputParameterExample(bodyParam, InputExampleValue.Value(InputPrimitiveType.String, "hello"))], + ""); + + var operation = InputFactory.Operation("SendMessage", + parameters: [InputFactory.BodyParameter("message", InputPrimitiveType.String, isRequired: true)]); + var serviceMethod = InputFactory.BasicServiceMethod("SendMessage", operation, parameters: [bodyParam]); + var inputClient = InputFactory.Client("MsgClient", methods: [serviceMethod]); + + MockHelpers.LoadMockGenerator( + createCSharpTypeCore: (inputType) => new CSharpType(typeof(string))); + + var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; + var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); + + // Use convenience sample so the parameter name "message" is preserved + var sample = new OperationSample(client, methodCollection, serviceMethod, example, true, "ShortVersion"); + var mapping = sample.ParameterValueMapping; + + Assert.IsTrue(mapping.ContainsKey("message")); + Assert.IsNotNull(mapping["message"].Value); + } + + [Test] + public void ParameterMapping_MissingOptionalParam_IsOmitted() + { + var optionalParam = InputFactory.MethodParameter("tag", InputPrimitiveType.String, isRequired: false, + defaultValue: InputFactory.Constant.String("default")); + var example = new InputOperationExample("ShortVersion", null, [], ""); + + var operation = InputFactory.Operation("GetItem", + parameters: [InputFactory.QueryParameter("tag", InputPrimitiveType.String, isRequired: false)]); + var serviceMethod = InputFactory.BasicServiceMethod("GetItem", operation, parameters: [optionalParam]); + var inputClient = InputFactory.Client("ItemClient", methods: [serviceMethod]); + + MockHelpers.LoadMockGenerator( + createCSharpTypeCore: (inputType) => new CSharpType(typeof(string))); + + var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; + var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); + + var sample = new OperationSample(client, methodCollection, serviceMethod, example, true, "ShortVersion"); + var mapping = sample.ParameterValueMapping; + + Assert.IsFalse(mapping.ContainsKey("tag")); + } + + // ------------------------------------------------------------------- + // GetValueExpressionsForParameters + // ------------------------------------------------------------------- + + [Test] + public void GetValueExpressions_ProducesExpressions() + { + var bodyParam = InputFactory.MethodParameter("count", InputPrimitiveType.Int32, isRequired: true); + var example = new InputOperationExample( + "ShortVersion", + null, + [new InputParameterExample(bodyParam, InputExampleValue.Value(InputPrimitiveType.Int32, 42))], + ""); + + var operation = InputFactory.Operation("DoStuff", + parameters: [InputFactory.BodyParameter("count", InputPrimitiveType.Int32, isRequired: true)]); + var serviceMethod = InputFactory.BasicServiceMethod("DoStuff", operation, parameters: [bodyParam]); + var inputClient = InputFactory.Client("StuffClient", methods: [serviceMethod]); + + MockHelpers.LoadMockGenerator( + createCSharpTypeCore: (inputType) => new CSharpType(typeof(int))); + + var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; + var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); + + var sample = new OperationSample(client, methodCollection, serviceMethod, example, false, "ShortVersion"); + + var declarations = new List(); + var syncProtocol = methodCollection.MethodProviders + .FirstOrDefault(m => m.Kind == ScmMethodKind.Protocol && + !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async)); + + if (syncProtocol != null) + { + var expressions = sample.GetValueExpressionsForParameters( + syncProtocol.Signature.Parameters, declarations).ToList(); + + Assert.IsTrue(expressions.Count > 0); + } + } + + // ------------------------------------------------------------------- + // Sample information (human-readable descriptions) + // ------------------------------------------------------------------- + + [Test] + public void GetSampleInformation_Sync_Convenience() + { + var sample = CreateBasicSample("ShortVersion", isConvenience: true); + var info = sample.GetSampleInformation(false); + Assert.IsTrue(info.Contains("how to call")); + Assert.IsFalse(info.Contains("Async")); + } + + [Test] + public void GetSampleInformation_Async_AppendsAsync() + { + var sample = CreateBasicSample("ShortVersion", isConvenience: true); + var info = sample.GetSampleInformation(true); + Assert.IsTrue(info.Contains("Async")); + } + + [Test] + public void GetSampleInformation_AllParameters_IncludesAllParameters() + { + var sample = CreateBasicSample("AllParameters", isConvenience: true); + var info = sample.GetSampleInformation(false); + Assert.IsTrue(info.Contains("all parameters")); + } + + // ------------------------------------------------------------------- + // Static helper: ShouldGenerateSample + // ------------------------------------------------------------------- + + [Test] + public void ShouldGenerateSample_PublicMethod_ReturnsTrue() + { + var operation = InputFactory.Operation("TestOp"); + var serviceMethod = InputFactory.BasicServiceMethod("TestOp", operation); + var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]); + + var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; + var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); + + var syncProtocol = methodCollection.MethodProviders + .FirstOrDefault(m => m.Kind == ScmMethodKind.Protocol && + !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async)); + + if (syncProtocol != null) + { + Assert.IsTrue(OperationSample.ShouldGenerateSample(client, syncProtocol.Signature)); + } + } + + [Test] + public void ShouldGenerateSample_SubClient_ReturnsTrue() + { + var (_, _, subProvider) = CreateSubClientSample("ShortVersion"); + + // Verify that subclients are identified correctly + Assert.IsTrue(subProvider.InputClient.Parent != null); + } + + // ------------------------------------------------------------------- + // Static helper: ShouldGenerateShortVersion + // ------------------------------------------------------------------- + + [Test] + public void ShouldGenerateShortVersion_NoConvenience_ReturnsTrue() + { + var operation = InputFactory.Operation("TestOp", generateConvenienceMethod: false); + var serviceMethod = InputFactory.BasicServiceMethod("TestOp", operation); + var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]); + + var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; + var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); + + Assert.IsTrue(OperationSample.ShouldGenerateShortVersion(methodCollection)); + } + + // ------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------- + + private static OperationSample CreateBasicSample(string exampleKey, bool isConvenience = false) + { + var operation = InputFactory.Operation("TestOperation"); + var serviceMethod = InputFactory.BasicServiceMethod("TestOperation", operation); + var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]); + + var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; + var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); + var example = new InputOperationExample(exampleKey, null, [], ""); + + return new OperationSample(client, methodCollection, serviceMethod, example, isConvenience, exampleKey); + } + + private static OperationSample CreatePagingSample(string exampleKey) + { + var operation = InputFactory.Operation("ListItems"); + var pagingMetadata = InputFactory.PagingMetadata(["items"], null, null); + var serviceMethod = InputFactory.PagingServiceMethod("ListItems", operation, pagingMetadata: pagingMetadata); + var inputClient = InputFactory.Client("PagingClient", methods: [serviceMethod]); + + var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; + var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); + var example = new InputOperationExample(exampleKey, null, [], ""); + + return new OperationSample(client, methodCollection, serviceMethod, example, false, exampleKey); + } + + private static (OperationSample sample, ClientProvider rootClient, ClientProvider subClient) CreateSubClientSample(string exampleKey) + { + var endpointParam = InputFactory.EndpointParameter("endpoint", InputPrimitiveType.Url, isRequired: true); + var parentClient = InputFactory.Client("RootClient", parameters: [endpointParam]); + var subOperation = InputFactory.Operation("DoWork"); + var subServiceMethod = InputFactory.BasicServiceMethod("DoWork", subOperation); + var subClient = InputFactory.Client("SubClient", parent: parentClient, + methods: [subServiceMethod], + initializedBy: InputClientInitializedBy.Parent); + + MockHelpers.LoadMockGenerator(clients: () => [parentClient, subClient]); + + var rootProvider = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(parentClient)!; + var subProvider = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(subClient)!; + var methodCollection = new ScmMethodProviderCollection(subServiceMethod, subProvider); + var example = new InputOperationExample(exampleKey, null, [], ""); + + var sample = new OperationSample(subProvider, methodCollection, subServiceMethod, example, false, exampleKey); + return (sample, rootProvider, subProvider); + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/Properties/AssemblyInfo.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/Properties/AssemblyInfo.cs index f3131262a05..53b57a107a3 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/Properties/AssemblyInfo.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/Properties/AssemblyInfo.cs @@ -6,3 +6,5 @@ [assembly: InternalsVisibleTo("Microsoft.TypeSpec.Generator.Input.Tests.Perf, PublicKey=002400000480000094000000060200000024000052534131000400000100010041df4fe80c5af6ff9a410db5a173b0ce24ad68764c623e308b1584a88b1d1d82277f746c1cccba48997e13db3366d5ed676576ffd293293baf42c643f008ba2e8a556e25e529c0407a38506555340749559f5100e6fd78cc935bb6c82d2af303beb0d3c6563400659610759b4ed5cb2e0faf36b17e6842f04cdc544c74e051ba")] [assembly: InternalsVisibleTo("Microsoft.TypeSpec.Generator.Input.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010041df4fe80c5af6ff9a410db5a173b0ce24ad68764c623e308b1584a88b1d1d82277f746c1cccba48997e13db3366d5ed676576ffd293293baf42c643f008ba2e8a556e25e529c0407a38506555340749559f5100e6fd78cc935bb6c82d2af303beb0d3c6563400659610759b4ed5cb2e0faf36b17e6842f04cdc544c74e051ba")] [assembly: InternalsVisibleTo("Microsoft.TypeSpec.Generator.Tests.Common, PublicKey=002400000480000094000000060200000024000052534131000400000100010041df4fe80c5af6ff9a410db5a173b0ce24ad68764c623e308b1584a88b1d1d82277f746c1cccba48997e13db3366d5ed676576ffd293293baf42c643f008ba2e8a556e25e529c0407a38506555340749559f5100e6fd78cc935bb6c82d2af303beb0d3c6563400659610759b4ed5cb2e0faf36b17e6842f04cdc544c74e051ba")] +[assembly: InternalsVisibleTo("Microsoft.TypeSpec.Generator, PublicKey=002400000480000094000000060200000024000052534131000400000100010041df4fe80c5af6ff9a410db5a173b0ce24ad68764c623e308b1584a88b1d1d82277f746c1cccba48997e13db3366d5ed676576ffd293293baf42c643f008ba2e8a556e25e529c0407a38506555340749559f5100e6fd78cc935bb6c82d2af303beb0d3c6563400659610759b4ed5cb2e0faf36b17e6842f04cdc544c74e051ba")] +[assembly: InternalsVisibleTo("Microsoft.TypeSpec.Generator.ClientModel, PublicKey=002400000480000094000000060200000024000052534131000400000100010041df4fe80c5af6ff9a410db5a173b0ce24ad68764c623e308b1584a88b1d1d82277f746c1cccba48997e13db3366d5ed676576ffd293293baf42c643f008ba2e8a556e25e529c0407a38506555340749559f5100e6fd78cc935bb6c82d2af303beb0d3c6563400659610759b4ed5cb2e0faf36b17e6842f04cdc544c74e051ba")] From 18f825d99e7f96b368e585e4a40d4160e51bcaef Mon Sep 17 00:00:00 2001 From: LazyLinh2 Date: Thu, 14 May 2026 10:03:57 -0700 Subject: [PATCH 08/10] OperationSample --- .../src/Primitives/ScmKnownParameters.cs | 4 + .../src/Providers/Samples/OperationSample.cs | 57 ++++- .../Providers/ScmMethodProviderCollection.cs | 44 ++++ .../Providers/Samples/OperationSampleTests.cs | 218 ++++++++++++++++++ .../ScmMethodProviderCollectionTests.cs | 79 +++++++ .../test/ScmKnownParametersTests.cs | 13 ++ 6 files changed, 407 insertions(+), 8 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Primitives/ScmKnownParameters.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Primitives/ScmKnownParameters.cs index e07006acf43..336fc35dff0 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Primitives/ScmKnownParameters.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Primitives/ScmKnownParameters.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.ClientModel; using System.ClientModel.Primitives; using System.Text.Json; using System.Threading; @@ -66,5 +67,8 @@ public static ParameterProvider ClientOptions(CSharpType clientOptionsType) public static readonly ParameterProvider NextPage = new ParameterProvider("nextPage", $"The url of the next page of responses.", typeof(Uri)); + + public static readonly ParameterProvider KeyCredential = new("credential", FormattableStringHelpers.Empty, typeof(ApiKeyCredential)); + public static readonly ParameterProvider TokenCredential = new("credential", FormattableStringHelpers.Empty, typeof(AuthenticationTokenProvider)); } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs index 9a1b11a8a02..8e35ee9e267 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs @@ -271,13 +271,35 @@ private bool TryProcessKnownParameter(Dictionary { var type = parameter.Type; + // WaitUntil — prefer Completed to make long-running samples deterministic. + if (type.Name == "WaitUntil") + { + result[parameter.Name] = new ExampleParameterValue( + parameter.Name, + type, + Static(type).Property("Completed")); + return true; + } + // CancellationToken — skip entirely (we don't set it in samples) - if (type.Equals(typeof(CancellationToken))) + if (parameter.Equals(ScmKnownParameters.CancellationToken)) + { + return true; + } + + // RequestOptions (required) — pass null explicitly to avoid ambiguity, similar + // to old RequestContextRequired handling in AutoRest. + if ((parameter.Equals(ScmKnownParameters.RequestOptions) || parameter.Equals(ScmKnownParameters.OptionalRequestOptions)) && + parameter.DefaultValue == null) { + result[parameter.Name] = new ExampleParameterValue( + parameter.Name, + type, + Null.CastTo(type)); return true; } - // Endpoint (Uri) — produce `new Uri("http://localhost:3000")` or use example value + // Endpoint (Uri) — use provided example value, otherwise a named placeholder like "" if (type.Equals(typeof(Uri)) && parameter.InputParameter is InputEndpointParameter) { var endpointExpr = GetEndpointValue(parameter.Name); @@ -292,8 +314,22 @@ private bool TryProcessKnownParameter(Dictionary return true; } + // Request conditions / match conditions — default to null in samples. + if (type.Name == "RequestConditions" || + type.Name == "MatchConditions" || + type.Equals(ScmCodeModelGenerator.Instance.TypeFactory.MatchConditionsType)) + { + result[parameter.Name] = new ExampleParameterValue( + parameter.Name, + type, + Null.CastTo(type)); + return true; + } + // ApiKeyCredential — produce `new ApiKeyCredential("")` - if (type.Name == "ApiKeyCredential" || type.Name == "AzureKeyCredential") + if (type.Equals(ScmKnownParameters.KeyCredential.Type) || + type.Name == ScmKnownParameters.KeyCredential.Type.Name || + type.Name == "AzureKeyCredential") { result[parameter.Name] = new ExampleParameterValue( parameter.Name, type, New.Instance(type, Literal(""))); @@ -301,7 +337,9 @@ private bool TryProcessKnownParameter(Dictionary } // TokenCredential — produce `new DefaultAzureCredential()` - if (type.Name == "TokenCredential") + if (type.Equals(ScmKnownParameters.TokenCredential.Type) || + type.Name == ScmKnownParameters.TokenCredential.Type.Name || + type.Name == "TokenCredential") { result[parameter.Name] = new ExampleParameterValue( parameter.Name, type, New.Instance(type)); @@ -309,8 +347,7 @@ private bool TryProcessKnownParameter(Dictionary } // ClientOptions — skip (optional, not needed in sample) - if (parameter.Name.EndsWith("Options", StringComparison.OrdinalIgnoreCase) && - parameter.DefaultValue != null) + if (parameter.Name.EndsWith("Options", StringComparison.OrdinalIgnoreCase) && parameter.DefaultValue != null) { return true; } @@ -418,7 +455,7 @@ private InputExampleValue GetEndpointValue(string parameterName) return endpointExampleValue; // Fallback: placeholder - return InputExampleValue.Value(InputPrimitiveType.String, $"http://localhost:3000"); + return InputExampleValue.Value(InputPrimitiveType.String, $"<{parameterName}>"); } /// @@ -525,7 +562,11 @@ private static bool IsInlineParameter(ParameterProvider parameter) return false; // Credentials → out-of-line - if (type.Name == "ApiKeyCredential" || type.Name == "AzureKeyCredential" || + if (type.Equals(ScmKnownParameters.KeyCredential.Type) || + type.Name == ScmKnownParameters.KeyCredential.Type.Name || + type.Name == "AzureKeyCredential" || + type.Equals(ScmKnownParameters.TokenCredential.Type) || + type.Name == ScmKnownParameters.TokenCredential.Type.Name || type.Name == "TokenCredential") return false; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs index c85805768e9..add87324523 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using System.Xml.Linq; using Microsoft.TypeSpec.Generator.ClientModel.Primitives; +using Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples; using Microsoft.TypeSpec.Generator.ClientModel.Snippets; using Microsoft.TypeSpec.Generator.ClientModel.Utilities; using Microsoft.TypeSpec.Generator.EmitterRpc; @@ -40,12 +41,14 @@ public class ScmMethodProviderCollection : IReadOnlyList private IReadOnlyList? _convenienceMethodParameters; private readonly InputPagingServiceMethod? _pagingServiceMethod; private IReadOnlyList? _methods; + private IReadOnlyList? _samples; private readonly bool _generateConvenienceMethod; private ClientProvider Client { get; } protected InputServiceMethod ServiceMethod { get; } protected TypeProvider EnclosingType { get; } public IReadOnlyList MethodProviders => _methods ??= BuildMethods(); + public IReadOnlyList Samples => _samples ??= BuildSamples(); public ScmMethodProvider this[int index] { @@ -108,6 +111,47 @@ protected virtual IReadOnlyList BuildMethods() ]; } + protected virtual IReadOnlyList BuildSamples() + { + if (ServiceMethod.Operation.Examples.Count == 0) + { + return []; + } + + var protocolMethod = MethodProviders.FirstOrDefault(m => + m.Kind == ScmMethodKind.Protocol && + !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async)); + + if (protocolMethod == null || !OperationSample.ShouldGenerateSample(Client, protocolMethod.Signature)) + { + return []; + } + + bool shouldGenerateShortVersion = OperationSample.ShouldGenerateShortVersion(this); + bool shouldGenerateConvenienceSamples = MethodProviders.Any(m => + m.Kind == ScmMethodKind.Convenience && + !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async) && + m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public)); + + List samples = new(); + foreach (var example in ServiceMethod.Operation.Examples) + { + if (!shouldGenerateShortVersion && example.Name == "ShortVersion") + { + continue; + } + + samples.Add(new OperationSample(Client, this, ServiceMethod, example, false, example.Name)); + + if (shouldGenerateConvenienceSamples) + { + samples.Add(new OperationSample(Client, this, ServiceMethod, example, true, example.Name)); + } + } + + return samples; + } + private ScmMethodProvider BuildConvenienceMethod(MethodProvider protocolMethod, bool isAsync) { if (EnclosingType is not ClientProvider client) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs index 9aa629dc7e4..0f753a9ebf9 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs @@ -3,6 +3,9 @@ using System.Collections.Generic; using System.Linq; +using System; +using System.Reflection; +using System.ClientModel.Primitives; using Microsoft.TypeSpec.Generator.ClientModel.Providers; using Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples; using Microsoft.TypeSpec.Generator.Expressions; @@ -193,6 +196,90 @@ public void ParameterMapping_MissingOptionalParam_IsOmitted() Assert.IsFalse(mapping.ContainsKey("tag")); } + [Test] + public void GetEndpointValue_WithoutExample_UsesPlaceholder() + { + var sample = CreateBasicSample("ShortVersion"); + var endpointValue = InvokeGetEndpointValue(sample, "endpoint"); + Assert.AreEqual("", GetRawExampleValue(endpointValue)); + } + + [Test] + public void ParameterMapping_TokenCredential_GeneratesNewInstanceExpression() + { + var sample = CreateSampleWithClientParameterType("credential", new CSharpType(typeof(TokenCredential))); + var mapping = sample.ParameterValueMapping; + + Assert.IsTrue(mapping.ContainsKey("credential")); + Assert.IsNull(mapping["credential"].Value); + var credentialExpression = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["credential"])); + Assert.IsInstanceOf(credentialExpression); + + var newCredential = (NewInstanceExpression)credentialExpression; + Assert.AreEqual(typeof(TokenCredential), newCredential.Type?.FrameworkType); + Assert.AreEqual(0, newCredential.Parameters.Count); + } + + [Test] + public void ParameterMapping_AzureKeyCredential_GeneratesKeyPlaceholderExpression() + { + var sample = CreateSampleWithClientParameterType("credential", new CSharpType(typeof(AzureKeyCredential))); + var mapping = sample.ParameterValueMapping; + + Assert.IsTrue(mapping.ContainsKey("credential")); + Assert.IsNull(mapping["credential"].Value); + var credentialExpression = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["credential"])); + Assert.IsInstanceOf(credentialExpression); + + var newCredential = (NewInstanceExpression)credentialExpression; + Assert.AreEqual(typeof(AzureKeyCredential), newCredential.Type?.FrameworkType); + Assert.AreEqual(1, newCredential.Parameters.Count); + Assert.IsNotInstanceOf(newCredential.Parameters[0]); + } + + [Test] + public void ParameterMapping_WaitUntil_UsesCompletedExpression() + { + var sample = CreateSampleWithClientParameterType("waitUntil", new CSharpType(typeof(WaitUntil))); + var mapping = sample.ParameterValueMapping; + + Assert.IsTrue(mapping.ContainsKey("waitUntil")); + Assert.IsNull(mapping["waitUntil"].Value); + + var expression = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["waitUntil"])); + Assert.IsNotInstanceOf(expression); + } + + [Test] + public void ParameterMapping_RequiredRequestOptions_UsesNullExpression() + { + var sample = CreateSampleWithClientParameterType( + "options", + new CSharpType(typeof(RequestOptions)), + isOptional: false, + defaultValue: null); + var mapping = sample.ParameterValueMapping; + + Assert.IsTrue(mapping.ContainsKey("options")); + Assert.IsNull(mapping["options"].Value); + + var expression = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["options"])); + Assert.IsInstanceOf(expression); + } + + [Test] + public void ParameterMapping_MatchConditions_UsesNullExpression() + { + var sample = CreateSampleWithClientParameterType("conditions", new CSharpType(typeof(MatchConditions))); + var mapping = sample.ParameterValueMapping; + + Assert.IsTrue(mapping.ContainsKey("conditions")); + Assert.IsNull(mapping["conditions"].Value); + + var expression = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["conditions"])); + Assert.IsInstanceOf(expression); + } + // ------------------------------------------------------------------- // GetValueExpressionsForParameters // ------------------------------------------------------------------- @@ -234,6 +321,15 @@ [new InputParameterExample(bodyParam, InputExampleValue.Value(InputPrimitiveType } } + [Test] + public void GetEndpointValue_WithExample_UsesExampleValue() + { + const string endpointUrl = "https://example.contoso.test"; + var sample = CreateSampleWithEndpointExample("ShortVersion", endpointUrl); + var endpointValue = InvokeGetEndpointValue(sample, "endpoint"); + Assert.AreEqual(endpointUrl, GetRawExampleValue(endpointValue)); + } + // ------------------------------------------------------------------- // Sample information (human-readable descriptions) // ------------------------------------------------------------------- @@ -364,5 +460,127 @@ private static (OperationSample sample, ClientProvider rootClient, ClientProvide var sample = new OperationSample(subProvider, methodCollection, subServiceMethod, example, false, exampleKey); return (sample, rootProvider, subProvider); } + + private static OperationSample CreateSubClientSampleWithEndpointExample(string exampleKey, string? endpointExample) + { + var endpointParam = InputFactory.EndpointParameter("endpoint", InputPrimitiveType.Url, isRequired: true); + var parentClient = InputFactory.Client("RootClient", parameters: [endpointParam]); + var subOperation = InputFactory.Operation("DoWork"); + var subServiceMethod = InputFactory.BasicServiceMethod("DoWork", subOperation); + var subClient = InputFactory.Client( + "SubClient", + parent: parentClient, + methods: [subServiceMethod], + initializedBy: InputClientInitializedBy.Parent); + + var exampleParameters = endpointExample is null + ? new List() + : new List + { + new InputParameterExample(endpointParam, InputExampleValue.Value(InputPrimitiveType.Url, endpointExample)) + }; + + MockHelpers.LoadMockGenerator( + clients: () => [parentClient, subClient], + createCSharpTypeCore: inputType => inputType.Equals(InputPrimitiveType.Url) + ? new CSharpType(typeof(Uri)) + : new CSharpType(typeof(string))); + + var subProvider = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(subClient)!; + var methodCollection = new ScmMethodProviderCollection(subServiceMethod, subProvider); + var example = new InputOperationExample(exampleKey, null, exampleParameters, ""); + + return new OperationSample(subProvider, methodCollection, subServiceMethod, example, false, exampleKey); + } + + private static OperationSample CreateSampleWithEndpointExample(string exampleKey, string endpointExample) + { + var endpointParam = InputFactory.EndpointParameter("endpoint", InputPrimitiveType.Url, isRequired: true); + var operation = InputFactory.Operation("TestOperation"); + var serviceMethod = InputFactory.BasicServiceMethod("TestOperation", operation); + var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]); + + MockHelpers.LoadMockGenerator(); + + var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; + var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); + var example = new InputOperationExample( + exampleKey, + null, + [new InputParameterExample(endpointParam, InputExampleValue.Value(InputPrimitiveType.Url, endpointExample))], + ""); + + return new OperationSample(client, methodCollection, serviceMethod, example, false, exampleKey); + } + + private static OperationSample CreateSampleWithClientParameterType( + string parameterName, + CSharpType mappedType, + bool isOptional = false, + InputConstant? defaultValue = null) + { + var clientParameter = InputFactory.QueryParameter( + parameterName, + InputPrimitiveType.String, + isRequired: !isOptional, + defaultValue: defaultValue); + var operation = InputFactory.Operation("DoWork"); + var serviceMethod = InputFactory.BasicServiceMethod("DoWork", operation); + var inputClient = InputFactory.Client("TypedClient", parameters: [clientParameter], methods: [serviceMethod]); + + MockHelpers.LoadMockGenerator( + clients: () => [inputClient], + createCSharpTypeCore: _ => mappedType); + + var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; + var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); + var example = new InputOperationExample("ShortVersion", null, [], ""); + + return new OperationSample(client, methodCollection, serviceMethod, example, false, "ShortVersion"); + } + + private sealed class TokenCredential + { + } + + private sealed class AzureKeyCredential + { + } + + private sealed class WaitUntil + { + public static WaitUntil Completed { get; } = new WaitUntil(); + } + + private sealed class MatchConditions + { + } + + private static object? GetRawExampleValue(InputExampleValue value) + { + var rawValueProperty = value.GetType().GetProperty("RawValue"); + return rawValueProperty?.GetValue(value); + } + + private static ValueExpression UnwrapCast(ValueExpression expression) + { + while (expression is CastExpression castExpression) + { + expression = castExpression.Inner; + } + + return expression; + } + + private static InputExampleValue InvokeGetEndpointValue(OperationSample sample, string parameterName) + { + var method = typeof(OperationSample).GetMethod("GetEndpointValue", BindingFlags.Instance | BindingFlags.NonPublic); + Assert.IsNotNull(method); + + var value = method!.Invoke(sample, [parameterName]); + Assert.IsNotNull(value); + + return (InputExampleValue)value!; + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs index b54ece36bd0..d23513876fe 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs @@ -6,10 +6,12 @@ using System.ClientModel.Primitives; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.TypeSpec.Generator.ClientModel.Providers; +using Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples; using Microsoft.TypeSpec.Generator.Input; using Microsoft.TypeSpec.Generator.Input.Extensions; using Microsoft.TypeSpec.Generator.Primitives; @@ -1996,5 +1998,82 @@ public void ConvenienceMethod_JsonListBody_DoesNotUseXmlFromEnumerable() Assert.IsFalse(methodBody.Contains("rootNameHint")); Assert.IsFalse(methodBody.Contains("childNameHint")); } + + [Test] + public void Samples_AttachesProtocolAndConvenienceSamplesForEachExample() + { + var shortVersion = new InputOperationExample("ShortVersion", null, [], ""); + var allParameters = new InputOperationExample("AllParameters", null, [], ""); + var operation = InputFactory.Operation("GetWidget"); + SetOperationExamples(operation, [shortVersion, allParameters]); + var serviceMethod = InputFactory.BasicServiceMethod("GetWidget", operation); + var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]); + + MockHelpers.LoadMockGenerator(clients: () => [inputClient]); + + var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; + var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); + + var expectedCount = OperationSample.ShouldGenerateShortVersion(methodCollection) ? 4 : 3; + Assert.AreEqual(expectedCount, methodCollection.Samples.Count); + Assert.IsTrue(methodCollection.Samples.Any(s => !s.IsConvenienceSample && s.ExampleKey == "AllParameters")); + Assert.IsTrue(methodCollection.Samples.Any(s => s.IsConvenienceSample && s.ExampleKey == "AllParameters")); + Assert.IsTrue(methodCollection.Samples.Any(s => s.IsConvenienceSample && s.ExampleKey == "ShortVersion") || + !OperationSample.ShouldGenerateShortVersion(methodCollection)); + + if (OperationSample.ShouldGenerateShortVersion(methodCollection)) + { + Assert.IsTrue(methodCollection.Samples.Any(s => !s.IsConvenienceSample && s.ExampleKey == "ShortVersion")); + } + else + { + Assert.IsFalse(methodCollection.Samples.Any(s => !s.IsConvenienceSample && s.ExampleKey == "ShortVersion")); + } + } + + [Test] + public void Samples_DoesNotAttachConvenienceSamplesWhenConvenienceMethodIsDisabled() + { + var operation = InputFactory.Operation("GetWidget", generateConvenienceMethod: false); + SetOperationExamples( + operation, + [ + new InputOperationExample("ShortVersion", null, [], ""), + new InputOperationExample("AllParameters", null, [], "") + ]); + var serviceMethod = InputFactory.BasicServiceMethod("GetWidget", operation); + var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]); + + MockHelpers.LoadMockGenerator(clients: () => [inputClient]); + + var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; + var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); + + Assert.AreEqual(2, methodCollection.Samples.Count); + Assert.IsTrue(methodCollection.Samples.All(s => !s.IsConvenienceSample)); + } + + [Test] + public void Samples_WithoutExamples_ReturnsEmpty() + { + var operation = InputFactory.Operation("GetWidget"); + var serviceMethod = InputFactory.BasicServiceMethod("GetWidget", operation); + var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]); + + MockHelpers.LoadMockGenerator(clients: () => [inputClient]); + + var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; + var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); + + Assert.IsEmpty(methodCollection.Samples); + } + + private static void SetOperationExamples(InputOperation operation, IReadOnlyList examples) + { + var setter = typeof(InputOperation) + .GetProperty(nameof(InputOperation.Examples), BindingFlags.Instance | BindingFlags.Public)! + .GetSetMethod(nonPublic: true)!; + setter.Invoke(operation, [examples]); + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ScmKnownParametersTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ScmKnownParametersTests.cs index 40e7c9e92bd..06b74024358 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ScmKnownParametersTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ScmKnownParametersTests.cs @@ -4,6 +4,7 @@ using System; using Microsoft.TypeSpec.Generator.ClientModel.Primitives; using Microsoft.TypeSpec.Generator.Primitives; +using Microsoft.TypeSpec.Generator.Providers; using NUnit.Framework; using static Microsoft.TypeSpec.Generator.Snippets.Snippet; @@ -40,5 +41,17 @@ public void RepeatabilityFirstSentParamHasDefaultValue() var expectedDefaultValue = Static(typeof(DateTimeOffset)).Property(nameof(DateTimeOffset.Now)); Assert.AreEqual(expectedDefaultValue, parameter.DefaultValue); } + + [Test] + public void KeyCredentialHasExpectedType() + { + Assert.AreEqual("ApiKeyCredential", ScmKnownParameters.KeyCredential.Type.Name); + } + + [Test] + public void TokenCredentialHasExpectedType() + { + Assert.AreEqual("AuthenticationTokenProvider", ScmKnownParameters.TokenCredential.Type.Name); + } } } From 07e1226d14d567261b88d14e14fce6ba5de239ec Mon Sep 17 00:00:00 2001 From: LazyLinh2 Date: Tue, 19 May 2026 14:26:49 -0700 Subject: [PATCH 09/10] Change TokenCredential to add DefaultAzureCredential for parity with autorest --- .../Samples/ExampleValueExpressionBuilder.cs | 79 +++- .../src/Providers/Samples/OperationSample.cs | 12 +- .../Providers/Samples/OperationSampleTests.cs | 385 +++++------------- 3 files changed, 174 insertions(+), 302 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleValueExpressionBuilder.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleValueExpressionBuilder.cs index 0b37e91f330..a2c906c9080 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleValueExpressionBuilder.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/ExampleValueExpressionBuilder.cs @@ -4,11 +4,13 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using System.Xml; using Microsoft.TypeSpec.Generator.Expressions; using Microsoft.TypeSpec.Generator.Input; using Microsoft.TypeSpec.Generator.Primitives; +using Microsoft.TypeSpec.Generator.Providers; using static Microsoft.TypeSpec.Generator.Snippets.Snippet; namespace Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples @@ -24,13 +26,13 @@ public static class ExampleValueExpressionBuilder /// If the parameter has a pre-built expression, returns it directly. /// Otherwise, converts the raw using type information. /// - public static ValueExpression GetExpression(ExampleParameterValue parameterValue) + public static ValueExpression GetExpression(ExampleParameterValue parameterValue, SerializationFormat format = SerializationFormat.Default) { if (parameterValue.Expression != null) return parameterValue.Expression; if (parameterValue.Value != null) - return GetExpression(parameterValue.Type, parameterValue.Value); + return GetExpression(parameterValue.Type, parameterValue.Value, format); return Default; } @@ -38,7 +40,7 @@ public static ValueExpression GetExpression(ExampleParameterValue parameterValue /// /// Converts an to a C# expression based on the target . /// - public static ValueExpression GetExpression(CSharpType type, InputExampleValue exampleValue) + public static ValueExpression GetExpression(CSharpType type, InputExampleValue exampleValue, SerializationFormat format = SerializationFormat.Default) { if (type.IsList) return GetExpressionForList(type, exampleValue); @@ -47,13 +49,13 @@ public static ValueExpression GetExpression(CSharpType type, InputExampleValue e if (type.IsEnum) return GetExpressionForEnum(type, exampleValue); if (type is { IsFrameworkType: true }) - return GetExpressionForFrameworkType(type.FrameworkType, exampleValue); + return GetExpressionForFrameworkType(type.FrameworkType, exampleValue, format); // For model types, fall back to default return GetExpressionForModel(type, exampleValue); } - private static ValueExpression GetExpressionForFrameworkType(Type frameworkType, InputExampleValue exampleValue) + private static ValueExpression GetExpressionForFrameworkType(Type frameworkType, InputExampleValue exampleValue, SerializationFormat format = SerializationFormat.Default) { var rawValue = GetRawValue(exampleValue); @@ -136,6 +138,11 @@ private static ValueExpression GetExpressionForFrameworkType(Type frameworkType, // DateTimeOffset if (frameworkType == typeof(DateTimeOffset)) { + if (format == SerializationFormat.DateTime_Unix) + { + var unixValue = rawValue is string us ? Convert.ToInt64(us) : rawValue is int or long ? Convert.ToInt64(rawValue) : 0L; + return Static(typeof(DateTimeOffset)).Invoke(nameof(DateTimeOffset.FromUnixTimeSeconds), Literal(unixValue)); + } if (rawValue is string s) return Static(typeof(DateTimeOffset)).Invoke(nameof(DateTimeOffset.Parse), Literal(s)); if (rawValue is int or long) @@ -146,6 +153,13 @@ private static ValueExpression GetExpressionForFrameworkType(Type frameworkType, // TimeSpan if (frameworkType == typeof(TimeSpan)) { + if (format is SerializationFormat.Duration_Seconds or SerializationFormat.Duration_Seconds_Float or SerializationFormat.Duration_Milliseconds) + { + if (rawValue is string ds) + return Static(typeof(TimeSpan)).Invoke(nameof(TimeSpan.FromSeconds), Literal(Convert.ToDouble(ds))); + if (rawValue is int or float or double) + return Static(typeof(TimeSpan)).Invoke(nameof(TimeSpan.FromSeconds), Literal(Convert.ToDouble(rawValue))); + } if (rawValue is string s) return Static(typeof(XmlConvert)).Invoke(nameof(XmlConvert.ToTimeSpan), Literal(s)); if (rawValue is int or float or double) @@ -231,10 +245,61 @@ private static ValueExpression GetExpressionForEnum(CSharpType enumType, InputEx private static ValueExpression GetExpressionForModel(CSharpType type, InputExampleValue exampleValue) { - // For model types, generate new ModelType() — will be enhanced in M3/M4 - // with constructor parameter analysis if (type.IsValueType) return Default; + + // Try to resolve the model's TypeProvider to get constructor parameters + if (exampleValue is InputExampleObjectValue objectValue && + CodeModelGenerator.Instance.TypeFactory.CSharpTypeMap.TryGetValue(type, out var typeProvider) && + typeProvider is ModelProvider modelProvider) + { + // Find the public constructor with the most parameters + ConstructorProvider? bestCtor = null; + foreach (var ctor in modelProvider.Constructors) + { + if (ctor.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public)) + { + if (bestCtor == null || ctor.Signature.Parameters.Count > bestCtor.Signature.Parameters.Count) + bestCtor = ctor; + } + } + + if (bestCtor != null && bestCtor.Signature.Parameters.Count > 0) + { + var arguments = new List(); + foreach (var param in bestCtor.Signature.Parameters) + { + InputExampleValue? matchedValue = null; + + // Try matching by parameter name + objectValue.Values.TryGetValue(param.Name, out matchedValue); + + // Try matching by wire serialized name + if (matchedValue == null && param.Property?.WireInfo?.SerializedName != null) + objectValue.Values.TryGetValue(param.Property.WireInfo.SerializedName, out matchedValue); + + // Try matching by property name + if (matchedValue == null && param.Property != null) + objectValue.Values.TryGetValue(param.Property.Name, out matchedValue); + + if (matchedValue != null) + { + arguments.Add(GetExpression(param.Type, matchedValue)); + } + else if (param.DefaultValue != null) + { + arguments.Add(param.DefaultValue); + } + else + { + arguments.Add(param.Type.IsValueType ? Default : Null); + } + } + + return New.Instance(type, [.. arguments]); + } + } + return New.Instance(type); } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs index 8e35ee9e267..9d8bdded510 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs @@ -146,7 +146,9 @@ private IReadOnlyList BuildClientInvocationChain() { var callChain = new Stack(); - // Walk from current client up to root, collecting factory methods + // Walk from the current client up to root, collecting factory methods. + // For each client that has a parent, find the factory method on the parent + // that returns it, and push that onto the chain. var currentInputClient = _client.InputClient; while (currentInputClient.Parent != null) { @@ -337,12 +339,15 @@ private bool TryProcessKnownParameter(Dictionary } // TokenCredential — produce `new DefaultAzureCredential()` + // TokenCredential is abstract, so we must use a concrete type. if (type.Equals(ScmKnownParameters.TokenCredential.Type) || type.Name == ScmKnownParameters.TokenCredential.Type.Name || type.Name == "TokenCredential") { + // Use FormattableStringExpression because DefaultAzureCredential + // lives in Azure.Identity which the generator doesn't reference. result[parameter.Name] = new ExampleParameterValue( - parameter.Name, type, New.Instance(type)); + parameter.Name, type, new FormattableStringExpression("new DefaultAzureCredential()", [])); return true; } @@ -517,7 +522,8 @@ public IEnumerable GetValueExpressionsForParameters( if (ParameterValueMapping.TryGetValue(parameter.Name, out var exampleValue)) { - parameterExpression = ExampleValueExpressionBuilder.GetExpression(exampleValue); + var format = parameter.WireInfo?.SerializationFormat ?? SerializationFormat.Default; + parameterExpression = ExampleValueExpressionBuilder.GetExpression(exampleValue, format); } else { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs index 0f753a9ebf9..dcec8a22bbf 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/Samples/OperationSampleTests.cs @@ -5,13 +5,12 @@ using System.Linq; using System; using System.Reflection; -using System.ClientModel.Primitives; using Microsoft.TypeSpec.Generator.ClientModel.Providers; using Microsoft.TypeSpec.Generator.ClientModel.Providers.Samples; using Microsoft.TypeSpec.Generator.Expressions; using Microsoft.TypeSpec.Generator.Input; using Microsoft.TypeSpec.Generator.Primitives; -using Microsoft.TypeSpec.Generator.Statements; +using Microsoft.TypeSpec.Generator.Snippets; using Microsoft.TypeSpec.Generator.Tests.Common; using NUnit.Framework; @@ -26,88 +25,71 @@ public void SetUp() } // ------------------------------------------------------------------- - // Core identity properties + // TokenCredential → new DefaultAzureCredential() // ------------------------------------------------------------------- [Test] - public void ExampleKey_PreservedFromInput() + public void TokenCredential_ProducesDefaultAzureCredential() { - var sample = CreateBasicSample("ShortVersion"); - Assert.AreEqual("ShortVersion", sample.ExampleKey); - } - - [Test] - public void IsAllParametersUsed_TrueForAllParameters() - { - var sample = CreateBasicSample("AllParameters"); - Assert.IsTrue(sample.IsAllParametersUsed); - } + var sample = CreateSampleWithClientParameterType("credential", new CSharpType(typeof(TokenCredential))); + var mapping = sample.ParameterValueMapping; - [Test] - public void IsAllParametersUsed_FalseForShortVersion() - { - var sample = CreateBasicSample("ShortVersion"); - Assert.IsFalse(sample.IsAllParametersUsed); - } + Assert.IsTrue(mapping.ContainsKey("credential")); - [Test] - public void IsConvenienceSample_PreservedFromInput() - { - var sample = CreateBasicSample("ShortVersion", isConvenience: true); - Assert.IsTrue(sample.IsConvenienceSample); - } + // The expression should be a FormattableStringExpression containing "new DefaultAzureCredential()" + // (not "new TokenCredential()" which would fail because TokenCredential is abstract) + var expr = ExampleValueExpressionBuilder.GetExpression(mapping["credential"]); + Assert.IsInstanceOf(expr); - [Test] - public void InputOperationName_PreservedFromServiceMethod() - { - var sample = CreateBasicSample("ShortVersion"); - Assert.AreEqual("TestOperation", sample.InputOperationName); - } - - [Test] - public void ResourceName_FallsBackToClientName() - { - var sample = CreateBasicSample("ShortVersion"); - // InputFactory.Operation sets ResourceName to null, - // so falls back to client name - Assert.AreEqual("TestClient", sample.ResourceName); + // Verify the format string is exactly "new DefaultAzureCredential()" via reflection + // (Format is private, but this is the key parity assertion) + var formatProp = typeof(FormattableStringExpression).GetProperty("Format", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.IsNotNull(formatProp, "Format property should exist on FormattableStringExpression"); + Assert.AreEqual("new DefaultAzureCredential()", formatProp!.GetValue(expr)); } // ------------------------------------------------------------------- - // Operation type detection + // ApiKeyCredential → new AzureKeyCredential("") // ------------------------------------------------------------------- [Test] - public void IsPageable_FalseForBasicMethod() + public void ApiKeyCredential_ProducesNewWithKeyPlaceholder() { - var sample = CreateBasicSample("ShortVersion"); - Assert.IsFalse(sample.IsPageable); - } + var sample = CreateSampleWithClientParameterType("credential", new CSharpType(typeof(AzureKeyCredential))); + var mapping = sample.ParameterValueMapping; - [Test] - public void IsPageable_TrueForPagingMethod() - { - var operation = InputFactory.Operation("ListItems"); - var pagingMetadata = InputFactory.PagingMetadata(["items"], null, null); - var serviceMethod = InputFactory.PagingServiceMethod("ListItems", operation, pagingMetadata: pagingMetadata); + Assert.IsTrue(mapping.ContainsKey("credential")); - // Verify the service method is recognized as paging - Assert.IsInstanceOf(serviceMethod); + var expr = Unwrap(UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["credential"]))); + Assert.IsInstanceOf(expr); + + var newExpr = (NewInstanceExpression)expr; + Assert.AreEqual(1, newExpr.Parameters.Count); + // The argument should be the literal "" + var argExpr = Unwrap(newExpr.Parameters[0]); + Assert.IsInstanceOf(argExpr); + Assert.AreEqual("", ((LiteralExpression)argExpr).Literal); } + // ------------------------------------------------------------------- + // Endpoint value resolution + // ------------------------------------------------------------------- + [Test] - public void IsLongRunning_FalseForBasicMethod() + public void Endpoint_WithoutExample_UsesPlaceholder() { var sample = CreateBasicSample("ShortVersion"); - Assert.IsFalse(sample.IsLongRunning); + var endpointValue = InvokeGetEndpointValue(sample, "endpoint"); + Assert.AreEqual("", GetRawExampleValue(endpointValue)); } [Test] - public void HasResponseBody_FalseForVoidReturn() + public void Endpoint_WithExample_UsesProvidedUrl() { - var sample = CreateBasicSample("ShortVersion"); - // Basic operation with no response type → no body - Assert.IsFalse(sample.HasResponseBody); + const string url = "https://example.contoso.test"; + var sample = CreateSampleWithEndpointExample("ShortVersion", url); + var endpointValue = InvokeGetEndpointValue(sample, "endpoint"); + Assert.AreEqual(url, GetRawExampleValue(endpointValue)); } // ------------------------------------------------------------------- @@ -115,7 +97,7 @@ public void HasResponseBody_FalseForVoidReturn() // ------------------------------------------------------------------- [Test] - public void ClientInvocationChain_RootClient_HasSingleConstructor() + public void RootClient_ChainHasConstructor() { var sample = CreateBasicSample("ShortVersion"); var chain = sample.ClientInvocationChain; @@ -129,22 +111,22 @@ public void ClientInvocationChain_RootClient_HasSingleConstructor() // ------------------------------------------------------------------- [Test] - public void ClientInvocationChain_SubClient_HasConstructorAndFactory() + public void SubClient_ChainHasConstructorThenFactory() { var (sample, _, _) = CreateSubClientSample("ShortVersion"); var chain = sample.ClientInvocationChain; Assert.IsTrue(chain.Count >= 2); - Assert.IsInstanceOf(chain[0]); - Assert.IsInstanceOf(chain[1]); + Assert.IsInstanceOf(chain[0], "First element should be root ctor"); + Assert.IsInstanceOf(chain[1], "Second element should be factory method"); } // ------------------------------------------------------------------- - // Parameter value mapping + // Parameter mapping // ------------------------------------------------------------------- [Test] - public void ParameterMapping_SpecProvidedValue_IsMapped() + public void ParameterMapping_ExampleValue_IsMapped() { var bodyParam = InputFactory.MethodParameter("message", InputPrimitiveType.String, isRequired: true); var example = new InputOperationExample( @@ -163,17 +145,19 @@ [new InputParameterExample(bodyParam, InputExampleValue.Value(InputPrimitiveType var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); - - // Use convenience sample so the parameter name "message" is preserved var sample = new OperationSample(client, methodCollection, serviceMethod, example, true, "ShortVersion"); - var mapping = sample.ParameterValueMapping; + var mapping = sample.ParameterValueMapping; Assert.IsTrue(mapping.ContainsKey("message")); Assert.IsNotNull(mapping["message"].Value); } + // ------------------------------------------------------------------- + // Optional params excluded from ShortVersion + // ------------------------------------------------------------------- + [Test] - public void ParameterMapping_MissingOptionalParam_IsOmitted() + public void ParameterMapping_OptionalParam_ExcludedFromShortVersion() { var optionalParam = InputFactory.MethodParameter("tag", InputPrimitiveType.String, isRequired: false, defaultValue: InputFactory.Constant.String("default")); @@ -189,226 +173,98 @@ public void ParameterMapping_MissingOptionalParam_IsOmitted() var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); - var sample = new OperationSample(client, methodCollection, serviceMethod, example, true, "ShortVersion"); - var mapping = sample.ParameterValueMapping; - - Assert.IsFalse(mapping.ContainsKey("tag")); - } - [Test] - public void GetEndpointValue_WithoutExample_UsesPlaceholder() - { - var sample = CreateBasicSample("ShortVersion"); - var endpointValue = InvokeGetEndpointValue(sample, "endpoint"); - Assert.AreEqual("", GetRawExampleValue(endpointValue)); + Assert.IsFalse(sample.ParameterValueMapping.ContainsKey("tag")); } - [Test] - public void ParameterMapping_TokenCredential_GeneratesNewInstanceExpression() - { - var sample = CreateSampleWithClientParameterType("credential", new CSharpType(typeof(TokenCredential))); - var mapping = sample.ParameterValueMapping; - - Assert.IsTrue(mapping.ContainsKey("credential")); - Assert.IsNull(mapping["credential"].Value); - var credentialExpression = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["credential"])); - Assert.IsInstanceOf(credentialExpression); - - var newCredential = (NewInstanceExpression)credentialExpression; - Assert.AreEqual(typeof(TokenCredential), newCredential.Type?.FrameworkType); - Assert.AreEqual(0, newCredential.Parameters.Count); - } - - [Test] - public void ParameterMapping_AzureKeyCredential_GeneratesKeyPlaceholderExpression() - { - var sample = CreateSampleWithClientParameterType("credential", new CSharpType(typeof(AzureKeyCredential))); - var mapping = sample.ParameterValueMapping; - - Assert.IsTrue(mapping.ContainsKey("credential")); - Assert.IsNull(mapping["credential"].Value); - var credentialExpression = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["credential"])); - Assert.IsInstanceOf(credentialExpression); - - var newCredential = (NewInstanceExpression)credentialExpression; - Assert.AreEqual(typeof(AzureKeyCredential), newCredential.Type?.FrameworkType); - Assert.AreEqual(1, newCredential.Parameters.Count); - Assert.IsNotInstanceOf(newCredential.Parameters[0]); - } + // ------------------------------------------------------------------- + // WaitUntil → .Completed + // ------------------------------------------------------------------- [Test] - public void ParameterMapping_WaitUntil_UsesCompletedExpression() + public void WaitUntil_ProducesCompletedMemberAccess() { var sample = CreateSampleWithClientParameterType("waitUntil", new CSharpType(typeof(WaitUntil))); var mapping = sample.ParameterValueMapping; Assert.IsTrue(mapping.ContainsKey("waitUntil")); - Assert.IsNull(mapping["waitUntil"].Value); - - var expression = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["waitUntil"])); - Assert.IsNotInstanceOf(expression); + var expr = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["waitUntil"])); + // Should be a property access like WaitUntil.Completed + Assert.IsInstanceOf(expr); } + // ------------------------------------------------------------------- + // RequestOptions (required) → null + // ------------------------------------------------------------------- + [Test] - public void ParameterMapping_RequiredRequestOptions_UsesNullExpression() + public void RequiredRequestOptions_ProducesNull() { var sample = CreateSampleWithClientParameterType( "options", - new CSharpType(typeof(RequestOptions)), + new CSharpType(typeof(System.ClientModel.Primitives.RequestOptions)), isOptional: false, defaultValue: null); var mapping = sample.ParameterValueMapping; Assert.IsTrue(mapping.ContainsKey("options")); - Assert.IsNull(mapping["options"].Value); - - var expression = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["options"])); - Assert.IsInstanceOf(expression); + var expr = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["options"])); + Assert.IsInstanceOf(expr); } + // ------------------------------------------------------------------- + // MatchConditions → null + // ------------------------------------------------------------------- + [Test] - public void ParameterMapping_MatchConditions_UsesNullExpression() + public void MatchConditions_ProducesNull() { var sample = CreateSampleWithClientParameterType("conditions", new CSharpType(typeof(MatchConditions))); var mapping = sample.ParameterValueMapping; Assert.IsTrue(mapping.ContainsKey("conditions")); - Assert.IsNull(mapping["conditions"].Value); - - var expression = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["conditions"])); - Assert.IsInstanceOf(expression); + var expr = UnwrapCast(ExampleValueExpressionBuilder.GetExpression(mapping["conditions"])); + Assert.IsInstanceOf(expr); } // ------------------------------------------------------------------- - // GetValueExpressionsForParameters + // ShouldGenerateShortVersion // ------------------------------------------------------------------- [Test] - public void GetValueExpressions_ProducesExpressions() + public void ShouldGenerateShortVersion_TrueWhenNoConvenience() { - var bodyParam = InputFactory.MethodParameter("count", InputPrimitiveType.Int32, isRequired: true); - var example = new InputOperationExample( - "ShortVersion", - null, - [new InputParameterExample(bodyParam, InputExampleValue.Value(InputPrimitiveType.Int32, 42))], - ""); - - var operation = InputFactory.Operation("DoStuff", - parameters: [InputFactory.BodyParameter("count", InputPrimitiveType.Int32, isRequired: true)]); - var serviceMethod = InputFactory.BasicServiceMethod("DoStuff", operation, parameters: [bodyParam]); - var inputClient = InputFactory.Client("StuffClient", methods: [serviceMethod]); - - MockHelpers.LoadMockGenerator( - createCSharpTypeCore: (inputType) => new CSharpType(typeof(int))); + var operation = InputFactory.Operation("TestOp", generateConvenienceMethod: false); + var serviceMethod = InputFactory.BasicServiceMethod("TestOp", operation); + var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]); var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); - var sample = new OperationSample(client, methodCollection, serviceMethod, example, false, "ShortVersion"); - - var declarations = new List(); - var syncProtocol = methodCollection.MethodProviders - .FirstOrDefault(m => m.Kind == ScmMethodKind.Protocol && - !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async)); - - if (syncProtocol != null) - { - var expressions = sample.GetValueExpressionsForParameters( - syncProtocol.Signature.Parameters, declarations).ToList(); - - Assert.IsTrue(expressions.Count > 0); - } - } - - [Test] - public void GetEndpointValue_WithExample_UsesExampleValue() - { - const string endpointUrl = "https://example.contoso.test"; - var sample = CreateSampleWithEndpointExample("ShortVersion", endpointUrl); - var endpointValue = InvokeGetEndpointValue(sample, "endpoint"); - Assert.AreEqual(endpointUrl, GetRawExampleValue(endpointValue)); + Assert.IsTrue(OperationSample.ShouldGenerateShortVersion(methodCollection)); } // ------------------------------------------------------------------- - // Sample information (human-readable descriptions) + // Sample metadata // ------------------------------------------------------------------- [Test] - public void GetSampleInformation_Sync_Convenience() + public void SampleInformation_ContainsMethodName() { var sample = CreateBasicSample("ShortVersion", isConvenience: true); var info = sample.GetSampleInformation(false); Assert.IsTrue(info.Contains("how to call")); - Assert.IsFalse(info.Contains("Async")); } [Test] - public void GetSampleInformation_Async_AppendsAsync() + public void SampleInformation_AsyncAppendsAsync() { var sample = CreateBasicSample("ShortVersion", isConvenience: true); var info = sample.GetSampleInformation(true); Assert.IsTrue(info.Contains("Async")); } - [Test] - public void GetSampleInformation_AllParameters_IncludesAllParameters() - { - var sample = CreateBasicSample("AllParameters", isConvenience: true); - var info = sample.GetSampleInformation(false); - Assert.IsTrue(info.Contains("all parameters")); - } - - // ------------------------------------------------------------------- - // Static helper: ShouldGenerateSample - // ------------------------------------------------------------------- - - [Test] - public void ShouldGenerateSample_PublicMethod_ReturnsTrue() - { - var operation = InputFactory.Operation("TestOp"); - var serviceMethod = InputFactory.BasicServiceMethod("TestOp", operation); - var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]); - - var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; - var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); - - var syncProtocol = methodCollection.MethodProviders - .FirstOrDefault(m => m.Kind == ScmMethodKind.Protocol && - !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Async)); - - if (syncProtocol != null) - { - Assert.IsTrue(OperationSample.ShouldGenerateSample(client, syncProtocol.Signature)); - } - } - - [Test] - public void ShouldGenerateSample_SubClient_ReturnsTrue() - { - var (_, _, subProvider) = CreateSubClientSample("ShortVersion"); - - // Verify that subclients are identified correctly - Assert.IsTrue(subProvider.InputClient.Parent != null); - } - - // ------------------------------------------------------------------- - // Static helper: ShouldGenerateShortVersion - // ------------------------------------------------------------------- - - [Test] - public void ShouldGenerateShortVersion_NoConvenience_ReturnsTrue() - { - var operation = InputFactory.Operation("TestOp", generateConvenienceMethod: false); - var serviceMethod = InputFactory.BasicServiceMethod("TestOp", operation); - var inputClient = InputFactory.Client("TestClient", methods: [serviceMethod]); - - var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; - var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); - - Assert.IsTrue(OperationSample.ShouldGenerateShortVersion(methodCollection)); - } - // ------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------- @@ -426,20 +282,6 @@ private static OperationSample CreateBasicSample(string exampleKey, bool isConve return new OperationSample(client, methodCollection, serviceMethod, example, isConvenience, exampleKey); } - private static OperationSample CreatePagingSample(string exampleKey) - { - var operation = InputFactory.Operation("ListItems"); - var pagingMetadata = InputFactory.PagingMetadata(["items"], null, null); - var serviceMethod = InputFactory.PagingServiceMethod("ListItems", operation, pagingMetadata: pagingMetadata); - var inputClient = InputFactory.Client("PagingClient", methods: [serviceMethod]); - - var client = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(inputClient)!; - var methodCollection = new ScmMethodProviderCollection(serviceMethod, client); - var example = new InputOperationExample(exampleKey, null, [], ""); - - return new OperationSample(client, methodCollection, serviceMethod, example, false, exampleKey); - } - private static (OperationSample sample, ClientProvider rootClient, ClientProvider subClient) CreateSubClientSample(string exampleKey) { var endpointParam = InputFactory.EndpointParameter("endpoint", InputPrimitiveType.Url, isRequired: true); @@ -461,38 +303,6 @@ private static (OperationSample sample, ClientProvider rootClient, ClientProvide return (sample, rootProvider, subProvider); } - private static OperationSample CreateSubClientSampleWithEndpointExample(string exampleKey, string? endpointExample) - { - var endpointParam = InputFactory.EndpointParameter("endpoint", InputPrimitiveType.Url, isRequired: true); - var parentClient = InputFactory.Client("RootClient", parameters: [endpointParam]); - var subOperation = InputFactory.Operation("DoWork"); - var subServiceMethod = InputFactory.BasicServiceMethod("DoWork", subOperation); - var subClient = InputFactory.Client( - "SubClient", - parent: parentClient, - methods: [subServiceMethod], - initializedBy: InputClientInitializedBy.Parent); - - var exampleParameters = endpointExample is null - ? new List() - : new List - { - new InputParameterExample(endpointParam, InputExampleValue.Value(InputPrimitiveType.Url, endpointExample)) - }; - - MockHelpers.LoadMockGenerator( - clients: () => [parentClient, subClient], - createCSharpTypeCore: inputType => inputType.Equals(InputPrimitiveType.Url) - ? new CSharpType(typeof(Uri)) - : new CSharpType(typeof(string))); - - var subProvider = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(subClient)!; - var methodCollection = new ScmMethodProviderCollection(subServiceMethod, subProvider); - var example = new InputOperationExample(exampleKey, null, exampleParameters, ""); - - return new OperationSample(subProvider, methodCollection, subServiceMethod, example, false, exampleKey); - } - private static OperationSample CreateSampleWithEndpointExample(string exampleKey, string endpointExample) { var endpointParam = InputFactory.EndpointParameter("endpoint", InputPrimitiveType.Url, isRequired: true); @@ -539,22 +349,14 @@ private static OperationSample CreateSampleWithClientParameterType( return new OperationSample(client, methodCollection, serviceMethod, example, false, "ShortVersion"); } - private sealed class TokenCredential - { - } - - private sealed class AzureKeyCredential - { - } - + // Stub types for credential/LRO tests (matched by type name in TryProcessKnownParameter) + private sealed class TokenCredential { } + private sealed class AzureKeyCredential { } private sealed class WaitUntil { public static WaitUntil Completed { get; } = new WaitUntil(); } - - private sealed class MatchConditions - { - } + private sealed class MatchConditions { } private static object? GetRawExampleValue(InputExampleValue value) { @@ -565,21 +367,20 @@ private sealed class MatchConditions private static ValueExpression UnwrapCast(ValueExpression expression) { while (expression is CastExpression castExpression) - { expression = castExpression.Inner; - } - return expression; } + /// Unwrap ScopedApi wrappers to get the underlying expression. + private static ValueExpression Unwrap(ValueExpression expr) + => expr is ScopedApi scoped ? scoped.Original : expr; + private static InputExampleValue InvokeGetEndpointValue(OperationSample sample, string parameterName) { var method = typeof(OperationSample).GetMethod("GetEndpointValue", BindingFlags.Instance | BindingFlags.NonPublic); Assert.IsNotNull(method); - var value = method!.Invoke(sample, [parameterName]); Assert.IsNotNull(value); - return (InputExampleValue)value!; } } From 2350208a4084387f327dfee619b78484b1e95d81 Mon Sep 17 00:00:00 2001 From: LazyLinh2 Date: Tue, 19 May 2026 15:27:07 -0700 Subject: [PATCH 10/10] fix spelling --- .../src/Providers/Samples/OperationSample.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs index 9d8bdded510..292a1882acc 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Samples/OperationSample.cs @@ -681,7 +681,7 @@ public static bool ShouldGenerateShortVersion(ScmMethodProviderCollection method var convenienceParams = convenienceMethod.Signature.Parameters; // If the convenience method has one fewer parameter (e.g., no CancellationToken) - // and all overlapping parameter types match, skip — they're effectively identica. + // and all overlapping parameter types match, skip — they're effectively identical. if (convenienceParams.Count == protocolParams.Count - 1 && convenienceParams.Count > 0 && !convenienceParams.Last().Type.Equals(typeof(CancellationToken))) {