@autorest/openapi-to-typespec
Version:
Autorest plugin to scaffold a Typespec definition from an OpenAPI document
234 lines • 13.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseMetadata = void 0;
const codemodel_1 = require("@autorest/codemodel");
const options_1 = require("../options");
const logger_1 = require("../utils/logger");
const strings_1 = require("../utils/strings");
const find_parent_1 = require("./find-parent");
const operation_set_1 = require("./operation-set");
const resource_equivalent_1 = require("./resource-equivalent");
const utils_1 = require("./utils");
const logger = () => (0, logger_1.getLogger)("parse-metadata");
function parseMetadata(codeModel, configuration) {
var _a, _b;
const { isFullCompatible } = (0, options_1.getOptions)();
const operationSets = {};
const operations = codeModel.operationGroups.flatMap((og) => og.operations);
for (const operation of operations) {
const path = getNormalizeHttpPath(operation);
if (path in operationSets) {
operationSets[path].Operations.push(operation);
}
else {
operationSets[path] = { RequestPath: path, Operations: [operation], SingletonRequestPath: undefined };
}
}
const operationSetsByResourceDataSchemaName = {};
for (const key in operationSets) {
const operationSet = operationSets[key];
let resourceSchemaName = (0, operation_set_1.getResourceDataSchema)(operationSet);
if (resourceSchemaName === undefined) {
const resourceDataConfiguration = configuration["request-path-to-resource-data"];
const configuredName = resourceDataConfiguration
? resourceDataConfiguration[operationSet.RequestPath]
: undefined;
if (configuredName && ((_a = codeModel.schemas.objects) === null || _a === void 0 ? void 0 : _a.find((o) => o.language.default.name === configuredName))) {
resourceSchemaName = configuredName;
(0, operation_set_1.setResourceDataSchema)(operationSet, resourceSchemaName);
}
}
if (resourceSchemaName !== undefined) {
(0, operation_set_1.populateSingletonRequestPath)(operationSet);
if (resourceSchemaName in operationSetsByResourceDataSchemaName) {
operationSetsByResourceDataSchemaName[resourceSchemaName].push(operationSet);
}
else {
operationSetsByResourceDataSchemaName[resourceSchemaName] = [operationSet];
}
}
}
for (const key in operationSets) {
const operationSet = operationSets[key];
if ((0, operation_set_1.getResourceDataSchema)(operationSet))
continue;
for (const operation of operationSet.Operations) {
// Check if this operation is a collection operation
if ((0, find_parent_1.setParentOfResourceCollectionOperation)(operation, key, Object.values(operationSetsByResourceDataSchemaName).flat()))
continue;
// Otherwise we find a request path that is the longest parent of this, and belongs to a resource
if ((0, find_parent_1.setParentOfOtherOperation)(operation, key, Object.values(operationSetsByResourceDataSchemaName).flat()))
continue;
(0, find_parent_1.setParentOfExtensionOperation)(operation, key, Object.values(operationSetsByResourceDataSchemaName).flat());
}
}
const resources = {};
for (const resourceSchemaName in operationSetsByResourceDataSchemaName) {
const operationSets = operationSetsByResourceDataSchemaName[resourceSchemaName];
for (let index = 0; index < operationSets.length; index++) {
if (index >= 1 && !isFullCompatible) {
logger().info(`Multi-path operations applied on the same resource. Some operations will be lost. \nResource schema name: ${resourceSchemaName}.\nPath:\n${operationSets
.map((o) => o.RequestPath)
.join("\n")}\nTurn on isFullCompatible to keep all operations, or adjust your TypeSpec.`);
resources[resourceSchemaName + "FixMe"] = [
{
Name: resourceSchemaName + "FixMe",
GetOperation: undefined,
ExistOperation: undefined,
CreateOperation: undefined,
UpdateOperation: undefined,
DeleteOperation: undefined,
ListOperations: [],
OperationsFromResourceGroupExtension: [],
OperationsFromSubscriptionExtension: [],
OperationsFromManagementGroupExtension: [],
OperationsFromTenantExtension: [],
OtherOperations: [],
Parents: [],
SwaggerModelName: "",
ResourceType: "",
ResourceKey: "",
ResourceKeySegment: "",
IsTrackedResource: false,
IsTenantResource: false,
IsSubscriptionResource: false,
IsManagementGroupResource: false,
IsExtensionResource: false,
IsSingletonResource: false,
},
];
break;
}
((_b = resources[resourceSchemaName]) !== null && _b !== void 0 ? _b : (resources[resourceSchemaName] = [])).push(buildResource(resourceSchemaName, operationSets[index], Object.values(operationSetsByResourceDataSchemaName).flat(), codeModel));
}
}
return {
Resources: resources,
RenameMapping: {},
OverrideOperationName: {},
};
}
exports.parseMetadata = parseMetadata;
// TO-DO: handle expanded resource
function buildResource(resourceSchemaName, set, operationSets, codeModel) {
var _a, _b;
const getOperation = buildLifeCycleOperation(set, codemodel_1.HttpMethod.Get, "Get");
if (getOperation === undefined) {
logger().error(`Resource ${resourceSchemaName} must have a GET operation.`);
}
const createOperation = buildLifeCycleOperation(set, codemodel_1.HttpMethod.Put, "CreateOrUpdate");
const updateOperation = (_a = buildLifeCycleOperation(set, codemodel_1.HttpMethod.Patch, "Update")) !== null && _a !== void 0 ? _a : buildLifeCycleOperation(set, codemodel_1.HttpMethod.Put, "Update");
const deleteOperation = buildLifeCycleOperation(set, codemodel_1.HttpMethod.Delete, "Delete");
const existOperation = buildLifeCycleOperation(set, codemodel_1.HttpMethod.Head, "CheckExistence");
const listOperation = buildListOperation(set);
const otherOperation = buildOtherOperation(set);
const resourceSchema = (_b = codeModel.schemas.objects) === null || _b === void 0 ? void 0 : _b.find((o) => o.language.default.name === resourceSchemaName);
if (!resourceSchema) {
logger().error(`Cannot find resource schema for name ${resourceSchemaName}`);
}
const parents = (0, find_parent_1.getParents)(set.RequestPath, operationSets);
const isTenantResource = parents.length > 0 && parents[0] === "TenantResource";
const isSubscriptionResource = parents.length > 0 && parents[0] === "SubscriptionResource";
const isManagementGroupResource = parents.length > 0 && parents[0] === "ManagementGroupResource";
const operationsFromResourceGroupExtension = [];
const operationsFromSubscriptionExtension = [];
const operationsFromManagementGroupExtension = [];
const operationsFromTenantExtension = [];
for (const extension of (0, find_parent_1.getExtensionOperation)(set)) {
const extensionOperation = buildResourceOperationFromOperation(extension[0], "_");
switch (extension[1]) {
case "ResourceGroup":
operationsFromResourceGroupExtension.push(extensionOperation);
break;
case "Subscription":
operationsFromSubscriptionExtension.push(extensionOperation);
break;
case "ManagementGroup":
operationsFromManagementGroupExtension.push(extensionOperation);
break;
case "Tenant":
operationsFromTenantExtension.push(extensionOperation);
break;
}
}
return {
Name: (0, strings_1.lastWordToSingular)(resourceSchemaName),
GetOperation: getOperation,
ExistOperation: existOperation,
CreateOperation: createOperation,
UpdateOperation: updateOperation,
DeleteOperation: deleteOperation,
ListOperations: listOperation !== null && listOperation !== void 0 ? listOperation : [],
OperationsFromResourceGroupExtension: operationsFromResourceGroupExtension,
OperationsFromSubscriptionExtension: operationsFromSubscriptionExtension,
OperationsFromManagementGroupExtension: operationsFromManagementGroupExtension,
OperationsFromTenantExtension: operationsFromTenantExtension,
OtherOperations: otherOperation,
Parents: parents,
SwaggerModelName: resourceSchemaName,
ResourceType: (0, utils_1.getResourceType)(set.RequestPath),
ResourceKey: (0, utils_1.getResourceKey)(set.RequestPath),
ResourceKeySegment: (0, utils_1.getResourceKeySegment)(set.RequestPath),
IsTrackedResource: (0, resource_equivalent_1.isTrackedResource)(resourceSchema),
IsTenantResource: isTenantResource,
IsSubscriptionResource: isSubscriptionResource,
IsManagementGroupResource: isManagementGroupResource,
IsExtensionResource: (0, utils_1.isScopedPath)(set.RequestPath),
IsSingletonResource: (0, utils_1.isSingleton)(set),
};
}
function buildResourceOperationFromOperation(operation, operationName) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
let pagingMetadata = null;
if (operationName === "GetAll" || (0, resource_equivalent_1.getPagingItemType)(operation) !== undefined) {
let itemName = "value";
let nextLinkName = "nextLink";
if ((_b = (_a = operation.extensions) === null || _a === void 0 ? void 0 : _a["x-ms-pageable"]) === null || _b === void 0 ? void 0 : _b.itemName) {
itemName = (_d = (_c = operation.extensions) === null || _c === void 0 ? void 0 : _c["x-ms-pageable"]) === null || _d === void 0 ? void 0 : _d.itemName;
}
if ((_f = (_e = operation.extensions) === null || _e === void 0 ? void 0 : _e["x-ms-pageable"]) === null || _f === void 0 ? void 0 : _f.nextLinkName) {
nextLinkName = (_h = (_g = operation.extensions) === null || _g === void 0 ? void 0 : _g["x-ms-pageable"]) === null || _h === void 0 ? void 0 : _h.nextLinkName;
}
pagingMetadata = {
Method: operation.language.default.name,
ItemName: itemName,
NextLinkName: nextLinkName,
};
}
const method = (_j = operation.requests[0].protocol.http) === null || _j === void 0 ? void 0 : _j.method;
return {
Name: operationName,
Path: (_k = operation.requests[0].protocol.http) === null || _k === void 0 ? void 0 : _k.path,
Method: method.toUpperCase(),
OperationID: (_l = operation.operationId) !== null && _l !== void 0 ? _l : "",
IsLongRunning: ((_m = operation.extensions) === null || _m === void 0 ? void 0 : _m["x-ms-long-running-operation"]) === true ||
method === codemodel_1.HttpMethod.Put ||
method === codemodel_1.HttpMethod.Delete,
PagingMetadata: pagingMetadata,
Description: operation.language.default.description,
};
}
function buildOtherOperation(set) {
const operations = (0, find_parent_1.getOtherOperations)(set);
return operations.map((o) => buildResourceOperationFromOperation(o, o.language.default.name));
}
function buildListOperation(set) {
const operation = (0, find_parent_1.getResourceCollectionOperations)(set);
return (operation === null || operation === void 0 ? void 0 : operation.length) ? operation.map((o) => buildResourceOperationFromOperation(o, "GetAll")) : undefined;
}
function buildLifeCycleOperation(set, method, operationName) {
const operation = (0, operation_set_1.findOperation)(set, method);
return operation ? buildResourceOperationFromOperation(operation, operationName) : undefined;
}
function getNormalizeHttpPath(operation) {
var _a, _b;
if (((_a = operation.requests) === null || _a === void 0 ? void 0 : _a.length) !== 1) {
throw `No request or more than 1 requests in operation ${operation.operationId}`;
}
const path = (_b = operation.requests[0].protocol.http) === null || _b === void 0 ? void 0 : _b.path;
const normalizedPath = (path === null || path === void 0 ? void 0 : path.length) === 1 ? path : path === null || path === void 0 ? void 0 : path.replace(/\/$/, "");
if (!normalizedPath)
throw `Invalid http path ${path} for operation ${operation.operationId}`;
return normalizedPath;
}
//# sourceMappingURL=parse-metadata.js.map