Skip to content

Commit e189062

Browse files
authored
Collection format (#3158)
* emit collection format
1 parent 8ed9805 commit e189062

File tree

14 files changed

+1573
-3
lines changed

14 files changed

+1573
-3
lines changed

src/AutoRest.CSharp/CommandLineOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ internal class CommandLineOptions
2828
[Option(longName: "server", Required = false, Default = null, HelpText = "Server argument.")]
2929
public string? Server { get; set; }
3030

31-
[Option('c', "clear-output-folder", Required = false, Default = false, HelpText = "Clear the output folder before generating code.")]
31+
[Option('x', "clear-output-folder", Required = false, Default = false, HelpText = "Clear the output folder before generating code.")]
3232
public bool ClearOutputFolder { get; set; }
3333
}
3434
}

src/AutoRest.CSharp/Properties/launchSettings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@
220220
"commandName": "Project",
221221
"commandLineArgs": "--standalone $(SolutionDir)\\test\\TestProjects\\CollapseRequestCondition-LowLevel\\Generated"
222222
},
223+
"CollectionFormat-Cadl": {
224+
"commandName": "Project",
225+
"commandLineArgs": "--standalone $(SolutionDir)\\test\\TestProjects\\CollectionFormat-Cadl\\Generated"
226+
},
223227
"constants": {
224228
"commandName": "Project",
225229
"commandLineArgs": "--standalone $(SolutionDir)\\test\\TestServerProjects\\constants\\Generated"

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { BodyMediaType } from "./type/BodyMediaType.js";
3939
import { InputParameter } from "./type/InputParameter.js";
4040
import {
4141
InputEnumType,
42+
InputListType,
4243
InputModelType,
4344
InputPrimitiveType,
4445
InputType
@@ -96,6 +97,10 @@ import {
9697
resolveOptions,
9798
resolveOutputFolder
9899
} from "./options.js";
100+
import {
101+
CollectionFormat,
102+
collectionFormatToDelimMap
103+
} from "./type/CollectionFormat.js";
99104

100105
export const $lib = createCadlLibrary({
101106
name: "cadl-csharp",
@@ -743,6 +748,10 @@ function loadOperation(
743748
parameter: HttpOperationParameter
744749
): InputParameter {
745750
const { type: location, name, param } = parameter;
751+
let format = undefined;
752+
if (parameter.type !== "path") {
753+
format = parameter.format;
754+
}
746755
const cadlType = param.type;
747756
const inputType: InputType = getInputType(
748757
program,
@@ -784,8 +793,14 @@ function loadOperation(
784793
IsContentType: isContentType,
785794
IsEndpoint: false,
786795
SkipUrlEncoding: false, //TODO: retrieve out value from extension
787-
Explode: false,
788-
Kind: kind
796+
Explode:
797+
(inputType as InputListType).ElementType && format === "multi"
798+
? true
799+
: false,
800+
Kind: kind,
801+
ArraySerializationDelimiter: format
802+
? collectionFormatToDelimMap[format]
803+
: undefined
789804
} as InputParameter;
790805
}
791806

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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 enum CollectionFormat {
5+
CSV = "csv",
6+
SSV = "ssv",
7+
TSV = "tsv",
8+
Pipes = "pipes",
9+
Multi = "multi"
10+
}
11+
12+
export const collectionFormatToDelimMap: {
13+
[key in CollectionFormat]: string | undefined;
14+
} = {
15+
[CollectionFormat.CSV]: ",",
16+
[CollectionFormat.SSV]: " ",
17+
[CollectionFormat.TSV]: "\t",
18+
[CollectionFormat.Pipes]: "|",
19+
[CollectionFormat.Multi]: undefined
20+
};
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { passOnSuccess, ScenarioMockApi, mockapi, json } from "@azure-tools/cadl-ranch-api";
2+
3+
export const Scenarios: Record<string, ScenarioMockApi> = {};
4+
5+
const options = ["value1", "value2", "value3"];
6+
const thing = {
7+
name: "collectionFormat"
8+
};
9+
Scenarios.CollectionFormat_CsvQuery = passOnSuccess(
10+
mockapi.get("/csvQuery", (req) => {
11+
req.expect.containsQueryParam("options", "value1,value2,value3");
12+
console.log(req.query["options"]);
13+
return {
14+
status: 200,
15+
body: json(thing),
16+
};
17+
}),
18+
);
19+
20+
Scenarios.CollectionFormat_MultiQuery = passOnSuccess(
21+
mockapi.get("/multiQuery", (req) => {
22+
if (req.query.options.length !== 3) {
23+
throw new Error("Query parameter 'options' should exploded as 3 query parameters.");
24+
}
25+
if (JSON.stringify(["value1", "value2", "value3"]) !== JSON.stringify(req.query.options)) {
26+
throw new Error("Query parameter 'options' is not correct.")
27+
}
28+
return {
29+
status: 200,
30+
body: json(thing),
31+
};
32+
}),
33+
);
34+
35+
Scenarios.CollectionFormat_NoFormatQuery = passOnSuccess(
36+
mockapi.get("/noFormatQuery", (req) => {
37+
if (req.query.options.length !== 3) {
38+
throw new Error("Query parameter 'options' should exploded as 3 query parameters.");
39+
}
40+
if (JSON.stringify(["value1", "value2", "value3"]) !== JSON.stringify(req.query.options)) {
41+
throw new Error("Query parameter 'options' is not correct.")
42+
}
43+
return {
44+
status: 200,
45+
body: json(thing),
46+
};
47+
}),
48+
);
49+
50+
Scenarios.CollectionFormat_CsvHeader = passOnSuccess(
51+
mockapi.get("/csvHeader", (req) => {
52+
req.expect.containsHeader("options", "value1,value2,value3");
53+
return {
54+
status: 200,
55+
body: json(thing),
56+
};
57+
}),
58+
);
59+
60+
Scenarios.CollectionFormat_NoFormateHeader = passOnSuccess(
61+
mockapi.get("/noFormatHeader", (req) => {
62+
req.expect.containsHeader("options", "value1,value2,value3");
63+
return {
64+
status: 200,
65+
body: json(thing),
66+
};
67+
}),
68+
);
69+
70+
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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 NUnit.Framework;
7+
using Azure;
8+
using System.Net;
9+
using Azure.Core;
10+
using Azure.Identity;
11+
using Spread.Models;
12+
using ArrayAsQueryOrHeader;
13+
using System.Collections.Generic;
14+
15+
namespace CadlRanchProjects.Tests
16+
{
17+
public class CollectionFormatCadlTests : CadlRanchMockApiTestBase
18+
{
19+
[Test]
20+
public Task CollectionFormat_CsvQueryAsync() => Test(async (host) =>
21+
{
22+
List<string> options = new List<string>() { "value1", "value2", "value3" };
23+
Response response = await new ArrayAsQueryOrHeaderClient(host).CsvQueryAsync(options);
24+
Assert.AreEqual(200, response.Status);
25+
});
26+
27+
[Test]
28+
public Task CollectionFormat_MultiQueryAsync() => Test(async (host) =>
29+
{
30+
List<string> options = new List<string>(){"value1", "value2", "value3"};
31+
Response response = await new ArrayAsQueryOrHeaderClient(host).MultiQueryAsync(options);
32+
Assert.AreEqual(200, response.Status);
33+
});
34+
35+
[Test]
36+
public Task CollectionFormat_NoFormatQueryAsync() => Test(async (host) =>
37+
{
38+
List<string> options = new List<string>() { "value1", "value2", "value3" };
39+
Response response = await new ArrayAsQueryOrHeaderClient(host).NoFormatQueryAsync(options);
40+
Assert.AreEqual(200, response.Status);
41+
});
42+
43+
[Test]
44+
public Task CollectionFormat_CsvHeaderAsync() => Test(async (host) =>
45+
{
46+
List<string> options = new List<string>(){"value1", "value2", "value3"};
47+
Response response = await new ArrayAsQueryOrHeaderClient(host).CsvHeaderAsync(options);
48+
Assert.AreEqual(200, response.Status);
49+
});
50+
51+
[Test]
52+
public Task CollectionFormat_NoFormatHeaderAsync() => Test(async (host) =>
53+
{
54+
List<string> options = new List<string>() { "value1", "value2", "value3" };
55+
Response response = await new ArrayAsQueryOrHeaderClient(host).NoFormatHeaderAsync(options);
56+
Assert.AreEqual(200, response.Status);
57+
});
58+
}
59+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import "@cadl-lang/rest";
2+
import "@cadl-lang/openapi";
3+
import "@azure-tools/cadl-dpg";
4+
import "@azure-tools/cadl-azure-core";
5+
6+
@service(
7+
{
8+
title: "Array Query and Header",
9+
version: "0.1.0"
10+
}
11+
)
12+
@doc("CADL project to test array as query or header.")
13+
@server(
14+
"{CollectionFormatCadlUrl}",
15+
"Endpoint Service",
16+
{
17+
CollectionFormatCadlUrl: string,
18+
}
19+
)
20+
namespace ArrayAsQueryOrHeader;
21+
22+
using Cadl.Http;
23+
using Cadl.Rest;
24+
using Azure.DPG;
25+
using Azure.Core;
26+
27+
model Thing {
28+
@doc("the object name.")
29+
name: string;
30+
}
31+
32+
@get
33+
@route("/csvQuery")
34+
op csvQuery(@query({format: "csv"}) options?: string[]): Thing;
35+
36+
@get
37+
@route("/multiQuery")
38+
op multiQuery(@query({format: "multi"}) options?: string[]): Thing;
39+
40+
@get
41+
@route("/noFormatQuery")
42+
op noFormatQuery(@query options?: string[]): Thing;
43+
44+
@get
45+
@route("/csvHeader")
46+
op csvHeader(@header({format: "csv"}) options?: string[]): Thing;
47+
48+
@get
49+
@route("/noFormatHeader")
50+
op noFormatHeader(@header options?: string[]): Thing;

test/TestProjects/CollectionFormat-Cadl/CollectionFormat.csproj

Lines changed: 13 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)