Skip to content

Commit aa049aa

Browse files
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 <[email protected]>
1 parent 44a88c1 commit aa049aa

File tree

17 files changed

+1350
-106
lines changed

17 files changed

+1350
-106
lines changed

eng/Generate.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,8 @@ $cadlRanchProjectPaths =
264264
'authentication/union',
265265
'models/property-optional',
266266
'models/property-types',
267-
'models/usage'
267+
'models/usage',
268+
"projection"
268269

269270
if (!($Exclude -contains "CadlRanchProjects"))
270271
{

src/AutoRest.CSharp/Common/Input/CadlInputModelPropertyConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ private static InputModelProperty ReadInputModelProperty(ref Utf8JsonReader read
3737
{
3838
var isKnownProperty = reader.TryReadReferenceId(ref isFirstProperty, ref id)
3939
|| reader.TryReadString(nameof(InputModelProperty.Name), ref name)
40-
|| reader.TryReadString(nameof(InputModelProperty.SerializedName), ref name)
40+
|| reader.TryReadString(nameof(InputModelProperty.SerializedName), ref serializedName)
4141
|| reader.TryReadString(nameof(InputModelProperty.Description), ref description)
4242
|| reader.TryReadWithConverter(nameof(InputModelProperty.Type), options, ref propertyType)
4343
|| reader.TryReadBoolean(nameof(InputModelProperty.IsReadOnly), ref isReadOnly)

src/AutoRest.CSharp/Properties/launchSettings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,10 @@
200200
"commandName": "Project",
201201
"commandLineArgs": "--standalone $(SolutionDir)\\test\\CadlRanchProjects\\models\\usage\\Generated"
202202
},
203+
"cadl-projection": {
204+
"commandName": "Project",
205+
"commandLineArgs": "--standalone $(SolutionDir)\\test\\CadlRanchProjects\\projection\\Generated"
206+
},
203207
"ClientAndOperationGroup-Cadl": {
204208
"commandName": "Project",
205209
"commandLineArgs": "--standalone $(SolutionDir)\\test\\TestProjects\\ClientAndOperationGroup-Cadl\\Generated"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
export const projectedNameJsonKey = "json";
5+
export const projectedNameCSharpKey = "csharp";
6+
export const projectedNameClientKey = "client";

src/CADL.Extension/Emitter.Csharp/src/emitter.ts

Lines changed: 74 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -211,18 +211,10 @@ function deleteFile(filePath: string) {
211211
function prettierOutput(output: string) {
212212
return output + "\n";
213213
}
214-
function getClient(
215-
clients: InputClient[],
216-
clientName: string
217-
): InputClient | undefined {
218-
for (const client of clients) {
219-
if (client.Name === clientName) return client;
220-
}
221-
222-
return undefined;
223-
}
224214

225-
export function createModel(context: EmitContext<NetEmitterOptions>): any {
215+
export function createModel(
216+
context: EmitContext<NetEmitterOptions>
217+
): CodeModel {
226218
const services = listServices(context.program);
227219
if (services.length === 0) {
228220
services.push({ type: context.program.getGlobalNamespaceType() });
@@ -241,7 +233,7 @@ export function createModel(context: EmitContext<NetEmitterOptions>): any {
241233
export function createModelForService(
242234
context: EmitContext<NetEmitterOptions>,
243235
service: Service
244-
): any {
236+
): CodeModel {
245237
const program = context.program;
246238
const title = service.title;
247239
const serviceNamespaceType = service.type;
@@ -305,98 +297,84 @@ export function createModelForService(
305297
let url: string = "";
306298
const convenienceOperations: HttpOperation[] = [];
307299
let lroMonitorOperations: Set<Operation>;
308-
const dpgContext = createDpgContext(context as EmitContext<any>);
309-
try {
310-
//create endpoint parameter from servers
311-
if (servers !== undefined) {
312-
const cadlServers = resolveServers(
313-
program,
314-
servers,
315-
modelMap,
316-
enumMap
317-
);
318-
if (cadlServers.length > 0) {
319-
/* choose the first server as endpoint. */
320-
url = cadlServers[0].url;
321-
urlParameters = cadlServers[0].parameters;
322-
}
323-
}
324-
const [services] = getAllHttpServices(program);
325-
const routes = services[0].operations;
326-
if (routes.length === 0) {
327-
throw `No Route for service ${services[0].namespace.name}`;
300+
const dpgContext = createDpgContext(context);
301+
302+
//create endpoint parameter from servers
303+
if (servers !== undefined) {
304+
const cadlServers = resolveServers(program, servers, modelMap, enumMap);
305+
if (cadlServers.length > 0) {
306+
/* choose the first server as endpoint. */
307+
url = cadlServers[0].url;
308+
urlParameters = cadlServers[0].parameters;
328309
}
329-
console.log("routes:" + routes.length);
330-
331-
lroMonitorOperations = getAllLroMonitorOperations(routes, program);
332-
const clients: InputClient[] = [];
333-
const dpgClients = listClients(dpgContext);
334-
for (const client of dpgClients) {
335-
clients.push(emitClient(client));
336-
const dpgOperationGroups = listOperationGroups(dpgContext, client);
337-
for (const dpgGroup of dpgOperationGroups) {
338-
clients.push(emitClient(dpgGroup, client));
339-
}
310+
}
311+
const [services] = getAllHttpServices(program);
312+
const routes = services[0].operations;
313+
if (routes.length === 0) {
314+
throw `No Route for service ${services[0].namespace.name}`;
315+
}
316+
console.log("routes:" + routes.length);
317+
318+
lroMonitorOperations = getAllLroMonitorOperations(routes, program);
319+
const clients: InputClient[] = [];
320+
const dpgClients = listClients(dpgContext);
321+
for (const client of dpgClients) {
322+
clients.push(emitClient(client));
323+
const dpgOperationGroups = listOperationGroups(dpgContext, client);
324+
for (const dpgGroup of dpgOperationGroups) {
325+
clients.push(emitClient(dpgGroup, client));
340326
}
327+
}
341328

342-
for (const client of clients) {
343-
for (const op of client.Operations) {
344-
const apiVersionIndex = op.Parameters.findIndex(
345-
(value) => value.IsApiVersion
346-
);
347-
if (apiVersionIndex !== -1) {
348-
const apiVersionInOperation =
349-
op.Parameters[apiVersionIndex];
350-
if (!apiVersionInOperation.DefaultValue?.Value) {
351-
apiVersionInOperation.DefaultValue =
352-
apiVersionParam.DefaultValue;
353-
}
354-
/**
355-
* replace to the global apiVerison parameter if the apiVersion defined in the operation is the same as the global service apiVersion parameter.
356-
* Three checkpoints:
357-
* the parameter is query parameter,
358-
* it is client parameter
359-
* it does not has default value, or the default value is included in the global service apiVersion.
360-
*/
361-
if (
362-
apiVersions.has(
363-
apiVersionInOperation.DefaultValue?.Value
364-
) &&
365-
apiVersionInOperation.Kind ===
366-
InputOperationParameterKind.Client &&
367-
apiVersionInOperation.Location ===
368-
apiVersionParam.Location
369-
) {
370-
op.Parameters[apiVersionIndex] = apiVersionParam;
371-
}
372-
} else {
373-
op.Parameters.push(apiVersionParam);
329+
for (const client of clients) {
330+
for (const op of client.Operations) {
331+
const apiVersionIndex = op.Parameters.findIndex(
332+
(value) => value.IsApiVersion
333+
);
334+
if (apiVersionIndex !== -1) {
335+
const apiVersionInOperation = op.Parameters[apiVersionIndex];
336+
if (!apiVersionInOperation.DefaultValue?.Value) {
337+
apiVersionInOperation.DefaultValue =
338+
apiVersionParam.DefaultValue;
374339
}
340+
/**
341+
* replace to the global apiVerison parameter if the apiVersion defined in the operation is the same as the global service apiVersion parameter.
342+
* Three checkpoints:
343+
* the parameter is query parameter,
344+
* it is client parameter
345+
* it does not has default value, or the default value is included in the global service apiVersion.
346+
*/
347+
if (
348+
apiVersions.has(
349+
apiVersionInOperation.DefaultValue?.Value
350+
) &&
351+
apiVersionInOperation.Kind ===
352+
InputOperationParameterKind.Client &&
353+
apiVersionInOperation.Location === apiVersionParam.Location
354+
) {
355+
op.Parameters[apiVersionIndex] = apiVersionParam;
356+
}
357+
} else {
358+
op.Parameters.push(apiVersionParam);
375359
}
376360
}
377-
378-
const usages = getUsages(program, convenienceOperations);
379-
setUsage(usages, modelMap);
380-
setUsage(usages, enumMap);
381-
382-
const clientModel = {
383-
Name: namespace,
384-
Description: description,
385-
ApiVersions: Array.from(apiVersions.values()),
386-
Enums: Array.from(enumMap.values()),
387-
Models: Array.from(modelMap.values()),
388-
Clients: clients,
389-
Auth: auth
390-
} as CodeModel;
391-
return clientModel;
392-
} catch (err) {
393-
if (err instanceof ErrorTypeFoundError) {
394-
return;
395-
} else {
396-
throw err;
397-
}
398361
}
399362

363+
const usages = getUsages(program, convenienceOperations);
364+
setUsage(usages, modelMap);
365+
setUsage(usages, enumMap);
366+
367+
const clientModel = {
368+
Name: namespace,
369+
Description: description,
370+
ApiVersions: Array.from(apiVersions.values()),
371+
Enums: Array.from(enumMap.values()),
372+
Models: Array.from(modelMap.values()),
373+
Clients: clients,
374+
Auth: auth
375+
} as CodeModel;
376+
return clientModel;
377+
400378
function emitClient(
401379
client: Client | OperationGroup,
402380
parent?: Client

src/CADL.Extension/Emitter.Csharp/src/lib/model.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@ import {
2121
resolveUsages,
2222
Type,
2323
UsageFlags,
24-
UsageTracker,
25-
TrackableType,
2624
getDiscriminator,
2725
IntrinsicType,
2826
isVoidType,
2927
isArrayModelType,
3028
isRecordModelType,
3129
Scalar,
32-
Union
30+
Union,
31+
getProjectedNames
3332
} from "@cadl-lang/compiler";
3433
import { getResourceOperation } from "@cadl-lang/rest";
3534
import {
@@ -39,6 +38,11 @@ import {
3938
HttpOperation,
4039
isStatusCode
4140
} from "@cadl-lang/rest/http";
41+
import {
42+
projectedNameClientKey,
43+
projectedNameCSharpKey,
44+
projectedNameJsonKey
45+
} from "../constants.js";
4246
import { InputEnumTypeValue } from "../type/InputEnumTypeValue.js";
4347
import { InputModelProperty } from "../type/InputModelProperty.js";
4448
import {
@@ -72,8 +76,8 @@ export function mapCadlTypeToCSharpInputTypeKind(
7276
case "Enum":
7377
return InputTypeKind.Enum;
7478
case "Number":
75-
let nubmerValue = cadlType.value;
76-
if (nubmerValue % 1 === 0) {
79+
let numberValue = cadlType.value;
80+
if (numberValue % 1 === 0) {
7781
return InputTypeKind.Int32;
7882
}
7983
return InputTypeKind.Float64;
@@ -512,15 +516,22 @@ export function getInputType(
512516
isReadOnly = true;
513517
}
514518
if (isNeverType(value.type) || isVoidType(value.type)) return;
519+
const projectedNamesMap = getProjectedNames(program, value);
520+
const name =
521+
projectedNamesMap?.get(projectedNameCSharpKey) ??
522+
projectedNamesMap?.get(projectedNameClientKey) ??
523+
value.name;
524+
const serializedName =
525+
projectedNamesMap?.get(projectedNameJsonKey) ?? value.name;
515526
const inputProp = {
516-
Name: value.name,
517-
SerializedName: value.name,
527+
Name: name,
528+
SerializedName: serializedName,
518529
Description: getDoc(program, value) ?? "",
519530
Type: getInputType(program, value.type, models, enums),
520531
IsRequired: !value.optional,
521532
IsReadOnly: isReadOnly,
522533
IsDiscriminator: false
523-
};
534+
} as InputModelProperty;
524535
outputProperties.push(inputProp);
525536
}
526537
});

src/CADL.Extension/Emitter.Csharp/src/type/InputModelProperty.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export interface InputModelProperty {
1010
Type: InputType;
1111
IsRequired: boolean;
1212
IsReadOnly: boolean;
13+
IsDiscriminator: boolean;
1314
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System.Threading.Tasks;
5+
using AutoRest.TestServer.Tests.Infrastructure;
6+
using Azure;
7+
using NUnit.Framework;
8+
using ProjectedName;
9+
using ProjectedName.Models;
10+
11+
namespace CadlRanchProjects.Tests
12+
{
13+
public class ProjectionTests : CadlRanchTestBase
14+
{
15+
[Test]
16+
public Task ProjectedName_jsonProjection() => Test(async (host) =>
17+
{
18+
Project project = new Project()
19+
{
20+
ProducedBy = "DPG",
21+
};
22+
Response response = await new ProjectedNameClient(host, null).JsonProjectionAsync(project);
23+
Assert.AreEqual(204, response.Status);
24+
});
25+
26+
[Test]
27+
public Task ProjectedName_clientProjection() => Test(async (host) =>
28+
{
29+
Project project = new Project()
30+
{
31+
CreatedBy = "DPG",
32+
};
33+
Response response = await new ProjectedNameClient(host, null).ClientProjectionAsync(project);
34+
Assert.AreEqual(204, response.Status);
35+
});
36+
37+
[Test]
38+
public Task ProjectedName_languageProjection() => Test(async (host) =>
39+
{
40+
Project project = new Project()
41+
{
42+
MadeForCS = "customers"
43+
};
44+
Response response = await new ProjectedNameClient(host, null).LanguageProjectionAsync(project);
45+
Assert.AreEqual(204, response.Status);
46+
});
47+
}
48+
}

test/CadlRanchProjects/projection/Generated/Configuration.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)