UNPKG

@autorest/openapi-to-typespec

Version:

Autorest plugin to scaffold a Typespec definition from an OpenAPI document

298 lines 11.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.markResources = void 0; const codemodel_1 = require("@autorest/codemodel"); const data_types_1 = require("../data-types"); const model_1 = require("../model"); const options_1 = require("../options"); const lro_1 = require("./lro"); const operations_1 = require("./operations"); const paging_1 = require("./paging"); const schemas_1 = require("./schemas"); const knownResourceSchema = new Map(); function markResources(codeModel) { for (const operationGroup of codeModel.operationGroups) { for (const operation of operationGroup.operations) { const resource = getResourceKind(codeModel, operation); if (resource) { operation.language.default.resource = resource; } } } } exports.markResources = markResources; /** * Figures out if the path represents an */ // function markActionResource(codeModel: CodeModel, operation: Operation): void { // const method = getHttpMethod(codeModel, operation); // const pathParts = getResourcePath(operation).split("/"); // const partsLastIndex = pathParts.length - 1; // if (method !== HttpMethod.Post) { // return; // } // const lastPart = pathParts[partsLastIndex]; // if (lastPart.startsWith(":")) { // operation.language.default.actionResource = { // resource: pathParts.slice(0, partsLastIndex).join("/"), // action: lastPart, // }; // } // } function isActionOperation(operation) { const path = getResourcePath(operation); const pathParts = path.split("/"); const lastPart = pathParts[pathParts.length - 1]; return lastPart.startsWith(":"); } function getResourceKind(codeModel, operation) { if (isActionOperation(operation)) { // Actions are not yet supported return undefined; } const operationMethod = (0, operations_1.getHttpMethod)(codeModel, operation); if ((0, lro_1.hasLROExtension)(operation)) { const resource = handleLROResource(codeModel, operation); if (resource) { return resource; } } if (operationMethod === codemodel_1.HttpMethod.Get) { const resource = handleGetOperation(codeModel, operation); if (resource) { return resource; } } if (operationMethod === codemodel_1.HttpMethod.Patch) { const resource = handleResource(codeModel, operation, "ResourceCreateOrUpdate"); if (resource) { return resource; } } if (operationMethod === codemodel_1.HttpMethod.Put) { const resource = handleResource(codeModel, operation, "ResourceCreateOrReplace"); if (resource) { return resource; } } if (operationMethod === codemodel_1.HttpMethod.Post) { const resource = handleResource(codeModel, operation, "ResourceCreateWithServiceProvidedName"); if (resource) { return resource; } } if (operationMethod === codemodel_1.HttpMethod.Delete) { const resource = handleResource(codeModel, operation, "ResourceDelete"); if (resource) { return resource; } } if (operation.language.default.actionResource) { return handleResource(codeModel, operation, "ResourceAction"); } return undefined; } function handleLROResource(codeModel, operation) { const operationMethod = (0, operations_1.getHttpMethod)(codeModel, operation); if (operationMethod === codemodel_1.HttpMethod.Patch) { return handleResource(codeModel, operation, "LongRunningResourceCreateOrUpdate"); } if (operationMethod === codemodel_1.HttpMethod.Put) { return handleResource(codeModel, operation, "LongRunningResourceCreateOrReplace"); } if (operationMethod === codemodel_1.HttpMethod.Post) { return handleResource(codeModel, operation, "LongRunningResourceCreateWithServiceProvidedName"); } if (operationMethod === codemodel_1.HttpMethod.Delete) { return handleResource(codeModel, operation, "LongRunningResourceDelete"); } return undefined; } function handleResource(codeModel, operation, kind) { var _a, _b, _c; const dataTypes = (0, data_types_1.getDataTypes)(codeModel); for (const response of (_a = operation.responses) !== null && _a !== void 0 ? _a : []) { let schema; if (!(0, schemas_1.isResponseSchema)(response)) { let resourcePath = getResourcePath(operation); schema = knownResourceSchema.get(resourcePath); if (kind === "ResourceAction" && ((_b = operation.language.default.actionResource) === null || _b === void 0 ? void 0 : _b.resource)) { resourcePath = operation.language.default.actionResource.resource; schema = knownResourceSchema.get(resourcePath); } if (!schema) { continue; } } else { schema = response.schema; } if (!markResource(operation, schema)) { return undefined; } const typespecResponse = (_c = dataTypes.get(schema)) !== null && _c !== void 0 ? _c : (0, model_1.transformDataType)(schema, codeModel); return { kind, response: typespecResponse, }; } return undefined; } function getResourcePath(operation) { var _a, _b; for (const requests of (_a = operation.requests) !== null && _a !== void 0 ? _a : []) { const path = (_b = requests.protocol.http) === null || _b === void 0 ? void 0 : _b.path; if (path) { return path; } } throw new Error(`Couldn't find a resource path for operation ${operation.language.default.name}`); } function getNonPageableListResource(operation) { if (!operation.responses) { throw new Error(`Operation ${operation.language.default.name} has no defined responses`); } for (const response of operation.responses) { let schema; if (!(0, schemas_1.isResponseSchema)(response)) { const resourcePath = getResourcePath(operation); schema = knownResourceSchema.get(resourcePath); if (!schema) { continue; } } else { schema = response.schema; } if (!(0, schemas_1.isArraySchema)(schema)) { return undefined; } } const firstResponse = operation.responses[0]; return (0, schemas_1.isResponseSchema)(firstResponse) && (0, schemas_1.isArraySchema)(firstResponse.schema) ? firstResponse.schema : undefined; } function handleGetOperation(codeModel, operation) { var _a; if ((0, paging_1.isPageableOperation)(operation)) { return getPageableResource(codeModel, operation); } const nonPageableListResource = getNonPageableListResource(operation); if (nonPageableListResource) { if (!markResource(operation, nonPageableListResource.elementType)) { return undefined; } const dataTypes = (0, data_types_1.getDataTypes)(codeModel); const typespecResponse = (_a = dataTypes.get(nonPageableListResource.elementType)) !== null && _a !== void 0 ? _a : (0, model_1.transformDataType)(nonPageableListResource.elementType, codeModel); return { kind: "NonPagedResourceList", response: typespecResponse, }; } return handleResource(codeModel, operation, "ResourceRead"); } function markResource(operation, elementType) { if (!(0, codemodel_1.isObjectSchema)(elementType)) { return false; } const hasKey = markWithKey(elementType); if (hasKey) { markModelWithResource(elementType, getResourcePath(operation)); return true; } return false; } function getPageableResource(codeModel, operation) { var _a, _b; const response = (0, paging_1.getPageableResponse)(operation); if ((0, codemodel_1.isObjectSchema)(response.schema)) { for (const property of (_a = response.schema.properties) !== null && _a !== void 0 ? _a : []) { if ((0, paging_1.isPageValue)(property) && (0, schemas_1.isArraySchema)(property.schema)) { const dataTypes = (0, data_types_1.getDataTypes)(codeModel); const elementType = property.schema.elementType; if ((0, codemodel_1.isObjectSchema)(elementType)) { if (!markResource(operation, elementType)) { return undefined; } } const typespecResponse = (_b = dataTypes.get(elementType)) !== null && _b !== void 0 ? _b : (0, model_1.transformDataType)(elementType, codeModel); return { kind: "ResourceList", response: typespecResponse, }; } } } throw new Error(`Couldn't determine the Pageable resource for the operation: ${operation.language.default.name}`); } function markModelWithResource(elementType, resource) { knownResourceSchema.set(resource, elementType); elementType.language.default.resource = resource; } function markWithKey(schema) { const { properties, parents } = schema; const options = (0, options_1.getOptions)(); const { guessResourceKey } = options; if (!guessResourceKey) { return false; } if (parents && parents.immediate.length) { if (hasParentWithKey(parents.immediate)) { return false; } const defaultKeyInParent = shouldTryDefaultKeyInParent(schema); if (markParents(parents.immediate, defaultKeyInParent)) { return false; } } return markKeyProperty(properties !== null && properties !== void 0 ? properties : [], true); } function hasParentWithKey(parents) { var _a; for (const parent of parents) { if (!(0, codemodel_1.isObjectSchema)(parent)) { continue; } const properties = (_a = parent.properties) !== null && _a !== void 0 ? _a : []; if (properties.some((p) => p.language.default.isResourceKey)) { return true; } } return false; } function markParents(parents, defaultToFirst = false) { var _a; for (const parent of parents) { if (!(0, codemodel_1.isObjectSchema)(parent)) { continue; } const properties = (_a = parent.properties) !== null && _a !== void 0 ? _a : []; if (markKeyProperty(properties, defaultToFirst)) { return true; } } return false; } function shouldTryDefaultKeyInParent(schema) { if (schema.properties && schema.properties.some((p) => p.required)) { return false; } return true; } function markKeyProperty(allProperties, defaultToFirst = false) { const properties = allProperties.filter((p) => p.required && !p.isDiscriminator); for (const property of properties) { const serializedName = property.serializedName.toLowerCase(); if (serializedName.endsWith("name") || serializedName.endsWith("key") || serializedName.endsWith("id")) { property.language.default.isResourceKey = true; return true; } } if (defaultToFirst) { const firstRequired = properties.find((p) => p.required); if (firstRequired) { firstRequired.language.default.isResourceKey = true; } } return false; } //# sourceMappingURL=resources.js.map