UNPKG

armpit

Version:

Another resource manager programming interface toolkit.

231 lines 9.97 kB
import { ResourceManagementClient, } from "@azure/arm-resources"; import { CallableClassBase, mergeAbortSignals } from "./tsUtils.js"; import { shallowMergeDefinedValues, shallowCloneDefinedValues } from "./optionsUtils.js"; import { ExistingGroupLocationConflictError, GroupNotEmptyError } from "./errors.js"; import { hasNameAndLocation, extractSubscriptionFromId, isSubscriptionId, locationNameOrCodeEquals, } from "./azureUtils.js"; import { handleGet } from "./azureSdkUtils.js"; import { NetworkTools } from "./networkTools.js"; import { ContainerAppTools } from "./containerAppTools.js"; import { ComputeTools } from "./computeTools.js"; import { AppServiceTools } from "./appServiceTools.js"; // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging export class ResourceGroupTools extends CallableClassBase { #dependencies; #invoker; #options; constructor(dependencies, options) { super(); this.#dependencies = dependencies; this.#invoker = dependencies.invoker; this.#options = shallowCloneDefinedValues(options); } async fnImpl(nameOrDescriptor, secondArg, thirdArg, fourthArg) { let groupName; let location; let subscriptionId = null; let abortSignal; if (nameOrDescriptor == null) { throw new Error("Name or descriptor is required"); } if (typeof nameOrDescriptor === "string") { // overload: name, location, subscriptionId?, abortSignal? abortSignal = fourthArg; groupName = nameOrDescriptor; if (secondArg == null) { location = this.#getRequiredDefaultLocation(); } else if (typeof secondArg === "string") { location = secondArg; } else { throw new Error("Location argument is not valid"); } if (thirdArg != null) { if (!isSubscriptionId(thirdArg)) { throw new Error("Given subscription ID is not valid"); } subscriptionId = thirdArg; } } else if (nameOrDescriptor.name != null) { // overload: {name, location, subscriptionId?}, abortSignal? abortSignal = secondArg; if (typeof nameOrDescriptor.name !== "string") { throw new Error("Group name is required"); } groupName = nameOrDescriptor.name; if (typeof nameOrDescriptor.location !== "string") { throw new Error("An explicit location is required"); } location = nameOrDescriptor.location; if (nameOrDescriptor.subscriptionId != null) { if (!isSubscriptionId(nameOrDescriptor.subscriptionId)) { throw new Error("Provided subscription ID is not valid"); } subscriptionId = nameOrDescriptor.subscriptionId; } } else { throw new Error("Unexpected arguments"); } if (location == null) { if (this.#options.location == null) { throw new Error("Location is required and no default location has been set"); } location = this.#options.location; } if (subscriptionId == null && this.#options.subscriptionId != null) { subscriptionId = this.#options.subscriptionId; } const baseOptions = { subscriptionId: subscriptionId ?? undefined, abortSignal }; let group = await this.get(groupName, baseOptions); if (group == null) { group = await this.create(groupName, location, baseOptions); } else if (!locationNameOrCodeEquals(location, group.location)) { throw new ExistingGroupLocationConflictError(group, location); } if (!hasNameAndLocation(group)) { throw new Error("Resource group is not correctly formed"); } const invoker = this.#invoker({ defaultLocation: group.location ?? location, defaultResourceGroup: group.name ?? groupName, }); if (subscriptionId == null && group.id != null) { subscriptionId = extractSubscriptionFromId(group.id); } const generalToolOptions = { groupName, location, subscriptionId, }; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type const mainFn = (...args) => invoker(...args); const cliResult = Object.assign(mainFn, { location, subscriptionId }); Object.defineProperty(cliResult, "name", { value: groupName, configurable: true, enumerable: true, writable: false, }); return Object.assign(cliResult, { appService: new AppServiceTools(this.#dependencies, generalToolOptions), containerApp: new ContainerAppTools(this.#dependencies, generalToolOptions), compute: new ComputeTools(this.#dependencies, generalToolOptions), network: new NetworkTools(this.#dependencies, generalToolOptions), getCredential: (options) => { if (subscriptionId) { options = { subscription: subscriptionId, ...options }; } return this.#dependencies.credentialFactory.getCredential(options); }, }); } async get(name, options) { const { subscriptionId, abortSignal } = this.#buildMergedOptions(options); if (subscriptionId != null) { const client = this.getClient(subscriptionId); return await handleGet(client.resourceGroups.get(name, { abortSignal })); } return await this.#getLaxInvokerFn(options) `group show --name ${name}`; } async exists(name, options) { const { subscriptionId, abortSignal } = this.#buildMergedOptions(options); if (subscriptionId != null) { const client = this.getClient(subscriptionId); const result = await client.resourceGroups.checkExistence(name, { abortSignal }); return !!result.body; } const args = ["--name", name]; if (subscriptionId != null) { args.push("--subscription", subscriptionId); } return !!(await this.#getLaxInvokerFn(options) `group exists ${args}`); } async create(name, location, options) { const { subscriptionId, abortSignal } = this.#buildMergedOptions(options); if (subscriptionId != null) { const client = this.getClient(subscriptionId); return await client.resourceGroups.createOrUpdate(name, { location }, { abortSignal }); } return await this.#getInvokerFn(options) `group create --name ${name} --location ${location}`; } async delete(name, options) { const group = await this.get(name, options); if (group == null) { return false; } if (typeof group.name !== "string") { throw new Error(`Loaded resource group for ${name} is not valid`); } else if (group.name !== name) { throw new Error(`Loaded resource group for ${name} has a conflicting name: ${group.name}`); } // TODO: Use SDK when possible const jmesQuery = "[].{id: id, name: name, type: type}"; // passes as an expression for correct escaping const resources = await this.#getInvokerFn(options) `resource list --resource-group ${name} --query ${jmesQuery}`; if (resources.length !== 0) { throw new GroupNotEmptyError(name, resources); } await this.#getLaxInvokerFn(options) `group delete --yes --name ${name}`; return true; } getClient(subscriptionId, options) { let clientSubscriptionId; if (subscriptionId != null) { if (!isSubscriptionId(subscriptionId)) { throw new Error("Given subscription ID is not valid."); } clientSubscriptionId = subscriptionId; } else { if (this.#options.subscriptionId == null) { throw new Error("No default subscription ID has been set, so an explicit subscription ID argument is required."); } clientSubscriptionId = this.#options.subscriptionId; } return this.#dependencies.managementClientFactory.get(ResourceManagementClient, clientSubscriptionId, options); } #getRequiredDefaultLocation() { const location = this.#options.location; if (location == null) { throw new Error("No required default location has been set"); } return location; } #buildMergedOptions(options) { if (options == null) { return this.#options; } const merged = shallowMergeDefinedValues(this.#options, options); const abortSignal = mergeAbortSignals(options.abortSignal, this.#options.abortSignal); if (abortSignal) { merged.abortSignal = abortSignal; } return merged; } #buildInvokerOptions(options) { const mergedOptions = this.#buildMergedOptions(options); const result = { forceAzCommandPrefix: true, }; if (mergedOptions.abortSignal != null) { result.abortSignal = mergedOptions.abortSignal; } if (mergedOptions.location != null) { result.defaultLocation = mergedOptions.location; } return result; } #getInvokerFn(options) { return this.#invoker(this.#buildInvokerOptions(options)); } #getLaxInvokerFn(options) { return this.#invoker({ ...this.#buildInvokerOptions(options), allowBlanks: true, }); } } //# sourceMappingURL=resourceGroupTools.js.map