@autorest/openapi-to-typespec
Version:
Autorest plugin to scaffold a Typespec definition from an OpenAPI document
298 lines • 11.6 kB
JavaScript
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
;