Skip to content

Commit

Permalink
upgrade tsp compiler to 0.61 (#5027)
Browse files Browse the repository at this point in the history
1. Upgrade tsp compiler to 0.61
2. Fix paging response issues by:
2.1 if user customize the paging response then use the customized mode.
2.2 if user defined model is the same as ResourceListResult, then
replace it by ResourceListResult
2.3 if interface name conflicts with model name, then rename interface
name
3. Support customize LRO
4. Support ArmProviderActionAsync template.

---------

Co-authored-by: Pan Shao <pashao@microsoft.com>
  • Loading branch information
pshao25 and Pan Shao authored Oct 16, 2024
1 parent 976033f commit ab37a4d
Show file tree
Hide file tree
Showing 210 changed files with 3,368 additions and 1,481 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@autorest/openapi-to-typespec",
"comment": "Fix paging, LRO and provideraction template. Upgrade compiler to 0.61",
"type": "patch"
}
],
"packageName": "@autorest/openapi-to-typespec"
}
309 changes: 161 additions & 148 deletions common/config/rush/pnpm-lock.yaml

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions packages/extensions/openapi-to-typespec/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@
"@azure-tools/codegen": "~2.10.0",
"@autorest/extension-base": "~3.6.0",
"@autorest/codemodel": "~4.20.0",
"@typespec/compiler": "^0.60.0",
"@typespec/rest": "^0.60.0",
"@typespec/http": "^0.60.0",
"@typespec/versioning": "^0.60.1",
"@typespec/prettier-plugin-typespec": "^0.60.0",
"@azure-tools/typespec-azure-core": "^0.46.0",
"@azure-tools/typespec-autorest": "^0.46.0",
"@azure-tools/typespec-azure-resource-manager": "^0.46.0",
"@typespec/openapi": "^0.60.0",
"@typespec/openapi3": "^0.60.0",
"@typespec/compiler": "^0.61.0",
"@typespec/rest": "^0.61.0",
"@typespec/http": "^0.61.0",
"@typespec/versioning": "^0.61.0",
"@typespec/prettier-plugin-typespec": "^0.61.0",
"@azure-tools/typespec-azure-core": "^0.47.0",
"@azure-tools/typespec-autorest": "^0.47.0",
"@azure-tools/typespec-azure-resource-manager": "^0.47.0",
"@typespec/openapi": "^0.61.0",
"@typespec/openapi3": "^0.61.0",
"prettier": "~3.1.0",
"lodash": "~4.17.20",
"pluralize": "^8.0.0",
Expand All @@ -68,9 +68,9 @@
"fs-extra": "^10.1.0",
"@types/fs-extra": "^9.0.13",
"chalk": "^4.1.0",
"@azure-tools/typespec-autorest": "^0.46.0",
"@azure-tools/typespec-client-generator-core": "^0.46.0",
"@azure-tools/typespec-azure-rulesets": "^0.46.0",
"@azure-tools/typespec-autorest": "^0.47.0",
"@azure-tools/typespec-client-generator-core": "^0.47.0",
"@azure-tools/typespec-azure-rulesets": "^0.47.0",
"webpack-cli": "~5.1.4",
"webpack": "~5.89.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { Case } from "change-case-all";
import { TypespecOperation, TspArmResource } from "interfaces";
import { TypespecOperation, TspArmResource, TypespecProgram } from "interfaces";
import _ from "lodash";
import pluralize from "pluralize";
import { getArmCommonTypeVersion } from "../autorest-session";
import { getOptions } from "../options";
import { replaceGeneratedResourceObject } from "../transforms/transform-arm-resources";
import { getTSPOperationGroupName } from "../transforms/transform-arm-resources";
import { generateAugmentedDecorators, generateDecorators } from "../utils/decorators";
import { generateDocs } from "../utils/docs";
import { getModelPropertiesDeclarations } from "../utils/model-generation";
Expand Down Expand Up @@ -78,15 +77,11 @@ function generateArmResourceModel(resource: TspArmResource): string {
function generateArmResourceOperation(resource: TspArmResource): string {
const { isFullCompatible } = getOptions();

const formalOperationGroupName = pluralize(resource.name);
const formalOperationGroupName = getTSPOperationGroupName(resource.name);
const definitions: string[] = [];

definitions.push("@armResourceOperations");
if (resource.name === formalOperationGroupName) {
definitions.push(`interface ${formalOperationGroupName}OperationGroup {`);
} else {
definitions.push(`interface ${formalOperationGroupName} {`);
}
definitions.push(`interface ${formalOperationGroupName} {`);

for (const operation of resource.resourceOperations) {
for (const fixme of operation.fixMe ?? []) {
Expand All @@ -109,11 +104,7 @@ function generateArmResourceOperation(resource: TspArmResource): string {
if (operation.kind === "ArmResourceExists") {
definitions.push(`op ${operation.name}(${operation.parameters.join(",")}): ${operation.responses.join("|")}`);
} else if (operation.templateParameters?.length) {
definitions.push(
`${operation.name} is ${operation.kind}<${(operation.templateParameters ?? [])
.map(replaceGeneratedResourceObject)
.join(",")}>`,
);
definitions.push(`${operation.name} is ${operation.kind}<${(operation.templateParameters ?? []).join(",")}>`);
} else {
definitions.push(`${operation.name} is ${operation.kind}`);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { TypespecOperation, TypespecOperationGroup, TypespecParameter } from "../interfaces";
import {
TspArmProviderActionOperation,
TypespecOperation,
TypespecOperationGroup,
TypespecParameter,
} from "../interfaces";
import { getOptions } from "../options";
import { replaceGeneratedResourceObject } from "../transforms/transform-arm-resources";
import { generateDecorators } from "../utils/decorators";
import { generateDocs, generateSummary } from "../utils/docs";
import { generateParameter } from "./generate-parameter";
Expand All @@ -10,12 +14,12 @@ export function generateOperation(operation: TypespecOperation, operationGroup?:
const doc = generateDocs(operation);
const summary = generateSummary(operation);
const { verb, name, route, parameters } = operation;
const responses = operation.responses.map(replaceGeneratedResourceObject);
const params = generateParameters(parameters);
const statements: string[] = [];
summary && statements.push(summary);
statements.push(doc);
generateMultiResponseWarning(responses, statements);
const modelResponses = [...new Set(operation.responses.filter((r) => r[1] !== "void").map((r) => r[1]))];
generateMultiResponseWarning(modelResponses, statements);

for (const fixme of operation.fixMe ?? []) {
statements.push(fixme);
Expand All @@ -26,25 +30,33 @@ export function generateOperation(operation: TypespecOperation, operationGroup?:
}
if (isArm) {
statements.push(`@route("${route}")`);
const otherResponses = operation.responses
.filter((r) => r[1] === "void" && ["200", "202"].includes(r[0]))
.map((r) => {
if (r[0] === "200") return "OkResponse";
if (r[0] === "202") return "ArmAcceptedResponse";
});
statements.push(
`@${verb} op \`${name}\`(
...ApiVersionParameter,
${params}
): ArmResponse<${responses.join(" | ")}> | ErrorResponse;\n\n\n`,
): ${modelResponses.length > 0 ? `ArmResponse<${modelResponses.join(" | ")}>` : ""}${
otherResponses.length > 0 ? `| ${otherResponses.join("|")}` : ""
} | ErrorResponse;\n\n\n`,
);
} else if (!operation.resource) {
const names = [name, ...responses, ...parameters.map((p) => p.name)];
const names = [name, ...modelResponses, ...parameters.map((p) => p.name)];
const duplicateNames = findDuplicates(names);
generateNameCollisionWarning(duplicateNames, statements);
statements.push(`@route("${route}")`);
statements.push(
`@${verb} op \`${name}\` is Azure.Core.Foundations.Operation<${params ? params : "{}"}, ${responses.join(
" | ",
)}>;\n\n\n`,
`@${verb} op \`${name}\` is Azure.Core.Foundations.Operation<${params ? params : "{}"}, ${
modelResponses.length > 0 ? `${modelResponses.join(" | ")}` : "void"
}>;\n\n\n`,
);
} else {
const { resource } = operation;
const names = [name, ...responses, ...parameters.map((p) => p.name)];
const names = [name, ...modelResponses, ...parameters.map((p) => p.name)];
const duplicateNames = findDuplicates(names);
generateNameCollisionWarning(duplicateNames, statements);
const resourceParameters = generateParameters(
Expand All @@ -59,6 +71,55 @@ export function generateOperation(operation: TypespecOperation, operationGroup?:
return statements.join("\n");
}

export function generateProviderAction(operation: TspArmProviderActionOperation) {
const doc = generateDocs(operation);
const summary = generateSummary(operation);
const statements: string[] = [];
summary && statements.push(summary);
statements.push(doc);
const templateParameters = [];
const responses = [...new Set(operation.responses)];
// Workaround for array response, refactor later.
const response =
responses.length === 1 && responses[0].endsWith("[]") ? `{@body _: ${responses[0]}}` : responses.join("|");
if (response !== "void") {
templateParameters.push(`Response = ${response}`);
}
templateParameters.push(`Scope = ${operation.scope}`);

if (operation.parameters.length > 0) {
const params: string[] = [];
for (const parameter of operation.parameters) {
if (parameter.name === "subscriptionId") continue;
if (parameter.name === "location") {
params.push("...LocationParameter");
} else {
params.push(generateParameter(parameter));
}
}

if (params.length === 1 && params[0] === "...LocationParameter") {
templateParameters.push(`Parameters = LocationParameter`);
} else {
templateParameters.push(`Parameters = {${params}}`);
}
}
if (operation.request) {
templateParameters.push(`Request = ${operation.request.type}`);
}

if (operation.decorators) {
statements.push(generateDecorators(operation.decorators));
}
statements.push(`@${operation.verb}`);
if (operation.action) {
statements.push(`@action("${operation.action}")`);
}

statements.push(`${operation.name} is ${operation.kind}<${(templateParameters ?? []).join(",")}>`);
return statements.join("\n");
}

function findDuplicates(arr: string[]) {
return arr.filter((item, index) => arr.indexOf(item) != index);
}
Expand Down Expand Up @@ -112,9 +173,19 @@ export function generateOperationGroup(operationGroup: TypespecOperationGroup) {

statements.push(`${doc}`);
const hasInterface = Boolean(name);
const hasProvider =
operations.find((o) => (o as TspArmProviderActionOperation).kind === "ArmProviderActionAsync") !== undefined;
if (hasProvider && hasInterface) {
statements.push(`@armResourceOperations`);
}
hasInterface && statements.push(`interface ${name} {`);

for (const operation of operations) {
statements.push(generateOperation(operation, operationGroup));
if ((operation as TspArmProviderActionOperation).kind === "ArmProviderActionAsync") {
statements.push(generateProviderAction(operation as TspArmProviderActionOperation));
} else {
statements.push(generateOperation(operation as TypespecOperation, operationGroup));
}
}
hasInterface && statements.push(`}`);

Expand Down
17 changes: 15 additions & 2 deletions packages/extensions/openapi-to-typespec/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ export interface WithSummary {

export interface TypespecOperationGroup extends WithDoc {
name: string;
operations: TypespecOperation[];
operations: (TypespecOperation | TspArmProviderActionOperation)[];
}

export type Extension = "Pageable" | "LRO";
export interface TypespecOperation extends WithDoc, WithSummary, WithFixMe {
name: string;
verb: "get" | "post" | "put" | "delete";
route: string;
responses: string[];
responses: [string, string][];
parameters: TypespecParameter[];
extensions: Extension[];
resource?: TypespecResource;
Expand Down Expand Up @@ -222,6 +222,19 @@ export interface TspArmResourceOperationBase extends WithDoc, WithFixMe, WithSup
customizations?: string[];
}

// TO-DO: consolidate with other templates
export interface TspArmProviderActionOperation extends WithDoc, WithSummary {
kind: "ArmProviderActionAsync";
name: string;
action?: string;
responses?: string[];
verb: string;
scope?: "TenantActionScope" | "SubscriptionActionScope";
parameters: TypespecParameter[];
request?: TypespecParameter;
decorators?: TypespecDecorator[];
}

export type TspArmResourceOperation =
| TspArmResourceListOperation
| TspArmResourceNonListOperation
Expand Down
Loading

0 comments on commit ab37a4d

Please sign in to comment.