UNPKG

@azure-tools/typespec-azure-resource-manager

Version:

TypeSpec Azure Resource Manager library

226 lines 10.1 kB
import { addService, getNamespaceFullName, } from "@typespec/compiler"; import { unsafe_Realm } from "@typespec/compiler/experimental"; import * as http from "@typespec/http"; import { getAuthentication, setAuthentication } from "@typespec/http"; import { unsafe_setRouteOptionsForNamespace as setRouteOptionsForNamespace } from "@typespec/http/experimental"; import { getResourceTypeForKeyParam } from "@typespec/rest"; import { $armCommonTypesVersion } from "./common-types.js"; import { reportDiagnostic } from "./lib.js"; import { getArmVirtualResourceDetails, getSingletonResourceKey } from "./resource.js"; import { ArmStateKeys } from "./state.js"; function getArmCommonTypesVersion(context, entity) { return entity.decorators.find((x) => x.definition?.name === "@armCommonTypesVersion")?.args[0] .jsValue; } function setArmCommonTypesVersionIfDoesnotExist(context, entity, commonTypeVersion) { // Determine whether to set a default ARM CommonTypes.Version const armCommonTypesVersion = entity.decorators.find((x) => x.definition?.name === "@armCommonTypesVersion"); // if no existing @armCommonTypesVersion decorator, add default. // This will NOT cause error if overrode on version enum. if (!armCommonTypesVersion) { context.call($armCommonTypesVersion, entity, commonTypeVersion); } } /** * Mark the target namespace as containign only ARM library types. This is used to create libraries to share among RPs * @param context The doecorator context, automatically supplied by the compiler * @param entity The decorated namespace */ export const $armLibraryNamespace = (context, entity) => { const { program } = context; program.stateMap(ArmStateKeys.armLibraryNamespaces).set(entity, true); setArmCommonTypesVersionIfDoesnotExist(context, entity, "v3"); }; /** * Check if the given namespace contains ARM library types * @param program The program to process * @param namespace The namespace to check * @returns true if the given namespace contains ARM library types only, false otherwise */ export function isArmLibraryNamespace(program, namespace) { return program.stateMap(ArmStateKeys.armLibraryNamespaces).get(namespace) === true; } export function isArmProviderNamespace(program, namespace) { return (namespace !== undefined && program.stateMap(ArmStateKeys.armProviderNamespaces).has(namespace)); } function isArmNamespaceOverride(program, entity) { return (program.stateMap(ArmStateKeys.armProviderNamespaces).size === 1 && program.stateMap(ArmStateKeys.armProviderNamespaces).has(entity)); } /** * Specify which ARM library namespaces this arm provider uses * @param {DecoratorContext} context Standard DecoratorContext object * @param {Namespace} entity The namespace the decorator is applied to * @param {Namespace[]} namespaces The library namespaces that will be used in this namespace */ export const $useLibraryNamespace = (context, entity, ...namespaces) => { const { program } = context; const provider = program.stateMap(ArmStateKeys.armProviderNamespaces).get(entity); if (provider) { setLibraryNamespaceProvider(program, provider, namespaces); } program.stateMap(ArmStateKeys.usesArmLibraryNamespaces).set(entity, namespaces); }; /** * Determine which library namespaces are used in this provider * @param {Program} program The program to check * @param {Namespace} namespace The provider namespace */ export function getUsedLibraryNamespaces(program, namespace) { return program.stateMap(ArmStateKeys.usesArmLibraryNamespaces).get(namespace); } function setLibraryNamespaceProvider(program, provider, namespaces) { for (const namespace of namespaces) { program.stateMap(ArmStateKeys.armProviderNamespaces).set(namespace, provider); } } /** * `@armProviderNamespace` sets the ARM provider namespace. * @param {DecoratorContext} context DecoratorContext object * @param {type} entity Target of the decorator. Must be `namespace` type * @param {string} armProviderNamespace Provider namespace */ export const $armProviderNamespace = (context, entity, armProviderNamespace) => { const { program } = context; const inRealm = unsafe_Realm.realmForType.has(entity); const override = isArmNamespaceOverride(program, entity); const namespaceCount = program.stateMap(ArmStateKeys.armProviderNamespaces).size; if (namespaceCount > 0 && !override && !inRealm) { reportDiagnostic(program, { code: "single-arm-provider", target: context.decoratorTarget, }); return; } // armProviderNamespace will set the service namespace if it's not done already if (!override || inRealm) { addService(program, entity); if (!http.getServers(program, entity)) { context.call(http.$server, entity, "https://management.azure.com", "Azure Resource Manager url."); } } const armCommonTypesVersion = getArmCommonTypesVersion(context, entity); // If it is versioned namespace, we will check each Version enum member. If no // @armCommonTypeVersion decorator, add the one const versioned = entity.decorators.find((x) => x.definition?.name === "@versioned"); if (versioned) { const versionEnum = versioned.args[0].value; versionEnum.members.forEach((v) => { if (!getArmCommonTypesVersion(context, v)) { context.call($armCommonTypesVersion, v, armCommonTypesVersion ?? "v3"); } }); } else { // if it is unversioned namespace, set @armCommonTypesVersion and // no existing @armCommonTypesVersion decorator, add default. // This will NOT cause error if overrode on version enum. if (!armCommonTypesVersion) { context.call($armCommonTypesVersion, entity, "v3"); } } // 'namespace' is optional, use the actual namespace string if omitted const typespecNamespace = getNamespaceFullName(entity); if (!armProviderNamespace) { armProviderNamespace = typespecNamespace; } program.stateMap(ArmStateKeys.armProviderNamespaces).set(entity, armProviderNamespace); const libraryNamespace = getUsedLibraryNamespaces(program, entity); if (libraryNamespace) { setLibraryNamespaceProvider(program, armProviderNamespace, libraryNamespace); } // Set default security definitions if (!override) { if (getAuthentication(program, entity) === undefined) { setAuthentication(program, entity, { options: [ { schemes: [ { id: "azure_auth", description: "Azure Active Directory OAuth2 Flow.", type: "oauth2", model: null, flows: [ { type: "implicit", authorizationUrl: "https://login.microsoftonline.com/common/oauth2/authorize", scopes: [ { value: "user_impersonation", description: "impersonate your user account" }, ], }, ], }, ], }, ], }); } // Set route options for the top level namespace let topLevelNamespace = entity; while (topLevelNamespace.namespace) { topLevelNamespace = topLevelNamespace.namespace; } setRouteOptionsForNamespace(program, topLevelNamespace, { autoRouteOptions: { // Filter key parameters for singleton resource types to insert the // singleton key value routeParamFilter: (operation, param) => { const paramResourceType = getResourceTypeForKeyParam(program, param); if (paramResourceType) { const singletonKey = getSingletonResourceKey(program, paramResourceType); if (singletonKey) { return { routeParamString: singletonKey, excludeFromOperationParams: true, }; } } // Returning undefined leaves the parameter unaffected return undefined; }, }, }); } }; /** * Get the ARM provider namespace for a given entity * @param {Program} program * @param {Namespace | Model} entity * @returns {string | undefined} ARM provider namespace */ export function getArmProviderNamespace(program, entity) { if (entity.kind === "Model") { const details = getArmVirtualResourceDetails(program, entity); if (details?.provider !== undefined) { return details.provider; } } const currentNamespace = entity.kind === "Namespace" ? entity : entity.namespace; return getArmProviderFromNamespace(program, currentNamespace); } function getArmProviderFromNamespace(program, ns) { let armProviderNamespace; while (ns) { armProviderNamespace = program.stateMap(ArmStateKeys.armProviderNamespaces).get(ns); if (armProviderNamespace) { return armProviderNamespace; } ns = ns.namespace; } return undefined; } export function resolveProviderNamespace(program, ns) { ns = ns ?? program.getGlobalNamespaceType(); if (program.stateMap(ArmStateKeys.armProviderNamespaces).get(ns)) { return ns; } for (const child of ns.namespaces.values()) { const providerNs = resolveProviderNamespace(program, child); if (providerNs) { return providerNs; } } return undefined; } //# sourceMappingURL=namespace.js.map