@azure-tools/typespec-azure-resource-manager
Version:
TypeSpec Azure Resource Manager library
287 lines • 12.1 kB
JavaScript
import { $doc, getFriendlyName, ignoreDiagnostics, } from "@typespec/compiler";
import { useStateMap } from "@typespec/compiler/utils";
import { $route, getHttpOperation } from "@typespec/http";
import { $actionSegment, $autoRoute, $createsOrReplacesResource, $deletesResource, $readsResource, $updatesResource, getActionSegment, getParentResource, getSegment, } from "@typespec/rest";
import { reportDiagnostic } from "./lib.js";
import { isArmLibraryNamespace } from "./namespace.js";
import { getArmResourceInfo, getResourceBaseType, isArmVirtualResource, isCustomAzureResource, ResourceBaseType, } from "./resource.js";
import { ArmStateKeys } from "./state.js";
function getArmResourceOperations(program, resourceType) {
let operations = program.stateMap(ArmStateKeys.armResourceOperations).get(resourceType);
if (!operations) {
operations = { lifecycle: {}, lists: {}, actions: {} };
program.stateMap(ArmStateKeys.armResourceOperations).set(resourceType, operations);
}
return operations;
}
function resolveHttpOperations(program, data) {
const result = {};
for (const [key, item] of Object.entries(data)) {
const httpOperation = ignoreDiagnostics(getHttpOperation(program, item.operation));
result[key] = {
...item,
path: httpOperation.path,
httpOperation: httpOperation,
};
}
return result;
}
export function resolveResourceOperations(program, resourceType) {
const operations = getArmResourceOperations(program, resourceType);
// Returned the updated operations object
return {
lifecycle: resolveHttpOperations(program, operations.lifecycle),
actions: resolveHttpOperations(program, operations.actions),
lists: resolveHttpOperations(program, operations.lists),
};
}
function setResourceLifecycleOperation(context, target, resourceType, kind) {
// Only register methods from non-templated interface types
if (target.interface === undefined ||
target.interface.node === undefined ||
target.interface.node.templateParameters.length > 0) {
return;
}
// We can't resolve the operation path yet so treat the operation as a partial
// type so that we can fill in the missing details later
const operations = getArmResourceOperations(context.program, resourceType);
const operation = {
name: target.name,
kind,
operation: target,
operationGroup: target.interface.name,
};
operations.lifecycle[kind] = operation;
setArmOperationIdentifier(context.program, target, resourceType, {
name: target.name,
kind: kind,
operation: target,
operationGroup: target.interface.name,
});
}
export const [getArmOperationList, setArmOperationList] = useStateMap(ArmStateKeys.resourceOperationList);
export function getArmResourceOperationList(program, resourceType) {
let operations = getArmOperationList(program, resourceType);
if (operations === undefined) {
operations = new Set();
setArmOperationList(program, resourceType, operations);
}
return operations;
}
export function addArmResourceOperation(program, resourceType, operationData) {
const operations = getArmResourceOperationList(program, resourceType);
operations.add(operationData);
setArmOperationList(program, resourceType, operations);
}
export const [getArmResourceOperationData, setArmResourceOperationData] = useStateMap(ArmStateKeys.armResourceOperationData);
export function setArmOperationIdentifier(program, target, resourceType, data) {
const operationId = {
name: data.name,
kind: data.kind,
operation: target,
operationGroup: data.operationGroup,
resource: resourceType,
};
// Initialize the operations for the resource type if not already done
if (!getArmResourceOperationData(program, target)) {
setArmResourceOperationData(program, target, operationId);
}
addArmResourceOperation(program, resourceType, operationId);
}
export const $armResourceRead = (context, target, resourceType) => {
context.call($readsResource, target, resourceType);
setResourceLifecycleOperation(context, target, resourceType, "read");
};
export const $armResourceCreateOrUpdate = (context, target, resourceType) => {
context.call($createsOrReplacesResource, target, resourceType);
setResourceLifecycleOperation(context, target, resourceType, "createOrUpdate");
};
export const $armResourceUpdate = (context, target, resourceType) => {
context.call($updatesResource, target, resourceType);
setResourceLifecycleOperation(context, target, resourceType, "update");
};
export const $armResourceDelete = (context, target, resourceType) => {
context.call($deletesResource, target, resourceType);
setResourceLifecycleOperation(context, target, resourceType, "delete");
};
export const $armResourceList = (context, target, resourceType) => {
// Only register methods from non-templated interface types
if (target.interface === undefined ||
target.interface.node === undefined ||
target.interface.node.templateParameters.length > 0) {
return;
}
// We can't resolve the operation path yet so treat the operation as a partial
// type so that we can fill in the missing details later
const operations = getArmResourceOperations(context.program, resourceType);
const operation = {
name: target.name,
kind: "list",
operation: target,
operationGroup: target.interface.name,
};
operations.lists[target.name] = operation;
addArmResourceOperation(context.program, resourceType, {
name: target.name,
kind: "list",
operation: target,
operationGroup: target.interface.name,
resource: resourceType,
});
setArmOperationIdentifier(context.program, target, resourceType, {
name: target.name,
kind: "list",
operation: target,
operationGroup: target.interface.name,
});
};
export function armRenameListByOperationInternal(context, entity, resourceType, parentTypeName, parentFriendlyTypeName, applyOperationRename) {
const { program } = context;
if (parentTypeName === undefined ||
parentTypeName === "" ||
parentFriendlyTypeName === undefined ||
parentFriendlyTypeName === "") {
[parentTypeName, parentFriendlyTypeName] = getArmParentName(context.program, resourceType);
}
const parentType = getParentResource(program, resourceType);
if (parentType &&
!isArmVirtualResource(program, parentType) &&
!isCustomAzureResource(program, parentType)) {
const parentResourceInfo = getArmResourceInfo(program, parentType);
if (!parentResourceInfo &&
resourceType.namespace !== undefined &&
isArmLibraryNamespace(program, resourceType.namespace))
return;
if (!parentResourceInfo) {
reportDiagnostic(program, {
code: "parent-type",
messageId: "notResourceType",
target: resourceType,
format: { type: resourceType.name, parent: parentType.name },
});
return;
}
// Make sure the first character of the name is upper-cased
parentTypeName = parentType.name[0].toUpperCase() + parentType.name.substring(1);
}
// Add a formatted doc string too
context.call($doc, entity, `List ${resourceType.name} resources by ${parentType ? parentTypeName : parentFriendlyTypeName}`, undefined);
if (applyOperationRename === undefined || applyOperationRename === true) {
// Set the operation name
entity.name =
parentTypeName === "Extension" || parentTypeName === undefined || parentTypeName.length < 1
? "list"
: `listBy${parentTypeName}`;
}
}
function getArmParentName(program, resource) {
const parent = getParentResource(program, resource);
if (parent && (isArmVirtualResource(program, parent) || isCustomAzureResource(program, parent))) {
const parentName = getFriendlyName(program, parent) ?? parent.name;
if (parentName === undefined || parentName.length < 2) {
return ["", ""];
}
return [
parentName,
parentName.length > 1 ? parentName.charAt(0).toLowerCase() + parentName.substring(1) : "",
];
}
switch (getResourceBaseType(program, resource)) {
case ResourceBaseType.Extension:
return ["Extension", "parent"];
case ResourceBaseType.Location:
return ["Location", "location"];
case ResourceBaseType.Subscription:
return ["Subscription", "subscription"];
case ResourceBaseType.Tenant:
return ["Tenant", "tenant"];
case ResourceBaseType.ResourceGroup:
default:
return ["ResourceGroup", "resource group"];
}
}
export const $armResourceAction = (context, target, resourceType) => {
const { program } = context;
// Only register methods from non-templated interface types
if (target.interface === undefined ||
target.interface.node === undefined ||
target.interface.node.templateParameters.length > 0) {
return;
}
// We can't resolve the operation path yet so treat the operation as a partial
// type so that we can fill in the missing details later
const operations = getArmResourceOperations(program, resourceType);
const operation = {
name: target.name,
kind: "action",
operation: target,
operationGroup: target.interface.name,
};
operations.actions[target.name] = operation;
addArmResourceOperation(program, resourceType, {
name: target.name,
kind: "action",
operation: target,
operationGroup: target.interface.name,
resource: resourceType,
});
setArmOperationIdentifier(context.program, target, resourceType, {
name: target.name,
kind: "action",
operation: target,
operationGroup: target.interface.name,
});
const segment = getSegment(program, target) ?? getActionSegment(program, target);
if (!segment) {
// Also apply the @actionSegment decorator to the operation
context.call($actionSegment, target, uncapitalize(target.name));
}
};
function uncapitalize(name) {
if (name === "") {
return name;
}
return name[0].toLowerCase() + name.substring(1);
}
export const $armResourceCollectionAction = (context, target) => {
context.program.stateMap(ArmStateKeys.armResourceCollectionAction).set(target, true);
};
export function isArmCollectionAction(program, target) {
return program.stateMap(ArmStateKeys.armResourceCollectionAction).get(target) === true;
}
export const $armOperationRoute = (context, target, options) => {
const route = options?.route;
if (!route && !options?.useStaticRoute) {
context.call($autoRoute, target);
return;
}
if (route && route.length > 0) {
context.call($route, target, route);
}
};
export function getRouteOptions(program, target) {
let options = undefined;
if (target.interface) {
options = options || program.stateMap(ArmStateKeys.armResourceRoute).get(target.interface);
if (options)
return options;
}
if (target.sourceOperation?.interface) {
options =
options ||
program.stateMap(ArmStateKeys.armResourceRoute).get(target.sourceOperation.interface);
}
if (target.sourceOperation?.interface?.sourceInterfaces[0]) {
options =
options ||
program
.stateMap(ArmStateKeys.armResourceRoute)
.get(target.sourceOperation.interface.sourceInterfaces[0]);
}
if (options)
return options;
return {
useStaticRoute: false,
};
}
//# sourceMappingURL=operations.js.map