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
2 changes: 2 additions & 0 deletions eng/testProjects.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"authentication/oauth2",
"authentication/union",
"azure/core/basic",
"azure/core/lro/rpc-legacy",
"azure/core/lro/standard",
"azure/core/traits",
"azure/client-generator-core/access",
"azure/client-generator-core/usage",
Expand Down
39 changes: 28 additions & 11 deletions src/AutoRest.CSharp/LowLevel/Output/OperationMethodChainBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public LowLevelClientMethod BuildOperationMethodChain()
var diagnostic = new Diagnostic($"{_clientName}.{_restClientMethod.Name}");

var requestBodyType = Operation.Parameters.FirstOrDefault(p => p.Location == RequestLocation.Body)?.Type;
var responseBodyType = Operation.Responses.FirstOrDefault()?.BodyType;
var responseBodyType = GetReturnedResponseInputType();

// samples will build below
var samples = new List<DpgOperationSample>();
Expand Down Expand Up @@ -255,16 +255,7 @@ private bool IsParameterTypeSame(Parameter? first, Parameter? second)

private ReturnTypeChain BuildReturnTypes()
{
var operationBodyTypes = Operation.Responses.Where(r => !r.IsErrorResponse).Select(r => r.BodyType).Distinct().ToArray();
CSharpType? responseType = null;
if (operationBodyTypes.Length != 0)
{
var firstBodyType = operationBodyTypes[0];
if (firstBodyType != null)
{
responseType = TypeFactory.GetOutputType(_typeFactory.CreateType(firstBodyType));
}
};
CSharpType? responseType = GetReturnedResponseCSharpType();

if (Operation.Paging != null)
{
Expand Down Expand Up @@ -322,6 +313,32 @@ private ReturnTypeChain BuildReturnTypes()
return new ReturnTypeChain(typeof(Response), typeof(Response), null);
}

private CSharpType? GetReturnedResponseCSharpType()
{
var inputType = GetReturnedResponseInputType();
if (inputType != null)
{
return TypeFactory.GetOutputType(_typeFactory.CreateType(inputType));
}
return null;
}

private InputType? GetReturnedResponseInputType()
{
if (Operation.LongRunning != null)
{
return Operation.LongRunning.FinalResponse.BodyType;
}

var operationBodyTypes = Operation.Responses.Where(r => !r.IsErrorResponse).Select(r => r.BodyType).Distinct();
if (operationBodyTypes.Any())
{
return operationBodyTypes.First();
}

return null;
}

private ConvenienceMethod? BuildConvenienceMethod(bool shouldRequestContextOptional, ConvenienceMethodGenerationInfo generationInfo)
{
if (!generationInfo.IsConvenienceMethodGenerated)
Expand Down
12 changes: 8 additions & 4 deletions src/AutoRest.CSharp/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,10 +308,6 @@
"commandName": "Project",
"commandLineArgs": "--standalone $(SolutionDir)\\test\\TestServerProjects\\lro\\Generated"
},
"Lro-Basic-TypeSpec": {
"commandName": "Project",
"commandLineArgs": "--standalone $(SolutionDir)\\test\\TestProjects\\Lro-Basic-TypeSpec\\src\\Generated -n"
},
"lro-LowLevel": {
"commandName": "Project",
"commandLineArgs": "--standalone $(SolutionDir)\\test\\TestServerProjectsLowLevel\\lro\\src\\Generated"
Expand Down Expand Up @@ -636,6 +632,14 @@
"commandName": "Project",
"commandLineArgs": "--standalone $(SolutionDir)\\test\\CadlRanchProjects\\azure\\core\\basic\\src\\Generated -n"
},
"typespec-azure/core/lro/rpc-legacy": {
"commandName": "Project",
"commandLineArgs": "--standalone $(SolutionDir)\\test\\CadlRanchProjects\\azure\\core\\lro\\rpc-legacy\\src\\Generated -n"
},
"typespec-azure/core/lro/standard": {
"commandName": "Project",
"commandLineArgs": "--standalone $(SolutionDir)\\test\\CadlRanchProjects\\azure\\core\\lro\\standard\\src\\Generated -n"
},
"typespec-azure/core/traits": {
"commandName": "Project",
"commandLineArgs": "--standalone $(SolutionDir)\\test\\CadlRanchProjects\\azure\\core\\traits\\src\\Generated -n"
Expand Down
66 changes: 21 additions & 45 deletions src/TypeSpec.Extension/Emitter.Csharp/src/lib/operation.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

import { getOperationLink } from "@azure-tools/typespec-azure-core";
import { getLroMetadata } from "@azure-tools/typespec-azure-core";
import {
createSdkContext,
isApiVersion,
Expand Down Expand Up @@ -44,7 +44,7 @@ import {
isInputLiteralType,
isInputUnionType
} from "../type/inputType.js";
import { OperationFinalStateVia } from "../type/operationFinalStateVia.js";
import { convertLroFinalStateVia } from "../type/operationFinalStateVia.js";
import { OperationLongRunning } from "../type/operationLongRunning.js";
import { OperationPaging } from "../type/operationPaging.js";
import { OperationResponse } from "../type/operationResponse.js";
Expand Down Expand Up @@ -379,54 +379,30 @@ export function loadOperation(
op: HttpOperation,
resourceOperation?: ResourceOperation
): OperationLongRunning | undefined {
if (!isLongRunningOperation(context, op.operation)) return undefined;

const finalResponse = loadLongRunningFinalResponse(
context,
op,
resourceOperation
);
if (finalResponse === undefined) return undefined;

return {
FinalStateVia: OperationFinalStateVia.Location, // data plane only supports `location`
FinalResponse: finalResponse
} as OperationLongRunning;
}

function loadLongRunningFinalResponse(
context: SdkContext,
op: HttpOperation,
resourceOperation?: ResourceOperation
): OperationResponse | undefined {
let finalResponse: any | undefined;
for (const response of op.responses) {
if (response.statusCode === "200") {
finalResponse = response;
break;
}
if (response.statusCode === "204") {
finalResponse = response;
}
const metadata = getLroMetadata(program, op.operation);
if (metadata === undefined) {
return undefined;
}

if (finalResponse !== undefined) {
return loadOperationResponse(
context,
finalResponse,
resourceOperation
var bodyType = undefined;
if (op.verb !== "delete") {
const formattedType = getFormattedType(
program,
metadata.logicalResult
);
bodyType = getInputType(context, formattedType, models, enums);
}

return loadOperationResponse(
context,
op.responses[0],
resourceOperation
);
}

function isLongRunningOperation(context: SdkContext, op: Operation) {
return getOperationLink(context.program, op, "polling") !== undefined;
return {
FinalStateVia: convertLroFinalStateVia(metadata.finalStateVia),
FinalResponse: {
// in swagger, we allow delete to return some meaningful body content
// for now, let assume we don't allow return type
StatusCodes: op.verb === "delete" ? [204] : [200],
BodyType: bodyType,
BodyMediaType: BodyMediaType.Json
} as OperationResponse
} as OperationLongRunning;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { FinalStateValue } from "@azure-tools/typespec-azure-core";

export enum OperationFinalStateVia {
AzureAsyncOperation,
Location,
OriginalUri
OriginalUri,
OperationLocation,
CustomLink,
CustomOperationReference
}

export function convertLroFinalStateVia(
finalStateValue: FinalStateValue
): OperationFinalStateVia {
switch (finalStateValue) {
case FinalStateValue.azureAsyncOperation:
return OperationFinalStateVia.AzureAsyncOperation;
// TODO: we don't have implementation of custom-link and custom-operation-reference yet
// case FinalStateValue.customLink:
// return OperationFinalStateVia.CustomLink;

// And right now some existing API specs are not correctly defined so that they are parsed
// into `custom-operation-reference` which should be `operation-location`.
// so let's fallback `custom-operation-reference` into `operation-location` as a work-around
case FinalStateValue.customOperationReference:
return OperationFinalStateVia.OperationLocation;
case FinalStateValue.location:
return OperationFinalStateVia.Location;
case FinalStateValue.originalUri:
return OperationFinalStateVia.OriginalUri;
case FinalStateValue.operationLocation:
return OperationFinalStateVia.OperationLocation;
default:
throw `Unsupported LRO final state value: ${finalStateValue}`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,26 @@
"application/merge-patch+json"
],
"BufferResponse": true,
"LongRunning": {
"$id": "33",
"FinalStateVia": 3,
"FinalResponse": {
"$id": "34",
"StatusCodes": [
200
],
"BodyType": {
"$ref": "2"
},
"BodyMediaType": "Json"
}
},
"GenerateProtocolMethod": true,
"GenerateConvenienceMethod": false
}
],
"Protocol": {
"$id": "33"
"$id": "35"
},
"Creatable": true
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { FinalStateValue } from "@azure-tools/typespec-azure-core";
import {
OperationFinalStateVia,
convertLroFinalStateVia
} from "../../src/type/operationFinalStateVia.js";
import assert from "assert";
import { describe } from "mocha";

describe("convertLroFinalStateVia()", () => {
describe("normal inputs", () => {
const mappings: [FinalStateValue, OperationFinalStateVia][] = [
[
FinalStateValue.azureAsyncOperation,
OperationFinalStateVia.AzureAsyncOperation
],
[FinalStateValue.location, OperationFinalStateVia.Location],
[FinalStateValue.originalUri, OperationFinalStateVia.OriginalUri],
[
FinalStateValue.operationLocation,
OperationFinalStateVia.OperationLocation
]
];
for (const [input, output] of mappings) {
it(`should return '${output}' for '${input}'`, function () {
assert.equal(convertLroFinalStateVia(input), output);
});
}
});

describe("unsupported inputs", () => {
const unsupportedInputs = [FinalStateValue.customLink];
for (const input of unsupportedInputs) {
it(`should throw exception for unsupported input '${input}'`, function () {
assert.throws(
() => convertLroFinalStateVia(input),
new RegExp(`Unsupported LRO final state value: ${input}`)
);
});
}
});
});
73 changes: 0 additions & 73 deletions test/CadlRanchProjects.Tests/Lro-Basic-TypeSpec.cs

This file was deleted.

Loading