Skip to content

Commit

Permalink
foo
Browse files Browse the repository at this point in the history
  • Loading branch information
mane-dev committed Sep 29, 2023
1 parent 9056c0d commit c1893ec
Show file tree
Hide file tree
Showing 22 changed files with 699 additions and 1,493 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,8 @@
},
"[typespec]": {
"editor.defaultFormatter": "Microsoft.typespec-vscode"
},
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import { CadlProgram } from "../interfaces";
import { ArmResourcesCache } from "../transforms/transform-resources";
import { formatCadlFile } from "../utils/format";
import { getResourcesImports } from "../utils/imports";
import { getNamespace } from "../utils/namespace";

export function emitArmResources(program: CadlProgram, basePath: string) {
// Create a file per resource
const session = getSession();
for (const [_schema, armResource] of ArmResourcesCache.entries()) {
const { modules, namespaces } = getResourcesImports(program);
const { modules, namespaces } = getResourcesImports(program, armResource);
const filePath = join(basePath, `${armResource.name}.tsp`);
const generatedResource = generateObject(armResource);
const armOperations = generateArmOperations(armResource);
Expand All @@ -20,6 +21,7 @@ export function emitArmResources(program: CadlProgram, basePath: string) {
"\n",
namespaces.join("\n"),
"\n",
getNamespace(program),
generatedResource,
"\n",
armOperations.join("\n"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { Operation, Schema, SchemaResponse, isObjectSchema } from "@autorest/codemodel";
import { lowerFirst } from "lodash";
import {
ArraySchema,
ObjectSchema,
Operation,
Response,
Schema,
SchemaResponse,
isObjectSchema,
} from "@autorest/codemodel";
import { lowerFirst, toLower } from "lodash";
import { plural } from "pluralize";
import { getSession } from "../autorest-session";
import { CadlObject, CadlObjectProperty, CadlOperation, TypespecArmResource } from "../interfaces";
Expand All @@ -8,6 +16,7 @@ import { ArmResourcesCache, getArmResourceNames } from "../transforms/transform-
import { generateDecorators } from "../utils/decorators";
import { generateDocs } from "../utils/docs";
import { isArraySchema, isResponseSchema } from "../utils/schemas";
import { generateParameters } from "./generate-operations";

export function generateArmOperations(resource: TypespecArmResource) {
const definitions: string[] = [];
Expand All @@ -16,17 +25,17 @@ export function generateArmOperations(resource: TypespecArmResource) {
const resourceOperationsKind = getResourceOperationsKind(resource);
definitions.push("@armResourceOperations");
definitions.push(
`interface ${plural(resource.name)} extends Azure.ResourceManager.${resourceOperationsKind}<${resource.name}, ${
resource.propertiesModelName
}>{`,
`interface ${plural(resource.name)} extends Azure.ResourceManager.${
resourceOperationsKind.name
}<${resourceOperationsKind.parameters.join()}>{`,
);

for (const operation of resource.operations) {
const typespecOperations = transformOperation(operation, codeModel).flatMap((p) => p);
for (const op of typespecOperations) {
definitions.push("@autoroute");
definitions.push("@autoRoute");
definitions.push(generateDocs(op));
definitions.push(`@armResourceLocation(${resource.name})`);
definitions.push(`@armResourceAction(${resource.name})`);
definitions.push(`@${op.verb}`);
definitions.push(
`${lowerFirst(op.name)}(${getOperationParameters(resource, op)}): ArmResponse<${getResponseType(
Expand All @@ -41,26 +50,58 @@ export function generateArmOperations(resource: TypespecArmResource) {
return definitions;
}

function getOperationParameters(resource: TypespecArmResource, operation: CadlOperation) {
const params = [`...ResourceInstanceParameters<${resource.name}>`];
const defaultResourceParameters = ["resourcegroupname", "subscriptionid", "apiversion"];

if (operation.extensions.includes("Pageable")) {
params.push(`...ListQueryParameters`);
function getParentKeys(resource: TypespecArmResource): string[] {
if (!resource.resourceParent) {
return [];
}

return [resource.resourceParent.metadata.name, ...getParentKeys(resource.resourceParent)];
}

function getOperationParameters(resource: TypespecArmResource, operation: CadlOperation) {
const params = [`...ResourceInstanceParameters<${resource.name}>`];
const parentsKeys = getParentKeys(resource);
params.push(
generateParameters(
operation.parameters
.filter((p) => !parentsKeys.includes(p.name))
.filter(
(p) =>
p.name !== resource.metadata.name &&
p.implementation === "Method" &&
p.name.toLowerCase() !== `${resource.name}Name`.toLowerCase() &&
!defaultResourceParameters.includes(p.name.toLowerCase()),
),
),
);

return params.join(", ");
}

function getResponseType(operation: CadlOperation, rawOperation: Operation) {
const responseTypes = operation.responses.join(" |");
if (operation.extensions.includes("Pageable")) {
const armResourceNames = getArmResourceNames();

if (!isResultResourceList(rawOperation)) {
return `Page<${responseTypes}>`;
}

return `ResourceListResult<${operation.responses[0]}>`;
const responses = rawOperation.responses ?? [];
if (responses.length && isResponseSchema(responses[0])) {
const schema = responses[0].schema as ObjectSchema;
const valuesProperty = schema.properties?.find((p) => p.serializedName === "value");
if (!valuesProperty) {
throw new Error(`Unable to determine response type for operation ${operation.name}`);
}
if (!isArraySchema(valuesProperty.schema)) {
throw new Error(`Unable to determine response type for operation ${operation.name}`);
}
const elementType = valuesProperty.schema.elementType.language.default.name;
return `ResourceListResult<${elementType}>`;
}

throw new Error(`Unable to determine response type for operation ${operation.name}`);
}

return responseTypes;
Expand Down Expand Up @@ -101,12 +142,19 @@ function isResultResourceList(operation: Operation) {
return resources.has(resultName);
}

function getResourceOperationsKind(resource: TypespecArmResource) {
interface ResourceOperationsKind {
name: "ProxyResourceOperations" | "TrackedResourceOperations";
parameters: string[];
}

function getResourceOperationsKind(resource: TypespecArmResource): ResourceOperationsKind {
switch (resource.resourceKind) {
case "ProxyResource":
return "ProxyResourceOperations";
case "ExtensionResource":
case "SingletonResource":
return { name: "ProxyResourceOperations", parameters: [resource.name] };
case "TrackedResource":
return "TrackedResourceOperations";
return { name: "TrackedResourceOperations", parameters: [resource.name, resource.propertiesModelName] };
default:
throw new Error(`Generating operations for ${resource.resourceKind} is not yet supported`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function generateMultiResponseWarning(responses: string[], statements: string[])
);
}

function generateParameters(parameters: CadlParameter[]) {
export function generateParameters(parameters: CadlParameter[]) {
const params: string[] = [];
for (const parameter of parameters) {
const location = parameter.location;
Expand Down
54 changes: 47 additions & 7 deletions packages/extensions/openapi-to-cadl/src/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ObjectSchema, Operation } from "@autorest/codemodel";
import { ImplementationLocation, ObjectSchema, Operation } from "@autorest/codemodel";

export interface CadlProgram {
models: Models;
Expand Down Expand Up @@ -101,6 +101,7 @@ export interface CadlParameter extends CadlDataType {
isOptional: boolean;
type: string;
location: CadlParameterLocation;
implementation?: "Method" | "Client" | "Context";
}

export interface CadlObjectProperty extends CadlDataType {
Expand All @@ -110,10 +111,18 @@ export interface CadlObjectProperty extends CadlDataType {
decorators?: CadlDecorator[];
visibility?: "read";
}
export interface DecoratorArgumentOptions {
unwrap?: boolean;
}

export interface DecoratorArgument {
value: string;
options?: DecoratorArgumentOptions;
}

export interface CadlDecorator extends WithFixMe {
name: string;
arguments?: string[];
arguments?: string[] | DecoratorArgument[];
module?: string;
namespace?: string;
}
Expand All @@ -134,17 +143,21 @@ export interface CadlObject extends CadlDataType {
alias?: CadlAlias;
}

export type ArmResourceKind = "TrackedResource" | "ProxyResource";
export type ArmResourceKind = "TrackedResource" | "ProxyResource" | "ExtensionResource" | "SingletonResource";

export type ArmResourceHierarchy = {
export interface ArmResourceMetadata {
name: string;
parent?: ArmResourceHierarchy;
};
doc: string;
location: any;
pattern: string;
segmentName: string;
}

export interface TypespecArmResource extends CadlObject {
resourceKind: ArmResourceKind;
propertiesModelName: string;
path: string;
resourceParent?: TypespecArmResource;
metadata: ArmResourceMetadata;
operations: Operation[];
schema: ObjectSchema;
}
Expand All @@ -153,3 +166,30 @@ export interface Models {
enums: CadlEnum[];
objects: CadlObject[];
}

export interface InternalArmResources {
Resources: InternalArmResource[];
}

export interface InternalArmResource {
Name: string;
Operations: InternalArmResourceOperation[];
Parents: string[];
ModelName: string;
IsTrackedResource: boolean;
IsResource: boolean;
IsExtensionResource: boolean;
IsSingletonResource: boolean;
}

export interface InternalArmResourceOperation {
Path: string;
Method: HttpMethod;
OperationID: string;
}

export interface ArmResourceObjectSchema extends ObjectSchema {
resourceInformation?: InternalArmResource;
}

type HttpMethod = "GET" | "POST" | "DELETE" | "PATCH" | "PUT";
18 changes: 14 additions & 4 deletions packages/extensions/openapi-to-cadl/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { join } from "path";
import { join, dirname } from "path";
import { CodeModel, codeModelSchema } from "@autorest/codemodel";
import { AutoRestExtension, AutorestExtensionHost, Session, startSession } from "@autorest/extension-base";
import { InternalArmResource, InternalArmResources } from "interfaces";
import { setSession } from "./autorest-session";
import { emitArmResources } from "./emiters/emit-arm-resources";
import { emitCadlConfig } from "./emiters/emit-cadl-config";
Expand All @@ -13,25 +14,34 @@ import { emitModels } from "./emiters/emit-models";
import { emitPackage } from "./emiters/emit-package";
import { emitRoutes } from "./emiters/emit-routes";
import { getModel } from "./model";
import { preTransformArmResources } from "./pretransforms/arm-resource-pretrasnform";
import { pretransformNames } from "./pretransforms/name-pretransform";
import { ArmResourcesCache, calculateArmResources } from "./transforms/transform-resources";
import { calculateArmResources } from "./transforms/transform-resources";
import { markErrorModels } from "./utils/errors";
import { markPagination } from "./utils/paging";
import { markResources } from "./utils/resources";
export interface ArmCodeModel extends CodeModel {
armResources?: InternalArmResource[];
}
export async function processRequest(host: AutorestExtensionHost) {
const session = await startSession<CodeModel>(host, codeModelSchema);
const session = await startSession<ArmCodeModel>(host, codeModelSchema);
setSession(session);
const codeModel = session.model;

preTransformArmResources(codeModel);
calculateArmResources(codeModel);
pretransformNames(codeModel);

markPagination(codeModel);
markErrorModels(codeModel);
markResources(codeModel);
const cadlProgramDetails = getModel(codeModel);
emitArmResources(cadlProgramDetails, getOutuptDirectory(session));
await emitModels(getFilePath(session, "models.tsp"), cadlProgramDetails);

await emitRoutes(getFilePath(session, "routes.tsp"), cadlProgramDetails);
if (!codeModel.armResources?.length) {
await emitRoutes(getFilePath(session, "routes.tsp"), cadlProgramDetails);
}
await emitMain(getFilePath(session, "main.tsp"), cadlProgramDetails);
await emitPackage(getFilePath(session, "package.json"), cadlProgramDetails);
await emitCadlConfig(getFilePath(session, "tspconfig.yaml"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { dirname, join } from "path";
import { ArmCodeModel } from "main";
import { getSession } from "../autorest-session";
import { ArmResourceObjectSchema, InternalArmResources } from "../interfaces";

export function preTransformArmResources(codeModel: ArmCodeModel) {
const session = getSession();
const configPath: string = session.configuration.configFileFolderUri;
const configFiles: string[] = session.configuration.configurationFiles;

const localConfigFolder = dirname(configFiles.find((c) => c.startsWith(configPath)) ?? "").replace("file://", "");

const { Resources: resources }: InternalArmResources = require(join(localConfigFolder, "resources.json"));
codeModel.armResources = resources;
const objectSchemas: ArmResourceObjectSchema[] = codeModel.schemas.objects ?? [];
for (const schema of objectSchemas) {
const resourceInfo = resources.find((r) => r.ModelName === schema.language.default.name);
schema.resourceInformation = resourceInfo;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export function transformParameter(parameter: Parameter, codeModel: CodeModel):
return {
kind: "parameter",
doc,
implementation: parameter.implementation,
name,
isOptional: parameter.required !== true,
type: visited.name,
Expand Down
Loading

0 comments on commit c1893ec

Please sign in to comment.