import { deploymentsResourceTypeLC , templateKeys } from "../../../../common" ; import { IProvideOpenedDocuments } from "../../../IProvideOpenedDocuments" ; import * as Json from "../../../language/json/JSON" ; import { assertNever } from "../../../util/assertNever" ; import { parseUri } from "../../../util/uri" ; import { IParameterDefinition } from "../../parameters/IParameterDefinition" ; import { IParameterDefinitionsSource } from "../../parameters/IParameterDefinitionsSource" ; import { IParameterValuesSource } from "../../parameters/IParameterValuesSource" ; import { ParameterDefinition } from "../../parameters/ParameterDefinition" ; import { ParameterValuesSourceFromJsonObject } from "../../parameters/ParameterValuesSourceFromJsonObject" ; import { SimpleParameterDefinitionsSource } from "../../parameters/SimpleParameterDefinitionsSource" ; import { DeploymentTemplateDoc } from "../DeploymentTemplateDoc" ; import { IJsonDocument } from "../IJsonDocument" ; import { IResource } from "../IResource" ; import { getParameterDefinitionsFromLinkedTemplate } from "../linkedTemplates/getParameterDefinitionsFromLinkedTemplate" ; import { ILinkedTemplateReference } from "../linkedTemplates/ILinkedTemplateReference" ; import { Resource } from "../Resource" ; import { UserFunctionNamespaceDefinition } from "../UserFunctionNamespaceDefinition" ; import { IVariableDefinition , TopLevelCopyBlockVariableDefinition , TopLevelVariableDefinition } from "../VariableDefinition" ; import { getDeploymentScopeReference } from "./getDeploymentScopeReference" ; import { IDeploymentSchemaReference } from "./IDeploymentSchemaReference" ; import { TemplateScope , TemplateScopeKind } from "./TemplateScope" ; export interface IChildDeploymentScope { /**
     * The resource which is a deployments resource and defines this child deployment
     */ owningDeploymentResource : IResource ; } export class EmptyScope extends TemplateScope { public scopeKind : TemplateScopeKind = TemplateScopeKind . Empty ; constructor ( ) { super ( undefined , new DeploymentTemplateDoc ( '' , parseUri ( 'https://emptydoc/scope' ) , 0 ) , undefined , undefined , "Empty Scope" ) ; } protected getParameterDefinitionsSource ( ) { return new SimpleParameterDefinitionsSource ( [ ] ) ; } } export class UserFunctionScope extends TemplateScope { constructor ( parent , document , rootObject , private readonly userFunctionParameterDefinitions , public readonly __debugDisplay ) { super ( parent , document , rootObject , undefined , __debugDisplay ) ; } public readonly scopeKind : TemplateScopeKind = TemplateScopeKind . UserFunction ; protected getVariableDefinitions ( ) { return undefined ; } protected getNamespaceDefinitions ( ) { return undefined ; } protected getParameterDefinitionsSource ( ) { return new SimpleParameterDefinitionsSource ( this . userFunctionParameterDefinitions ) ; } } abstract class TemplateScopeFromObject extends TemplateScope { private _parameterDefinitionsSource : IParameterDefinitionsSource ; public constructor ( parent , document , private _templateRootObject , __debugDisplay ) { super ( parent , document , _templateRootObject , getDeploymentScopeReferenceFromRootObject ( _templateRootObject ) , __debugDisplay ) ; this . _parameterDefinitionsSource = new SimpleParameterDefinitionsSource ( getParameterDefinitionsFromObject ( this . document , this . _templateRootObject ) ) ; } protected getParameterDefinitionsSource ( ) { return this . _parameterDefinitionsSource ; } protected getVariableDefinitions ( ) { return getVariableDefinitionsFromObject ( this . _templateRootObject ) ; } protected getNamespaceDefinitions ( ) { return getNamespaceDefinitionsFromObject ( this , this . document , this . _templateRootObject ) ; } protected getResources ( ) { return getResourcesFromObject ( this , this . _templateRootObject ) ; } } export class TopLevelTemplateScope extends TemplateScopeFromObject { public constructor ( document , templateTopLevelValue , __debugDisplay ) { super ( undefined , document , templateTopLevelValue , __debugDisplay ) ; } public readonly scopeKind : TemplateScopeKind = TemplateScopeKind . TopLevel ; } function getParameterDefinitionsFromObject ( document , objectValue ) { const parameterDefinitions = [ ] ; if ( objectValue ) { const parameters = Json . asObjectValue ( objectValue . getPropertyValue ( templateKeys . parameters ) ) ; if ( parameters ) { for ( const parameter of parameters . properties ) { parameterDefinitions . push ( new ParameterDefinition ( document , parameter ) ) ; } } } return parameterDefinitions ; } function getVariableDefinitionsFromObject ( objectValue ) { if ( objectValue ) { const variables = Json . asObjectValue ( objectValue . getPropertyValue ( templateKeys . variables ) ) ; if ( variables ) { const varDefs = [ ] ; for ( let prop of variables . properties ) { if ( prop . nameValue . unquotedValue . toLowerCase ( ) === templateKeys . copyLoop ) { const varsArray = Json . asArrayValue ( prop . value ) ; for ( let varElement of varsArray ?. elements ?? [ ] ) { const def = TopLevelCopyBlockVariableDefinition . createIfValid ( varElement ) ; if ( def ) { varDefs . push ( def ) ; } } } else { varDefs . push ( new TopLevelVariableDefinition ( prop ) ) ; } } return varDefs ; } } return [ ] ; } function getNamespaceDefinitionsFromObject ( parentScope , document , objectValue ) { const namespaceDefinitions = [ ] ; if ( objectValue ) { const functionNamespacesArray = Json . asArrayValue ( objectValue . getPropertyValue ( templateKeys . functions ) ) ; if ( functionNamespacesArray ) { for ( let namespaceElement of functionNamespacesArray . elements ) { const namespaceObject = Json . asObjectValue ( namespaceElement ) ; if ( namespaceObject ) { let namespace = UserFunctionNamespaceDefinition . createIfValid ( parentScope , document , namespaceObject ) ; if ( namespace ) { namespaceDefinitions . push ( namespace ) ; } } } } } return namespaceDefinitions ; } function getResourceObjects ( objectValue ) { if ( objectValue ) { return objectValue ?. getPropertyValue ( templateKeys . resources ) ?. asArrayValue ; } return undefined ; } export function getResourcesFromObject ( owningScope , objectValue ) { const resources = [ ] ; if ( objectValue ) { const resourceObjects = getResourceObjects ( objectValue ) ; for ( let resourceValue of resourceObjects ?. elements ?? [ ] ) { const resourceObject = resourceValue . asObjectValue ; if ( resourceObject ) { resources . push ( new Resource ( owningScope , resourceObject ) ) ; } } } return resources ; } export enum ExpressionScopeKind { inner = "inner" , outer = "outer" } /**
 * A nested template with propertes.expressionEvaluationOptions.scope set to "inner".
 *
 * Evaluation of expressions, resource() etc. will be in the context of a scope unique to
 * this nested template.
 *
 * See https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/linked-templates#expression-evaluation-scope-in-nested-templates
 */ export class NestedTemplateInnerScope extends TemplateScopeFromObject { private _parameterValuesSource : ParameterValuesSourceFromJsonObject ; public constructor ( parent , document , public nestedTemplateObject , public owningDeploymentResource , private parameterValuesProperty , __debugDisplay ) { super ( parent , document , nestedTemplateObject , __debugDisplay ) ; this . _parameterValuesSource = new ParameterValuesSourceFromJsonObject ( this . document , this . parameterValuesProperty , nestedTemplateObject ) ; } protected getParameterValuesSource ( ) { return this . _parameterValuesSource ; } public readonly scopeKind : TemplateScopeKind = TemplateScopeKind . NestedDeploymentWithInnerScope ; protected getResources ( ) { return getResourcesFromObject ( this , this . nestedTemplateObject ) ; } } /**
 * A nested template with propertes.expressionEvaluationOptions.scope not present or set to "outer".
 *
 * Evaluation of expressions, resource() etc. will be in the context of the parent's scope.
 *
 * See https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/linked-templates#expression-evaluation-scope-in-nested-templates
 */ export class NestedTemplateOuterScope extends TemplateScope { public constructor ( private readonly parentScope , public readonly nestedTemplateObject , public owningDeploymentResource , public parameterValuesProperty , __debugDisplay ) { super ( parentScope , parentScope . document , nestedTemplateObject , getDeploymentScopeReferenceFromRootObject ( nestedTemplateObject ) , __debugDisplay ) ; } public readonly scopeKind : TemplateScopeKind = TemplateScopeKind . NestedDeploymentWithOuterScope ; public readonly hasUniqueParamsVarsAndFunctions : boolean = false ; protected getMemberOwningRootObject ( ) { return this . parentScope . memberOwningRootObject ; } protected getParameterDefinitionsSource ( ) { return this . parentScope . parameterDefinitionsSource ; } protected getVariableDefinitions ( ) { return this . parentScope . variableDefinitions ; } protected getNamespaceDefinitions ( ) { return this . parentScope . namespaceDefinitions ; } protected getResources ( ) { return getResourcesFromObject ( this , this . nestedTemplateObject ) ; } } export class LinkedTemplateScope extends TemplateScope implements IChildDeploymentScope { private _parameterValuesSource : ParameterValuesSourceFromJsonObject ; private _linkedFileReferences : ILinkedTemplateReference [ ] | undefined ; private _linkedFileParameterDefinitionsSource : SimpleParameterDefinitionsSource = new SimpleParameterDefinitionsSource ( ) ; public constructor ( parentScope , public templateLinkObject , private parameterValuesProperty , public owningDeploymentResource , __debugDisplay ) { super ( parentScope , parentScope . document , undefined , undefined , __debugDisplay ) ; this . _parameterValuesSource = new ParameterValuesSourceFromJsonObject ( this . document , this . parameterValuesProperty , templateLinkObject ) ; } /**
     * Linked files referenced by this scope (may change as new information is obtained)
     */ public get linkedFileReferences ( ) : ILinkedTemplateReference [ ] | undefined { return this . _linkedFileReferences ; } public get isRelativePath ( ) : boolean { return ! ! ( this . templateLinkObject ?. hasProperty ( templateKeys . linkedDeploymentTemplateLinkRelativePath ) && ! this . templateLinkObject ?. hasProperty ( templateKeys . linkedDeploymentTemplateLinkUri ) ) ; } public assignLinkedFileReferences ( linkedFileReferences , provideOpenDocuments ) { this . _linkedFileReferences = linkedFileReferences ; this . _linkedFileParameterDefinitionsSource . setParameterDefinitions ( [ ] ) ; if ( linkedFileReferences && linkedFileReferences . length > 0 ) { const firstLinkedFileReference = linkedFileReferences [ 0 ] ; const parameterDefinitions = getParameterDefinitionsFromLinkedTemplate ( firstLinkedFileReference , provideOpenDocuments ) ; this . _linkedFileParameterDefinitionsSource . setParameterDefinitions ( parameterDefinitions ) ; } } public readonly scopeKind : TemplateScopeKind = TemplateScopeKind . LinkedDeployment ; public readonly hasUniqueParamsVarsAndFunctions : boolean = true ; public readonly isExternal : boolean = true ; protected getVariableDefinitions ( ) { return undefined ; } protected getNamespaceDefinitions ( ) { return undefined ; } protected getResources ( ) { return undefined ; } protected getParameterValuesSource ( ) { return this . _parameterValuesSource ; } protected getParameterDefinitionsSource ( ) { return this . _linkedFileParameterDefinitionsSource ; } } export function isDeploymentResource ( resourceObject ) { const resourceTypeLC = resourceObject ?. asObjectValue ?. getPropertyValue ( templateKeys . resourceType ) ?. asStringValue ?. unquotedValue ; return resourceTypeLC ?. toLowerCase ( ) === deploymentsResourceTypeLC ; } export function getChildTemplateForResourceObject ( parentScope , resource , resourceObject ) { if ( resourceObject && isDeploymentResource ( resourceObject ) ) { const propertiesObject = resourceObject ?. getPropertyValue ( templateKeys . properties ) ?. asObjectValue ; const nestedTemplateObject = propertiesObject ?. getPropertyValue ( templateKeys . nestedDeploymentTemplateProperty ) ?. asObjectValue ; const templateName = resourceObject ?. getPropertyValue ( templateKeys . resourceName ) ?. asStringValue ?. unquotedValue ?? '(unnamed)' ; const parameterValuesProperty = resourceObject ?. getPropertyValue ( templateKeys . properties ) ?. asObjectValue ?. getProperty ( templateKeys . parameters ) ; if ( nestedTemplateObject ) { const scopeKind = getExpressionScopeKind ( resourceObject ) ; switch ( scopeKind ) { case ExpressionScopeKind . outer : return new NestedTemplateOuterScope ( parentScope , nestedTemplateObject , resource , parameterValuesProperty , `Nested template "${ templateName }" with outer scope` ) ; case ExpressionScopeKind . inner : return new NestedTemplateInnerScope ( parentScope , parentScope . document , nestedTemplateObject , resource , parameterValuesProperty , `Nested template "${ templateName }" with inner scope` ) ; default : assertNever ( scopeKind ) ; } } else { const templateLinkObject = propertiesObject ?. getPropertyValue ( templateKeys . linkedDeploymentTemplateLink ) ?. asObjectValue ; if ( templateLinkObject ) { return new LinkedTemplateScope ( parentScope , templateLinkObject , parameterValuesProperty , resource , `Linked template "${ templateName }"` ) ; } } return undefined ; } } function getExpressionScopeKind ( resourceObject ) { const scopeValue = resourceObject ?. getPropertyValue ( templateKeys . properties ) ?. asObjectValue ?. getPropertyValue ( templateKeys . nestedDeploymentExprEvalOptions ) ?. asObjectValue ?. getPropertyValue ( templateKeys . nestedDeploymentExprEvalScope ) ?. asStringValue ?. unquotedValue ; return scopeValue ?. toLowerCase ( ) === templateKeys . nestedDeploymentExprEvalScopeInner ? ExpressionScopeKind . inner : ExpressionScopeKind . outer ; } function getDeploymentScopeReferenceFromRootObject ( rootObject ) { const schemaStringValue = rootObject ?. getPropertyValue ( templateKeys . schema ) ?. asStringValue ; return getDeploymentScopeReference ( schemaStringValue ) ; }