UNPKG

@autorest/openapi-to-typespec

Version:

Autorest plugin to scaffold a Typespec definition from an OpenAPI document

234 lines 13.2 kB
"use strict"; 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