From aa049aa61c5c3b92daf51c2006d05de253b05276 Mon Sep 17 00:00:00 2001 From: Dapeng Zhang Date: Tue, 21 Feb 2023 10:29:34 +0800 Subject: [PATCH] Support `@projectedName` (#3147) * fix a typo * support projected name * update shared code * remove an unused function * the generation is incorrect * fixed issue * add cadl ranch test cases * regenerate * move the constants to a standalone file * fix prettier --------- Co-authored-by: Mingzhe Huang --- eng/Generate.ps1 | 3 +- .../Input/CadlInputModelPropertyConverter.cs | 2 +- .../Properties/launchSettings.json | 4 + .../Emitter.Csharp/src/constants.ts | 6 + .../Emitter.Csharp/src/emitter.ts | 170 +++---- .../Emitter.Csharp/src/lib/model.ts | 27 +- .../src/type/InputModelProperty.ts | 1 + test/CadlRanchProjects.Tests/projection.cs | 48 ++ .../projection/Generated/Configuration.json | 10 + .../Generated/Docs/ProjectedNameClient.xml | 245 ++++++++++ .../Generated/Models/Project.Serialization.cs | 44 ++ .../projection/Generated/Models/Project.cs | 25 + .../Generated/ProjectedNameClient.cs | 330 +++++++++++++ .../ProjectedNameClientBuilderExtensions.cs | 35 ++ .../Generated/ProjectedNameClientOptions.cs | 37 ++ .../projection/Generated/cadl.json | 449 ++++++++++++++++++ .../projection/Projection.csproj | 20 + 17 files changed, 1350 insertions(+), 106 deletions(-) create mode 100644 src/CADL.Extension/Emitter.Csharp/src/constants.ts create mode 100644 test/CadlRanchProjects.Tests/projection.cs create mode 100644 test/CadlRanchProjects/projection/Generated/Configuration.json create mode 100644 test/CadlRanchProjects/projection/Generated/Docs/ProjectedNameClient.xml create mode 100644 test/CadlRanchProjects/projection/Generated/Models/Project.Serialization.cs create mode 100644 test/CadlRanchProjects/projection/Generated/Models/Project.cs create mode 100644 test/CadlRanchProjects/projection/Generated/ProjectedNameClient.cs create mode 100644 test/CadlRanchProjects/projection/Generated/ProjectedNameClientBuilderExtensions.cs create mode 100644 test/CadlRanchProjects/projection/Generated/ProjectedNameClientOptions.cs create mode 100644 test/CadlRanchProjects/projection/Generated/cadl.json create mode 100644 test/CadlRanchProjects/projection/Projection.csproj diff --git a/eng/Generate.ps1 b/eng/Generate.ps1 index 7a529e08e7f..6618c8a40b6 100644 --- a/eng/Generate.ps1 +++ b/eng/Generate.ps1 @@ -264,7 +264,8 @@ $cadlRanchProjectPaths = 'authentication/union', 'models/property-optional', 'models/property-types', - 'models/usage' + 'models/usage', + "projection" if (!($Exclude -contains "CadlRanchProjects")) { diff --git a/src/AutoRest.CSharp/Common/Input/CadlInputModelPropertyConverter.cs b/src/AutoRest.CSharp/Common/Input/CadlInputModelPropertyConverter.cs index c2c81590fd4..8358691467f 100644 --- a/src/AutoRest.CSharp/Common/Input/CadlInputModelPropertyConverter.cs +++ b/src/AutoRest.CSharp/Common/Input/CadlInputModelPropertyConverter.cs @@ -37,7 +37,7 @@ private static InputModelProperty ReadInputModelProperty(ref Utf8JsonReader read { var isKnownProperty = reader.TryReadReferenceId(ref isFirstProperty, ref id) || reader.TryReadString(nameof(InputModelProperty.Name), ref name) - || reader.TryReadString(nameof(InputModelProperty.SerializedName), ref name) + || reader.TryReadString(nameof(InputModelProperty.SerializedName), ref serializedName) || reader.TryReadString(nameof(InputModelProperty.Description), ref description) || reader.TryReadWithConverter(nameof(InputModelProperty.Type), options, ref propertyType) || reader.TryReadBoolean(nameof(InputModelProperty.IsReadOnly), ref isReadOnly) diff --git a/src/AutoRest.CSharp/Properties/launchSettings.json b/src/AutoRest.CSharp/Properties/launchSettings.json index 4cb6fee90ab..eded62b7232 100644 --- a/src/AutoRest.CSharp/Properties/launchSettings.json +++ b/src/AutoRest.CSharp/Properties/launchSettings.json @@ -200,6 +200,10 @@ "commandName": "Project", "commandLineArgs": "--standalone $(SolutionDir)\\test\\CadlRanchProjects\\models\\usage\\Generated" }, + "cadl-projection": { + "commandName": "Project", + "commandLineArgs": "--standalone $(SolutionDir)\\test\\CadlRanchProjects\\projection\\Generated" + }, "ClientAndOperationGroup-Cadl": { "commandName": "Project", "commandLineArgs": "--standalone $(SolutionDir)\\test\\TestProjects\\ClientAndOperationGroup-Cadl\\Generated" diff --git a/src/CADL.Extension/Emitter.Csharp/src/constants.ts b/src/CADL.Extension/Emitter.Csharp/src/constants.ts new file mode 100644 index 00000000000..39ccaf74189 --- /dev/null +++ b/src/CADL.Extension/Emitter.Csharp/src/constants.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +export const projectedNameJsonKey = "json"; +export const projectedNameCSharpKey = "csharp"; +export const projectedNameClientKey = "client"; diff --git a/src/CADL.Extension/Emitter.Csharp/src/emitter.ts b/src/CADL.Extension/Emitter.Csharp/src/emitter.ts index 1115df38ff6..2ebc3c48170 100644 --- a/src/CADL.Extension/Emitter.Csharp/src/emitter.ts +++ b/src/CADL.Extension/Emitter.Csharp/src/emitter.ts @@ -211,18 +211,10 @@ function deleteFile(filePath: string) { function prettierOutput(output: string) { return output + "\n"; } -function getClient( - clients: InputClient[], - clientName: string -): InputClient | undefined { - for (const client of clients) { - if (client.Name === clientName) return client; - } - - return undefined; -} -export function createModel(context: EmitContext): any { +export function createModel( + context: EmitContext +): CodeModel { const services = listServices(context.program); if (services.length === 0) { services.push({ type: context.program.getGlobalNamespaceType() }); @@ -241,7 +233,7 @@ export function createModel(context: EmitContext): any { export function createModelForService( context: EmitContext, service: Service -): any { +): CodeModel { const program = context.program; const title = service.title; const serviceNamespaceType = service.type; @@ -305,98 +297,84 @@ export function createModelForService( let url: string = ""; const convenienceOperations: HttpOperation[] = []; let lroMonitorOperations: Set; - const dpgContext = createDpgContext(context as EmitContext); - try { - //create endpoint parameter from servers - if (servers !== undefined) { - const cadlServers = resolveServers( - program, - servers, - modelMap, - enumMap - ); - if (cadlServers.length > 0) { - /* choose the first server as endpoint. */ - url = cadlServers[0].url; - urlParameters = cadlServers[0].parameters; - } - } - const [services] = getAllHttpServices(program); - const routes = services[0].operations; - if (routes.length === 0) { - throw `No Route for service ${services[0].namespace.name}`; + const dpgContext = createDpgContext(context); + + //create endpoint parameter from servers + if (servers !== undefined) { + const cadlServers = resolveServers(program, servers, modelMap, enumMap); + if (cadlServers.length > 0) { + /* choose the first server as endpoint. */ + url = cadlServers[0].url; + urlParameters = cadlServers[0].parameters; } - console.log("routes:" + routes.length); - - lroMonitorOperations = getAllLroMonitorOperations(routes, program); - const clients: InputClient[] = []; - const dpgClients = listClients(dpgContext); - for (const client of dpgClients) { - clients.push(emitClient(client)); - const dpgOperationGroups = listOperationGroups(dpgContext, client); - for (const dpgGroup of dpgOperationGroups) { - clients.push(emitClient(dpgGroup, client)); - } + } + const [services] = getAllHttpServices(program); + const routes = services[0].operations; + if (routes.length === 0) { + throw `No Route for service ${services[0].namespace.name}`; + } + console.log("routes:" + routes.length); + + lroMonitorOperations = getAllLroMonitorOperations(routes, program); + const clients: InputClient[] = []; + const dpgClients = listClients(dpgContext); + for (const client of dpgClients) { + clients.push(emitClient(client)); + const dpgOperationGroups = listOperationGroups(dpgContext, client); + for (const dpgGroup of dpgOperationGroups) { + clients.push(emitClient(dpgGroup, client)); } + } - for (const client of clients) { - for (const op of client.Operations) { - const apiVersionIndex = op.Parameters.findIndex( - (value) => value.IsApiVersion - ); - if (apiVersionIndex !== -1) { - const apiVersionInOperation = - op.Parameters[apiVersionIndex]; - if (!apiVersionInOperation.DefaultValue?.Value) { - apiVersionInOperation.DefaultValue = - apiVersionParam.DefaultValue; - } - /** - * replace to the global apiVerison parameter if the apiVersion defined in the operation is the same as the global service apiVersion parameter. - * Three checkpoints: - * the parameter is query parameter, - * it is client parameter - * it does not has default value, or the default value is included in the global service apiVersion. - */ - if ( - apiVersions.has( - apiVersionInOperation.DefaultValue?.Value - ) && - apiVersionInOperation.Kind === - InputOperationParameterKind.Client && - apiVersionInOperation.Location === - apiVersionParam.Location - ) { - op.Parameters[apiVersionIndex] = apiVersionParam; - } - } else { - op.Parameters.push(apiVersionParam); + for (const client of clients) { + for (const op of client.Operations) { + const apiVersionIndex = op.Parameters.findIndex( + (value) => value.IsApiVersion + ); + if (apiVersionIndex !== -1) { + const apiVersionInOperation = op.Parameters[apiVersionIndex]; + if (!apiVersionInOperation.DefaultValue?.Value) { + apiVersionInOperation.DefaultValue = + apiVersionParam.DefaultValue; } + /** + * replace to the global apiVerison parameter if the apiVersion defined in the operation is the same as the global service apiVersion parameter. + * Three checkpoints: + * the parameter is query parameter, + * it is client parameter + * it does not has default value, or the default value is included in the global service apiVersion. + */ + if ( + apiVersions.has( + apiVersionInOperation.DefaultValue?.Value + ) && + apiVersionInOperation.Kind === + InputOperationParameterKind.Client && + apiVersionInOperation.Location === apiVersionParam.Location + ) { + op.Parameters[apiVersionIndex] = apiVersionParam; + } + } else { + op.Parameters.push(apiVersionParam); } } - - const usages = getUsages(program, convenienceOperations); - setUsage(usages, modelMap); - setUsage(usages, enumMap); - - const clientModel = { - Name: namespace, - Description: description, - ApiVersions: Array.from(apiVersions.values()), - Enums: Array.from(enumMap.values()), - Models: Array.from(modelMap.values()), - Clients: clients, - Auth: auth - } as CodeModel; - return clientModel; - } catch (err) { - if (err instanceof ErrorTypeFoundError) { - return; - } else { - throw err; - } } + const usages = getUsages(program, convenienceOperations); + setUsage(usages, modelMap); + setUsage(usages, enumMap); + + const clientModel = { + Name: namespace, + Description: description, + ApiVersions: Array.from(apiVersions.values()), + Enums: Array.from(enumMap.values()), + Models: Array.from(modelMap.values()), + Clients: clients, + Auth: auth + } as CodeModel; + return clientModel; + function emitClient( client: Client | OperationGroup, parent?: Client diff --git a/src/CADL.Extension/Emitter.Csharp/src/lib/model.ts b/src/CADL.Extension/Emitter.Csharp/src/lib/model.ts index ecb3e51eb46..7633deddaa4 100644 --- a/src/CADL.Extension/Emitter.Csharp/src/lib/model.ts +++ b/src/CADL.Extension/Emitter.Csharp/src/lib/model.ts @@ -21,15 +21,14 @@ import { resolveUsages, Type, UsageFlags, - UsageTracker, - TrackableType, getDiscriminator, IntrinsicType, isVoidType, isArrayModelType, isRecordModelType, Scalar, - Union + Union, + getProjectedNames } from "@cadl-lang/compiler"; import { getResourceOperation } from "@cadl-lang/rest"; import { @@ -39,6 +38,11 @@ import { HttpOperation, isStatusCode } from "@cadl-lang/rest/http"; +import { + projectedNameClientKey, + projectedNameCSharpKey, + projectedNameJsonKey +} from "../constants.js"; import { InputEnumTypeValue } from "../type/InputEnumTypeValue.js"; import { InputModelProperty } from "../type/InputModelProperty.js"; import { @@ -72,8 +76,8 @@ export function mapCadlTypeToCSharpInputTypeKind( case "Enum": return InputTypeKind.Enum; case "Number": - let nubmerValue = cadlType.value; - if (nubmerValue % 1 === 0) { + let numberValue = cadlType.value; + if (numberValue % 1 === 0) { return InputTypeKind.Int32; } return InputTypeKind.Float64; @@ -512,15 +516,22 @@ export function getInputType( isReadOnly = true; } if (isNeverType(value.type) || isVoidType(value.type)) return; + const projectedNamesMap = getProjectedNames(program, value); + const name = + projectedNamesMap?.get(projectedNameCSharpKey) ?? + projectedNamesMap?.get(projectedNameClientKey) ?? + value.name; + const serializedName = + projectedNamesMap?.get(projectedNameJsonKey) ?? value.name; const inputProp = { - Name: value.name, - SerializedName: value.name, + Name: name, + SerializedName: serializedName, Description: getDoc(program, value) ?? "", Type: getInputType(program, value.type, models, enums), IsRequired: !value.optional, IsReadOnly: isReadOnly, IsDiscriminator: false - }; + } as InputModelProperty; outputProperties.push(inputProp); } }); diff --git a/src/CADL.Extension/Emitter.Csharp/src/type/InputModelProperty.ts b/src/CADL.Extension/Emitter.Csharp/src/type/InputModelProperty.ts index d5b571c9889..ab9f14f0856 100644 --- a/src/CADL.Extension/Emitter.Csharp/src/type/InputModelProperty.ts +++ b/src/CADL.Extension/Emitter.Csharp/src/type/InputModelProperty.ts @@ -10,4 +10,5 @@ export interface InputModelProperty { Type: InputType; IsRequired: boolean; IsReadOnly: boolean; + IsDiscriminator: boolean; } diff --git a/test/CadlRanchProjects.Tests/projection.cs b/test/CadlRanchProjects.Tests/projection.cs new file mode 100644 index 00000000000..8fafe5bcddc --- /dev/null +++ b/test/CadlRanchProjects.Tests/projection.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using AutoRest.TestServer.Tests.Infrastructure; +using Azure; +using NUnit.Framework; +using ProjectedName; +using ProjectedName.Models; + +namespace CadlRanchProjects.Tests +{ + public class ProjectionTests : CadlRanchTestBase + { + [Test] + public Task ProjectedName_jsonProjection() => Test(async (host) => + { + Project project = new Project() + { + ProducedBy = "DPG", + }; + Response response = await new ProjectedNameClient(host, null).JsonProjectionAsync(project); + Assert.AreEqual(204, response.Status); + }); + + [Test] + public Task ProjectedName_clientProjection() => Test(async (host) => + { + Project project = new Project() + { + CreatedBy = "DPG", + }; + Response response = await new ProjectedNameClient(host, null).ClientProjectionAsync(project); + Assert.AreEqual(204, response.Status); + }); + + [Test] + public Task ProjectedName_languageProjection() => Test(async (host) => + { + Project project = new Project() + { + MadeForCS = "customers" + }; + Response response = await new ProjectedNameClient(host, null).LanguageProjectionAsync(project); + Assert.AreEqual(204, response.Status); + }); + } +} diff --git a/test/CadlRanchProjects/projection/Generated/Configuration.json b/test/CadlRanchProjects/projection/Generated/Configuration.json new file mode 100644 index 00000000000..33537c218f2 --- /dev/null +++ b/test/CadlRanchProjects/projection/Generated/Configuration.json @@ -0,0 +1,10 @@ +{ + "OutputFolder": ".", + "Namespace": "ProjectedName", + "LibraryName": null, + "SharedSourceFolders": [ + "../../../../artifacts/bin/AutoRest.CSharp/Debug/net6.0/Generator.Shared", + "../../../../artifacts/bin/AutoRest.CSharp/Debug/net6.0/Azure.Core.Shared" + ], + "unreferenced-types-handling": "keepAll" +} diff --git a/test/CadlRanchProjects/projection/Generated/Docs/ProjectedNameClient.xml b/test/CadlRanchProjects/projection/Generated/Docs/ProjectedNameClient.xml new file mode 100644 index 00000000000..534ee38df81 --- /dev/null +++ b/test/CadlRanchProjects/projection/Generated/Docs/ProjectedNameClient.xml @@ -0,0 +1,245 @@ + + + + + +This sample shows how to call JsonProjectionAsync. + +This sample shows how to call JsonProjectionAsync with all request content. +", + builtfrom = "", + wasMadeFor = "", +}; + +Response response = await client.JsonProjectionAsync(RequestContent.Create(data)); +Console.WriteLine(response.Status); +]]> + + +Below is the JSON schema for the request payload. + +Request Body: + +Schema for Project: +{ + codegen: string, # Optional. Only valid value is 'DPG' + builtfrom: string, # Optional. Only valid value is 'DPG' + wasMadeFor: string, # Optional. Only valid value is 'customers' +} + + + + + + +This sample shows how to call JsonProjection. + +This sample shows how to call JsonProjection with all request content. +", + builtfrom = "", + wasMadeFor = "", +}; + +Response response = client.JsonProjection(RequestContent.Create(data)); +Console.WriteLine(response.Status); +]]> + + +Below is the JSON schema for the request payload. + +Request Body: + +Schema for Project: +{ + codegen: string, # Optional. Only valid value is 'DPG' + builtfrom: string, # Optional. Only valid value is 'DPG' + wasMadeFor: string, # Optional. Only valid value is 'customers' +} + + + + + + +This sample shows how to call ClientProjectionAsync. + +This sample shows how to call ClientProjectionAsync with all request content. +", + builtfrom = "", + wasMadeFor = "", +}; + +Response response = await client.ClientProjectionAsync(RequestContent.Create(data)); +Console.WriteLine(response.Status); +]]> + + +Below is the JSON schema for the request payload. + +Request Body: + +Schema for Project: +{ + codegen: string, # Optional. Only valid value is 'DPG' + builtfrom: string, # Optional. Only valid value is 'DPG' + wasMadeFor: string, # Optional. Only valid value is 'customers' +} + + + + + + +This sample shows how to call ClientProjection. + +This sample shows how to call ClientProjection with all request content. +", + builtfrom = "", + wasMadeFor = "", +}; + +Response response = client.ClientProjection(RequestContent.Create(data)); +Console.WriteLine(response.Status); +]]> + + +Below is the JSON schema for the request payload. + +Request Body: + +Schema for Project: +{ + codegen: string, # Optional. Only valid value is 'DPG' + builtfrom: string, # Optional. Only valid value is 'DPG' + wasMadeFor: string, # Optional. Only valid value is 'customers' +} + + + + + + +This sample shows how to call LanguageProjectionAsync. + +This sample shows how to call LanguageProjectionAsync with all request content. +", + builtfrom = "", + wasMadeFor = "", +}; + +Response response = await client.LanguageProjectionAsync(RequestContent.Create(data)); +Console.WriteLine(response.Status); +]]> + + +Below is the JSON schema for the request payload. + +Request Body: + +Schema for Project: +{ + codegen: string, # Optional. Only valid value is 'DPG' + builtfrom: string, # Optional. Only valid value is 'DPG' + wasMadeFor: string, # Optional. Only valid value is 'customers' +} + + + + + + +This sample shows how to call LanguageProjection. + +This sample shows how to call LanguageProjection with all request content. +", + builtfrom = "", + wasMadeFor = "", +}; + +Response response = client.LanguageProjection(RequestContent.Create(data)); +Console.WriteLine(response.Status); +]]> + + +Below is the JSON schema for the request payload. + +Request Body: + +Schema for Project: +{ + codegen: string, # Optional. Only valid value is 'DPG' + builtfrom: string, # Optional. Only valid value is 'DPG' + wasMadeFor: string, # Optional. Only valid value is 'customers' +} + + + + + + \ No newline at end of file diff --git a/test/CadlRanchProjects/projection/Generated/Models/Project.Serialization.cs b/test/CadlRanchProjects/projection/Generated/Models/Project.Serialization.cs new file mode 100644 index 00000000000..8f712cd70fa --- /dev/null +++ b/test/CadlRanchProjects/projection/Generated/Models/Project.Serialization.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Text.Json; +using Azure.Core; + +namespace ProjectedName.Models +{ + public partial class Project : IUtf8JsonSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + writer.WriteStartObject(); + if (Optional.IsDefined(ProducedBy)) + { + writer.WritePropertyName("codegen"u8); + writer.WriteStringValue(ProducedBy); + } + if (Optional.IsDefined(CreatedBy)) + { + writer.WritePropertyName("builtfrom"u8); + writer.WriteStringValue(CreatedBy); + } + if (Optional.IsDefined(MadeForCS)) + { + writer.WritePropertyName("wasMadeFor"u8); + writer.WriteStringValue(MadeForCS); + } + writer.WriteEndObject(); + } + + /// Convert into a Utf8JsonRequestContent. + internal virtual RequestContent ToRequestContent() + { + var content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteObjectValue(this); + return content; + } + } +} diff --git a/test/CadlRanchProjects/projection/Generated/Models/Project.cs b/test/CadlRanchProjects/projection/Generated/Models/Project.cs new file mode 100644 index 00000000000..8608cd74e3d --- /dev/null +++ b/test/CadlRanchProjects/projection/Generated/Models/Project.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +namespace ProjectedName.Models +{ + /// The Project. + public partial class Project + { + /// Initializes a new instance of Project. + public Project() + { + } + + /// Only valid value is 'DPG'. + public string ProducedBy { get; set; } + /// Only valid value is 'DPG'. + public string CreatedBy { get; set; } + /// Only valid value is 'customers'. + public string MadeForCS { get; set; } + } +} diff --git a/test/CadlRanchProjects/projection/Generated/ProjectedNameClient.cs b/test/CadlRanchProjects/projection/Generated/ProjectedNameClient.cs new file mode 100644 index 00000000000..01c4c690c57 --- /dev/null +++ b/test/CadlRanchProjects/projection/Generated/ProjectedNameClient.cs @@ -0,0 +1,330 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Threading; +using System.Threading.Tasks; +using Azure; +using Azure.Core; +using Azure.Core.Pipeline; +using ProjectedName.Models; + +namespace ProjectedName +{ + // Data plane generated client. + /// Projection. + public partial class ProjectedNameClient + { + private readonly HttpPipeline _pipeline; + private readonly Uri _endpoint; + private readonly string _apiVersion; + + /// The ClientDiagnostics is used to provide tracing support for the client library. + internal ClientDiagnostics ClientDiagnostics { get; } + + /// The HTTP pipeline for sending and receiving REST requests and responses. + public virtual HttpPipeline Pipeline => _pipeline; + + /// Initializes a new instance of ProjectedNameClient. + public ProjectedNameClient() : this(new Uri("http://localhost:3000"), new ProjectedNameClientOptions()) + { + } + + /// Initializes a new instance of ProjectedNameClient. + /// TestServer endpoint. + /// The options for configuring the client. + /// is null. + public ProjectedNameClient(Uri endpoint, ProjectedNameClientOptions options) + { + Argument.AssertNotNull(endpoint, nameof(endpoint)); + options ??= new ProjectedNameClientOptions(); + + ClientDiagnostics = new ClientDiagnostics(options, true); + _pipeline = HttpPipelineBuilder.Build(options, Array.Empty(), Array.Empty(), new ResponseClassifier()); + _endpoint = endpoint; + _apiVersion = options.Version; + } + + /// The Project to use. + /// The cancellation token to use. + /// is null. + public virtual async Task JsonProjectionAsync(Project project, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(project, nameof(project)); + + RequestContext context = FromCancellationToken(cancellationToken); + Response response = await JsonProjectionAsync(project.ToRequestContent(), context).ConfigureAwait(false); + return response; + } + + /// The Project to use. + /// The cancellation token to use. + /// is null. + public virtual Response JsonProjection(Project project, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(project, nameof(project)); + + RequestContext context = FromCancellationToken(cancellationToken); + Response response = JsonProjection(project.ToRequestContent(), context); + return response; + } + + /// The content to send as the body of the request. Details of the request body schema are in the Remarks section below. + /// The request context, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. + /// Service returned a non-success status code. + /// The response returned from the service. + /// + public virtual async Task JsonProjectionAsync(RequestContent content, RequestContext context = null) + { + Argument.AssertNotNull(content, nameof(content)); + + using var scope = ClientDiagnostics.CreateScope("ProjectedNameClient.JsonProjection"); + scope.Start(); + try + { + using HttpMessage message = CreateJsonProjectionRequest(content, context); + return await _pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// The content to send as the body of the request. Details of the request body schema are in the Remarks section below. + /// The request context, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. + /// Service returned a non-success status code. + /// The response returned from the service. + /// + public virtual Response JsonProjection(RequestContent content, RequestContext context = null) + { + Argument.AssertNotNull(content, nameof(content)); + + using var scope = ClientDiagnostics.CreateScope("ProjectedNameClient.JsonProjection"); + scope.Start(); + try + { + using HttpMessage message = CreateJsonProjectionRequest(content, context); + return _pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// The Project to use. + /// The cancellation token to use. + /// is null. + public virtual async Task ClientProjectionAsync(Project project, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(project, nameof(project)); + + RequestContext context = FromCancellationToken(cancellationToken); + Response response = await ClientProjectionAsync(project.ToRequestContent(), context).ConfigureAwait(false); + return response; + } + + /// The Project to use. + /// The cancellation token to use. + /// is null. + public virtual Response ClientProjection(Project project, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(project, nameof(project)); + + RequestContext context = FromCancellationToken(cancellationToken); + Response response = ClientProjection(project.ToRequestContent(), context); + return response; + } + + /// The content to send as the body of the request. Details of the request body schema are in the Remarks section below. + /// The request context, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. + /// Service returned a non-success status code. + /// The response returned from the service. + /// + public virtual async Task ClientProjectionAsync(RequestContent content, RequestContext context = null) + { + Argument.AssertNotNull(content, nameof(content)); + + using var scope = ClientDiagnostics.CreateScope("ProjectedNameClient.ClientProjection"); + scope.Start(); + try + { + using HttpMessage message = CreateClientProjectionRequest(content, context); + return await _pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// The content to send as the body of the request. Details of the request body schema are in the Remarks section below. + /// The request context, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. + /// Service returned a non-success status code. + /// The response returned from the service. + /// + public virtual Response ClientProjection(RequestContent content, RequestContext context = null) + { + Argument.AssertNotNull(content, nameof(content)); + + using var scope = ClientDiagnostics.CreateScope("ProjectedNameClient.ClientProjection"); + scope.Start(); + try + { + using HttpMessage message = CreateClientProjectionRequest(content, context); + return _pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// The Project to use. + /// The cancellation token to use. + /// is null. + public virtual async Task LanguageProjectionAsync(Project project, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(project, nameof(project)); + + RequestContext context = FromCancellationToken(cancellationToken); + Response response = await LanguageProjectionAsync(project.ToRequestContent(), context).ConfigureAwait(false); + return response; + } + + /// The Project to use. + /// The cancellation token to use. + /// is null. + public virtual Response LanguageProjection(Project project, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(project, nameof(project)); + + RequestContext context = FromCancellationToken(cancellationToken); + Response response = LanguageProjection(project.ToRequestContent(), context); + return response; + } + + /// The content to send as the body of the request. Details of the request body schema are in the Remarks section below. + /// The request context, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. + /// Service returned a non-success status code. + /// The response returned from the service. + /// + public virtual async Task LanguageProjectionAsync(RequestContent content, RequestContext context = null) + { + Argument.AssertNotNull(content, nameof(content)); + + using var scope = ClientDiagnostics.CreateScope("ProjectedNameClient.LanguageProjection"); + scope.Start(); + try + { + using HttpMessage message = CreateLanguageProjectionRequest(content, context); + return await _pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// The content to send as the body of the request. Details of the request body schema are in the Remarks section below. + /// The request context, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. + /// Service returned a non-success status code. + /// The response returned from the service. + /// + public virtual Response LanguageProjection(RequestContent content, RequestContext context = null) + { + Argument.AssertNotNull(content, nameof(content)); + + using var scope = ClientDiagnostics.CreateScope("ProjectedNameClient.LanguageProjection"); + scope.Start(); + try + { + using HttpMessage message = CreateLanguageProjectionRequest(content, context); + return _pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + internal HttpMessage CreateJsonProjectionRequest(RequestContent content, RequestContext context) + { + var message = _pipeline.CreateMessage(context, ResponseClassifier204); + var request = message.Request; + request.Method = RequestMethod.Post; + var uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/projection/json", false); + uri.AppendQuery("api-version", _apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + request.Headers.Add("Content-Type", "application/json"); + request.Content = content; + return message; + } + + internal HttpMessage CreateClientProjectionRequest(RequestContent content, RequestContext context) + { + var message = _pipeline.CreateMessage(context, ResponseClassifier204); + var request = message.Request; + request.Method = RequestMethod.Post; + var uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/projection/client", false); + uri.AppendQuery("api-version", _apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + request.Headers.Add("Content-Type", "application/json"); + request.Content = content; + return message; + } + + internal HttpMessage CreateLanguageProjectionRequest(RequestContent content, RequestContext context) + { + var message = _pipeline.CreateMessage(context, ResponseClassifier204); + var request = message.Request; + request.Method = RequestMethod.Post; + var uri = new RawRequestUriBuilder(); + uri.Reset(_endpoint); + uri.AppendPath("/projection/language", false); + uri.AppendQuery("api-version", _apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + request.Headers.Add("Content-Type", "application/json"); + request.Content = content; + return message; + } + + private static RequestContext DefaultRequestContext = new RequestContext(); + internal static RequestContext FromCancellationToken(CancellationToken cancellationToken = default) + { + if (!cancellationToken.CanBeCanceled) + { + return DefaultRequestContext; + } + + return new RequestContext() { CancellationToken = cancellationToken }; + } + + private static ResponseClassifier _responseClassifier204; + private static ResponseClassifier ResponseClassifier204 => _responseClassifier204 ??= new StatusCodeClassifier(stackalloc ushort[] { 204 }); + } +} diff --git a/test/CadlRanchProjects/projection/Generated/ProjectedNameClientBuilderExtensions.cs b/test/CadlRanchProjects/projection/Generated/ProjectedNameClientBuilderExtensions.cs new file mode 100644 index 00000000000..6e3695fb911 --- /dev/null +++ b/test/CadlRanchProjects/projection/Generated/ProjectedNameClientBuilderExtensions.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using Azure.Core.Extensions; +using ProjectedName; + +namespace Microsoft.Extensions.Azure +{ + /// Extension methods to add to client builder. + public static partial class ProjectedNameClientBuilderExtensions + { + /// Registers a instance. + /// The builder to register with. + /// TestServer endpoint. + public static IAzureClientBuilder AddProjectedNameClient(this TBuilder builder, Uri endpoint) + where TBuilder : IAzureClientFactoryBuilder + { + return builder.RegisterClientFactory((options) => new ProjectedNameClient(endpoint, options)); + } + + /// Registers a instance. + /// The builder to register with. + /// The configuration values. + public static IAzureClientBuilder AddProjectedNameClient(this TBuilder builder, TConfiguration configuration) + where TBuilder : IAzureClientFactoryBuilderWithConfiguration + { + return builder.RegisterClientFactory(configuration); + } + } +} diff --git a/test/CadlRanchProjects/projection/Generated/ProjectedNameClientOptions.cs b/test/CadlRanchProjects/projection/Generated/ProjectedNameClientOptions.cs new file mode 100644 index 00000000000..5c565f2e733 --- /dev/null +++ b/test/CadlRanchProjects/projection/Generated/ProjectedNameClientOptions.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using Azure.Core; + +namespace ProjectedName +{ + /// Client options for ProjectedNameClient. + public partial class ProjectedNameClientOptions : ClientOptions + { + private const ServiceVersion LatestVersion = ServiceVersion.V1_0_0; + + /// The version of the service to use. + public enum ServiceVersion + { + /// Service version "1.0.0". + V1_0_0 = 1, + } + + internal string Version { get; } + + /// Initializes new instance of ProjectedNameClientOptions. + public ProjectedNameClientOptions(ServiceVersion version = LatestVersion) + { + Version = version switch + { + ServiceVersion.V1_0_0 => "1.0.0", + _ => throw new NotSupportedException() + }; + } + } +} diff --git a/test/CadlRanchProjects/projection/Generated/cadl.json b/test/CadlRanchProjects/projection/Generated/cadl.json new file mode 100644 index 00000000000..5175acf9ba5 --- /dev/null +++ b/test/CadlRanchProjects/projection/Generated/cadl.json @@ -0,0 +1,449 @@ +{ + "$id": "1", + "Name": "ProjectedName", + "Description": "Projection", + "ApiVersions": [ + "1.0.0" + ], + "Enums": [], + "Models": [ + { + "$id": "2", + "Name": "Project", + "Namespace": "ProjectedName", + "IsNullable": false, + "Usage": "Input", + "Properties": [ + { + "$id": "3", + "Name": "producedBy", + "SerializedName": "codegen", + "Description": "Only valid value is 'DPG'", + "Type": { + "$id": "4", + "Name": "string", + "Kind": "String", + "IsNullable": false + }, + "IsRequired": false, + "IsReadOnly": false, + "IsDiscriminator": false + }, + { + "$id": "5", + "Name": "createdBy", + "SerializedName": "builtfrom", + "Description": "Only valid value is 'DPG'", + "Type": { + "$id": "6", + "Name": "string", + "Kind": "String", + "IsNullable": false + }, + "IsRequired": false, + "IsReadOnly": false, + "IsDiscriminator": false + }, + { + "$id": "7", + "Name": "MadeForCS", + "SerializedName": "wasMadeFor", + "Description": "Only valid value is 'customers'", + "Type": { + "$id": "8", + "Name": "string", + "Kind": "String", + "IsNullable": false + }, + "IsRequired": false, + "IsReadOnly": false, + "IsDiscriminator": false + } + ] + } + ], + "Clients": [ + { + "$id": "9", + "Name": "ProjectedNameClient", + "Description": "Projection", + "Operations": [ + { + "$id": "10", + "Name": "jsonProjection", + "ResourceName": "ProjectedName", + "Parameters": [ + { + "$id": "11", + "Name": "host", + "NameInRequest": "host", + "Description": "TestServer endpoint", + "Type": { + "$id": "12", + "Name": "String", + "Kind": "String", + "IsNullable": false + }, + "Location": "Uri", + "IsApiVersion": false, + "IsResourceParameter": false, + "IsContentType": false, + "IsRequired": true, + "IsEndpoint": true, + "SkipUrlEncoding": false, + "Explode": false, + "Kind": "Client", + "DefaultValue": { + "$id": "13", + "Type": { + "$id": "14", + "Name": "String", + "Kind": "String", + "IsNullable": false + }, + "Value": "http://localhost:3000" + } + }, + { + "$id": "15", + "Name": "Project", + "NameInRequest": "Project", + "Type": { + "$ref": "2" + }, + "Location": "Body", + "IsRequired": true, + "IsApiVersion": false, + "IsResourceParameter": false, + "IsContentType": false, + "IsEndpoint": false, + "SkipUrlEncoding": false, + "Explode": false, + "Kind": "Method" + }, + { + "$id": "16", + "Name": "contentType", + "NameInRequest": "Content-Type", + "Type": { + "$id": "17", + "Name": "String", + "Kind": "String", + "IsNullable": false + }, + "Location": "Header", + "IsApiVersion": false, + "IsResourceParameter": false, + "IsContentType": true, + "IsRequired": true, + "IsEndpoint": false, + "SkipUrlEncoding": false, + "Explode": false, + "Kind": "Constant", + "DefaultValue": { + "$id": "18", + "Type": { + "$ref": "17" + }, + "Value": "application/json" + } + }, + { + "$id": "19", + "Name": "accept", + "NameInRequest": "Accept", + "Type": { + "$id": "20", + "Name": "String", + "Kind": "String", + "IsNullable": false + }, + "Location": "Header", + "IsApiVersion": false, + "IsResourceParameter": false, + "IsContentType": false, + "IsRequired": true, + "IsEndpoint": false, + "SkipUrlEncoding": false, + "Explode": false, + "Kind": "Constant", + "DefaultValue": { + "$id": "21", + "Type": { + "$ref": "20" + }, + "Value": "application/json" + } + }, + { + "$id": "22", + "Name": "apiVersion", + "NameInRequest": "api-version", + "Description": "", + "Type": { + "$id": "23", + "Name": "String", + "Kind": "String", + "IsNullable": false + }, + "Location": "Query", + "IsRequired": true, + "IsApiVersion": true, + "IsContentType": false, + "IsEndpoint": false, + "IsResourceParameter": false, + "SkipUrlEncoding": false, + "Explode": false, + "Kind": "Client", + "DefaultValue": { + "$id": "24", + "Type": { + "$id": "25", + "Name": "String", + "Kind": "String", + "IsNullable": false + }, + "Value": "1.0.0" + } + } + ], + "Responses": [ + { + "$id": "26", + "StatusCodes": [ + 204 + ], + "BodyMediaType": "Json", + "Headers": [], + "IsErrorResponse": false + } + ], + "HttpMethod": "POST", + "RequestBodyMediaType": "Json", + "Uri": "{host}", + "Path": "/projection/json", + "RequestMediaTypes": [ + "application/json" + ], + "BufferResponse": true, + "GenerateProtocolMethod": true, + "GenerateConvenienceMethod": true + }, + { + "$id": "27", + "Name": "clientProjection", + "ResourceName": "ProjectedName", + "Parameters": [ + { + "$ref": "11" + }, + { + "$id": "28", + "Name": "Project", + "NameInRequest": "Project", + "Type": { + "$ref": "2" + }, + "Location": "Body", + "IsRequired": true, + "IsApiVersion": false, + "IsResourceParameter": false, + "IsContentType": false, + "IsEndpoint": false, + "SkipUrlEncoding": false, + "Explode": false, + "Kind": "Method" + }, + { + "$id": "29", + "Name": "contentType", + "NameInRequest": "Content-Type", + "Type": { + "$id": "30", + "Name": "String", + "Kind": "String", + "IsNullable": false + }, + "Location": "Header", + "IsApiVersion": false, + "IsResourceParameter": false, + "IsContentType": true, + "IsRequired": true, + "IsEndpoint": false, + "SkipUrlEncoding": false, + "Explode": false, + "Kind": "Constant", + "DefaultValue": { + "$id": "31", + "Type": { + "$ref": "30" + }, + "Value": "application/json" + } + }, + { + "$id": "32", + "Name": "accept", + "NameInRequest": "Accept", + "Type": { + "$id": "33", + "Name": "String", + "Kind": "String", + "IsNullable": false + }, + "Location": "Header", + "IsApiVersion": false, + "IsResourceParameter": false, + "IsContentType": false, + "IsRequired": true, + "IsEndpoint": false, + "SkipUrlEncoding": false, + "Explode": false, + "Kind": "Constant", + "DefaultValue": { + "$id": "34", + "Type": { + "$ref": "33" + }, + "Value": "application/json" + } + }, + { + "$ref": "22" + } + ], + "Responses": [ + { + "$id": "35", + "StatusCodes": [ + 204 + ], + "BodyMediaType": "Json", + "Headers": [], + "IsErrorResponse": false + } + ], + "HttpMethod": "POST", + "RequestBodyMediaType": "Json", + "Uri": "{host}", + "Path": "/projection/client", + "RequestMediaTypes": [ + "application/json" + ], + "BufferResponse": true, + "GenerateProtocolMethod": true, + "GenerateConvenienceMethod": true + }, + { + "$id": "36", + "Name": "languageProjection", + "ResourceName": "ProjectedName", + "Parameters": [ + { + "$ref": "11" + }, + { + "$id": "37", + "Name": "Project", + "NameInRequest": "Project", + "Type": { + "$ref": "2" + }, + "Location": "Body", + "IsRequired": true, + "IsApiVersion": false, + "IsResourceParameter": false, + "IsContentType": false, + "IsEndpoint": false, + "SkipUrlEncoding": false, + "Explode": false, + "Kind": "Method" + }, + { + "$id": "38", + "Name": "contentType", + "NameInRequest": "Content-Type", + "Type": { + "$id": "39", + "Name": "String", + "Kind": "String", + "IsNullable": false + }, + "Location": "Header", + "IsApiVersion": false, + "IsResourceParameter": false, + "IsContentType": true, + "IsRequired": true, + "IsEndpoint": false, + "SkipUrlEncoding": false, + "Explode": false, + "Kind": "Constant", + "DefaultValue": { + "$id": "40", + "Type": { + "$ref": "39" + }, + "Value": "application/json" + } + }, + { + "$id": "41", + "Name": "accept", + "NameInRequest": "Accept", + "Type": { + "$id": "42", + "Name": "String", + "Kind": "String", + "IsNullable": false + }, + "Location": "Header", + "IsApiVersion": false, + "IsResourceParameter": false, + "IsContentType": false, + "IsRequired": true, + "IsEndpoint": false, + "SkipUrlEncoding": false, + "Explode": false, + "Kind": "Constant", + "DefaultValue": { + "$id": "43", + "Type": { + "$ref": "42" + }, + "Value": "application/json" + } + }, + { + "$ref": "22" + } + ], + "Responses": [ + { + "$id": "44", + "StatusCodes": [ + 204 + ], + "BodyMediaType": "Json", + "Headers": [], + "IsErrorResponse": false + } + ], + "HttpMethod": "POST", + "RequestBodyMediaType": "Json", + "Uri": "{host}", + "Path": "/projection/language", + "RequestMediaTypes": [ + "application/json" + ], + "BufferResponse": true, + "GenerateProtocolMethod": true, + "GenerateConvenienceMethod": true + } + ], + "Protocol": { + "$id": "45" + }, + "Creatable": true + } + ] +} diff --git a/test/CadlRanchProjects/projection/Projection.csproj b/test/CadlRanchProjects/projection/Projection.csproj new file mode 100644 index 00000000000..d92fed650a1 --- /dev/null +++ b/test/CadlRanchProjects/projection/Projection.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + true + annotations + + + + $(DefineConstants);EXPERIMENTAL + + + + + + + + + +