Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion eng/Generate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,8 @@ $cadlRanchProjectPaths =
'authentication/union',
'models/property-optional',
'models/property-types',
'models/usage'
'models/usage',
"projection"

if (!($Exclude -contains "CadlRanchProjects"))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions src/AutoRest.CSharp/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 6 additions & 0 deletions src/CADL.Extension/Emitter.Csharp/src/constants.ts
Original file line number Diff line number Diff line change
@@ -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";
170 changes: 74 additions & 96 deletions src/CADL.Extension/Emitter.Csharp/src/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<NetEmitterOptions>): any {
export function createModel(
context: EmitContext<NetEmitterOptions>
): CodeModel {
const services = listServices(context.program);
if (services.length === 0) {
services.push({ type: context.program.getGlobalNamespaceType() });
Expand All @@ -241,7 +233,7 @@ export function createModel(context: EmitContext<NetEmitterOptions>): any {
export function createModelForService(
context: EmitContext<NetEmitterOptions>,
service: Service
): any {
): CodeModel {
const program = context.program;
const title = service.title;
const serviceNamespaceType = service.type;
Expand Down Expand Up @@ -305,98 +297,84 @@ export function createModelForService(
let url: string = "";
const convenienceOperations: HttpOperation[] = [];
let lroMonitorOperations: Set<Operation>;
const dpgContext = createDpgContext(context as EmitContext<any>);
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
Expand Down
27 changes: 19 additions & 8 deletions src/CADL.Extension/Emitter.Csharp/src/lib/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export interface InputModelProperty {
Type: InputType;
IsRequired: boolean;
IsReadOnly: boolean;
IsDiscriminator: boolean;
}
48 changes: 48 additions & 0 deletions test/CadlRanchProjects.Tests/projection.cs
Original file line number Diff line number Diff line change
@@ -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);
});
}
}
10 changes: 10 additions & 0 deletions test/CadlRanchProjects/projection/Generated/Configuration.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading