Skip to content

Commit

Permalink
Refactor resource operation generation to accept customization (#5048)
Browse files Browse the repository at this point in the history
1. Upgrade compiler version to 0.63
2. Refactor resource operation template so that it could accept
customizations
3. Enable tests "arm-alertsmanagement",
"arm-analysisservices","arm-azureintegrationspaces"

---------

Co-authored-by: Pan Shao <[email protected]>
  • Loading branch information
pshao25 and Pan Shao authored Dec 19, 2024
1 parent fbd40b8 commit 5a09cb7
Show file tree
Hide file tree
Showing 336 changed files with 16,530 additions and 5,172 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@autorest/openapi-to-typespec",
"comment": "Refactor resource operation template to accept customzations",
"type": "patch"
}
],
"packageName": "@autorest/openapi-to-typespec"
}
308 changes: 156 additions & 152 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.62.0",
"@typespec/rest": "^0.62.0",
"@typespec/http": "^0.62.0",
"@typespec/versioning": "^0.62.0",
"@typespec/prettier-plugin-typespec": "^0.62.0",
"@azure-tools/typespec-azure-core": "^0.48.0",
"@azure-tools/typespec-autorest": "^0.48.0",
"@azure-tools/typespec-azure-resource-manager": "^0.48.0",
"@typespec/openapi": "^0.62.0",
"@typespec/openapi3": "^0.62.0",
"@typespec/compiler": "^0.63.0",
"@typespec/rest": "^0.63.0",
"@typespec/http": "^0.63.0",
"@typespec/versioning": "^0.63.0",
"@typespec/prettier-plugin-typespec": "^0.63.0",
"@azure-tools/typespec-azure-core": "^0.49.0",
"@azure-tools/typespec-autorest": "^0.49.0",
"@azure-tools/typespec-azure-resource-manager": "^0.49.0",
"@typespec/openapi": "^0.63.0",
"@typespec/openapi3": "^0.63.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.48.0",
"@azure-tools/typespec-client-generator-core": "^0.48.0",
"@azure-tools/typespec-azure-rulesets": "^0.48.0",
"@azure-tools/typespec-autorest": "^0.49.0",
"@azure-tools/typespec-client-generator-core": "^0.49.0",
"@azure-tools/typespec-azure-rulesets": "^0.49.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
Expand Up @@ -15,7 +15,7 @@ export async function emitTypespecConfig(filePath: string, programDetails: Types
"@azure-tools/typespec-autorest":
azure-resource-provider-folder: "data-plane"
emitter-output-dir: "{project-root}/.."
examples-directory: "{project-root}/examples"
examples-dir: "{project-root}/examples"
output-file: "{azure-resource-provider-folder}/{service-name}/{version-status}/{version}/openapi.json"
# Uncomment this line and add "@azure-tools/typespec-python" to your package.json to generate Python code
# "@azure-tools/typespec-python":
Expand Down Expand Up @@ -43,7 +43,7 @@ options:
emitter-output-dir: "{project-root}/.."
azure-resource-provider-folder: "resource-manager"
output-file: "{azure-resource-provider-folder}/{service-name}/{version-status}/{version}/${swaggerName}"
examples-directory: "{project-root}/examples"${
examples-dir: "{project-root}/examples"${
isFullCompatible
? `
arm-resource-flattening: true`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import { Case } from "change-case-all";
import { TypespecOperation, TspArmResource, TypespecProgram } from "interfaces";
import _ from "lodash";
import pluralize from "pluralize";
import {
TypespecOperation,
TspArmResource,
isArmResourceActionOperation,
TypespecTemplateModel,
TypespecVoidType,
TspLroHeaders,
TypespecParameter,
TypespecDataType,
} from "../interfaces";
import { getOptions } from "../options";
import { getTSPOperationGroupName } from "../transforms/transform-arm-resources";
import { generateAugmentedDecorators, generateDecorators } from "../utils/decorators";
import { generateDocs } from "../utils/docs";
import { getLogger } from "../utils/logger";
import { getModelPropertiesDeclarations } from "../utils/model-generation";
import { generateSuppressions } from "../utils/suppressions";
import { generateOperation } from "./generate-operations";
import { generateOperation, generateParameters } from "./generate-operations";
import { generateParameter } from "./generate-parameter";

const logger = () => getLogger("generate-arm-resource");

Expand All @@ -29,7 +39,7 @@ export function generateArmResource(resource: TspArmResource): string {
}

for (const o of resource.resourceOperations) {
for (const d of o.customizations ?? []) {
for (const d of o.augmentedDecorators ?? []) {
definitions.push(`${d}`);
}
}
Expand Down Expand Up @@ -104,14 +114,37 @@ function generateArmResourceOperation(resource: TspArmResource): string {
if (isFullCompatible && operation.suppressions) {
definitions.push(...generateSuppressions(operation.suppressions));
}
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 ?? []).join(",")}>`);

if (operation.kind === "ArmResourceActionSync" || operation.kind === "ArmResourceActionAsync") {
definitions.push(
`${operation.name} is ${operation.kind}<${operation.resource}, ${generateArmRequest(
operation.request,
)}, ${generateArmResponse(operation.response)}${
operation.baseParameters ? `, BaseParameters = ${operation.baseParameters[0]}` : ""
}${operation.parameters ? `, Parameters = { ${generateParameters(operation.parameters)} }` : ""}${
operation.lroHeaders ? `, LroHeaders = ${generateLroHeaders(operation.lroHeaders)}` : ""
}>`,
);
} else if (operation.kind === "ArmResourceActionAsyncBase") {
definitions.push(
`${operation.name} is ${operation.kind}<${operation.resource}, ${generateArmRequest(
operation.request,
)}, ${generateArmResponse(operation.response)}${operation.baseParameters![0]}${
operation.parameters ? `, Parameters = { ${generateParameters(operation.parameters)} }` : ""
}>`,
);
} else {
definitions.push(`${operation.name} is ${operation.kind}`);
definitions.push(
`${operation.name} is ${operation.kind}<${operation.resource}${
operation.patchModel ? `, PatchModel = ${operation.patchModel}` : ""
}${operation.baseParameters ? `, BaseParameters = ${operation.baseParameters[0]}` : ""}${
operation.parameters ? `, Parameters = { ${generateParameters(operation.parameters)} }` : ""
}${operation.response ? `, Response = ${generateArmResponse(operation.response)}` : ""}${
operation.lroHeaders ? `, LroHeaders = ${generateLroHeaders(operation.lroHeaders)}` : ""
}>`,
);
}
definitions.push("");
definitions.push("\n");
}
for (const operation of resource.normalOperations) {
if (
Expand All @@ -131,6 +164,49 @@ function generateArmResourceOperation(resource: TspArmResource): string {
return definitions.join("\n");
}

function generateArmRequest(request: TypespecParameter | TypespecVoidType | TypespecDataType): string {
if (request.kind === "void") {
return "void";
}

if (request.kind === "parameter") {
return `{${generateParameter(request as TypespecParameter)}}`;
}

return request.name;
}

function generateLroHeaders(lroHeaders: TspLroHeaders): string {
if (lroHeaders === "Azure-AsyncOperation") {
return "ArmAsyncOperationHeader & Azure.Core.Foundations.RetryAfterHeader";
} else if (lroHeaders === "Location") {
return "ArmLroLocationHeader & Azure.Core.Foundations.RetryAfterHeader";
}
throw new Error(`Unknown LRO header: ${lroHeaders}`);
}

function generateArmResponse(responses: TypespecTemplateModel[] | TypespecVoidType): string {
if (!Array.isArray(responses)) {
return "void";
}

return responses.map((r) => generateTemplateModel(r)).join(" | ");
}

export function generateTemplateModel(templateModel: TypespecTemplateModel): string {
return `${templateModel.name}${
templateModel.arguments
? `<${templateModel.arguments
.map((a) => (a.kind === "template" ? generateTemplateModel(a as TypespecTemplateModel) : a.name))
.join(",")}>`
: ""
}${
templateModel.additionalProperties
? ` & { ${templateModel.additionalProperties.map((p) => generateParameter(p)).join(";")} }`
: ""
}${templateModel.additionalTemplateModel ? templateModel.additionalTemplateModel : ""}`;
}

export function generateArmResourceExamples(resource: TspArmResource): Record<string, string> {
const formalOperationGroupName = pluralize(resource.name);
const examples: Record<string, string> = {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,14 @@ export function generateOperationGroup(operationGroup: TypespecOperationGroup) {

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

for (const operation of operations) {
if ((operation as TspArmProviderActionOperation).kind === "ArmProviderActionAsync") {
if ((operation as TspArmProviderActionOperation).kind !== undefined) {
statements.push(generateProviderAction(operation as TspArmProviderActionOperation));
} else {
statements.push(generateOperation(operation as TypespecOperation, operationGroup));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { TypespecParameter } from "../interfaces";
import { getOptions } from "../options";
import { generateDecorators } from "../utils/decorators";
import { generateDocs } from "../utils/docs";
import { generateSuppressionForDocumentRequired } from "../utils/suppressions";
import { generateSuppressionForDocumentRequired, generateSuppressions } from "../utils/suppressions";

const _ARM_PARAM_REPLACEMENTS: { [key: string]: string } = {
subscriptionId: "...SubscriptionIdParameter",
Expand All @@ -11,15 +11,15 @@ const _ARM_PARAM_REPLACEMENTS: { [key: string]: string } = {
};

export function generateParameter(parameter: TypespecParameter): string {
const { isArm, isFullCompatible } = getOptions();
const { isArm } = getOptions();
if (isArm && _ARM_PARAM_REPLACEMENTS[parameter.name] !== undefined) {
return _ARM_PARAM_REPLACEMENTS[parameter.name];
}
const definitions: string[] = [];
const doc = generateDocs(parameter);
if (doc === "" && isFullCompatible) definitions.push(generateSuppressionForDocumentRequired());
definitions.push(doc);

parameter.suppressions && definitions.push(...generateSuppressions(parameter.suppressions));
const decorators = generateDecorators(parameter.decorators);
decorators && definitions.push(decorators);
let defaultValue = "";
Expand Down
Loading

0 comments on commit 5a09cb7

Please sign in to comment.