file copyright author Vivek M. Chawla <@VivekMChawla>
 *  version license summary description import { JsonMap } from '@salesforce/ts-types' ; import { SfdxFalconDebug } from '../../../sfdx-falcon-debug' ; import { SfdxFalconRecipe } from '../../../sfdx-falcon-recipe' ; import { SfdxFalconRecipeJson } from '../../../sfdx-falcon-recipe' ; import { AppxRecipeEngine } from '../../../sfdx-falcon-recipe/engines/appx' ; import { AppxEngineStepGroup } from '../../../sfdx-falcon-recipe/engines/appx' ; import { AppxEngineAction } from '../appx/actions' ; import { ConfigureAdminUserAction } from '../appx/actions/configure-admin-user' ; import { CreateScratchOrgAction } from '../appx/actions/create-scratch-org' ; import { CreateUserAction } from '../appx/actions/create-user' ; import { DeleteScratchOrgAction } from '../appx/actions/delete-scratch-org' ; import { DeployMetadataAction } from '../appx/actions/deploy-metadata' ; import { ExecuteApexAction } from '../appx/actions/execute-apex' ; import { ImportDataTreeAction } from '../appx/actions/import-data-tree' ; import { InstallPackageAction } from '../appx/actions/install-package' ; import { ListrContext } from '../../../sfdx-falcon-types' ; import { SfdxCliLogLevel } from '../../../sfdx-falcon-types' ; import { YeomanChoice } from '../../../sfdx-falcon-types' ; import { YeomanCheckboxChoice } from '../../../sfdx-falcon-types' ; import { CompileOptions } from '../../types' ; import { ExecutionOptions } from '../../types' ; const inquirer = require ( 'inquirer' ) ; const chalk = require ( 'chalk' ) ; const dbgNs = 'ENGINE:appx:demo-config:' ; export interface AppxDemoConfigCompileOptions { targetOrgAlias : string ; haltOnError : boolean ; skipOrgRefresh : boolean ; skipGroups : string [ ] ; skipActions : string [ ] ; } class extends AppxRecipeEngine summary description public export class AppxDemoConfigEngine extends AppxRecipeEngine { method param { SfdxFalconRecipe } recipe param { CompileOptions } compileOptions returns { Promise < AppxDemoConfigEngine > } description public static async public static async compileRecipe ( recipe , compileOptions ) { const compiledRecipeEngine = new AppxDemoConfigEngine ( 'appx:demo-config' , recipe . recipeName ) ; await compiledRecipeEngine . compile ( recipe , compileOptions ) ; return compiledRecipeEngine ; } method param { SfdxFalconRecipeJson } sfdxFalconRecipeJson returns { void } description version public static public static validateRecipe ( sfdxFalconRecipeJson ) { AppxRecipeEngine . validateOuterRecipe ( sfdxFalconRecipeJson ) ; AppxDemoConfigEngine . validateInnerRecipe ( sfdxFalconRecipeJson ) ; return ; } method param { SfdxFalconRecipeJson } sfdxFalconRecipeJson returns { Promise < void > } description version private static private static validateInnerRecipe ( sfdxFalconRecipeJson ) { } method param { ExecutionOptions } executionOptions returns { Promise < ListrContext > } description protected async protected async executeEngine ( executionOptions = { } ) { console . log ( chalk `\n{yellow Recipe '${ this . recipe . recipeName }' installing '${ this . engineContext . targetOrg . orgName }' to ${ this . engineContext . targetOrg . alias }:}` ) ; const listrContext = await this . listrTasks . run ( ) ; return listrContext ; } method returns { Promise < void > } description protected async protected async initializeActionMap ( ) { this . actionExecutorMap = new Map < string , AppxEngineAction > ( ) ; this . actionExecutorMap . set ( 'create-scratch-org' , new CreateScratchOrgAction ( ) ) ; this . actionExecutorMap . set ( 'create-user' , new CreateUserAction ( ) ) ; this . actionExecutorMap . set ( 'configure-admin-user' , new ConfigureAdminUserAction ( ) ) ; this . actionExecutorMap . set ( 'delete-scratch-org' , new DeleteScratchOrgAction ( ) ) ; this . actionExecutorMap . set ( 'deploy-metadata' , new DeployMetadataAction ( ) ) ; this . actionExecutorMap . set ( 'execute-apex' , new ExecuteApexAction ( ) ) ; this . actionExecutorMap . set ( 'import-data-tree' , new ImportDataTreeAction ( ) ) ; this . actionExecutorMap . set ( 'install-package' , new InstallPackageAction ( ) ) ; } method returns { Promise < void > } description protected async protected async initializePostBuildStepGroups ( ) { SfdxFalconDebug . obj ( `${ dbgNs }initializePostBuildStepGroups:postBuildStepGroups:` , this . postBuildStepGroups , `postBuildStepGroups: ` ) ; } method returns { Promise < void > } description protected async protected async initializePreBuildStepGroups ( ) { if ( this . engineContext . targetOrg . isScratchOrg && this . engineContext . compileOptions . skipOrgRefresh !== true ) { const rebuildScratchOrgRSG = { stepGroupName : "Refresh Scratch Org" , alias : "appx:demo-recipe:rebuild-scratch-org" , description : "Refresh (delete then create) the target scratch org before installing the demo" , recipeSteps : [ { stepName : 'Delete Current Scratch Org' , description : 'Deletes the current scratch org' , action : 'delete-scratch-org' , options : { scratchOrgAlias : `${ this . engineContext . targetOrg . alias }` } } , { stepName : 'Create New Scratch Org' , description : 'Creates a new scratch org for this demo' , action : 'create-scratch-org' , options : { scratchOrgAlias : `${ this . engineContext . targetOrg . alias }` , scratchDefJson : `${ this . engineContext . targetOrg . scratchDefJson }` } } ] } ; this . preBuildStepGroups . push ( rebuildScratchOrgRSG ) ; } SfdxFalconDebug . obj ( `${ dbgNs }initializePreBuildStepGroups:preBuildStepGroups:` , this . preBuildStepGroups , `preBuildStepGroups: ` ) ; } method returns { Promise < void > } description protected async protected async initializeRecipeEngineContext ( ) { if ( typeof this . recipe === 'undefined' || this . recipe . validated !== true ) { throw new Error ( `ERROR_RECIPE_NOT_VALIDATED: The call to initializeRecipeEngineContext () ` + `was made before the incoming recipe was validated` ) ; } SfdxFalconDebug . obj ( `${ dbgNs }initializeRecipeEngineContext:` , this . engineContext . compileOptions , `this.engineContext.compileOptions: ` ) ; this . engineContext . projectContext = this . recipe . projectContext ; this . engineContext . devHubAlias = ( this . engineContext . compileOptions . devHubAlias || this . engineContext . projectContext . falconLocalConfig . devHubAlias ) as string ; this . engineContext . haltOnError = ( this . engineContext . compileOptions . haltOnError || this . recipe . options . haltOnError ) as boolean ; this . engineContext . logLevel = ( this . engineContext . compileOptions . logLevel || SfdxCliLogLevel . ERROR ) as SfdxCliLogLevel ; this . engineContext . executing = false ; SfdxFalconDebug . obj ( `${ dbgNs }initializeRecipeEngineContext:engineContext:` , this . engineContext , `engineContext: (at end of Engine Context Initialization) ` ) ; } method returns { Promise < void > } description protected async protected async initializeSkipActions ( ) { if ( Array . isArray ( this . engineContext . compileOptions . skipActions ) ) { this . engineContext . skipActions = this . engineContext . compileOptions . skipActions as string [ ] ; return ; } this . engineContext . skipActions = this . recipe . options . skipActions as string [ ] ; } method returns { Promise < void > } description protected async protected async initializeSkipGroups ( ) { if ( Array . isArray ( this . engineContext . compileOptions . skipGroups ) ) { this . engineContext . skipGroups = this . engineContext . compileOptions . skipGroups as string [ ] ; return ; } if ( this . recipe . options . noCustomInstall ) { this . engineContext . skipGroups = this . recipe . options . skipGroups as string [ ] ; return ; } this . engineContext . skipGroups = await this . askUserForSkipGroups ( ) ; return ; } method returns { Promise < void > } description protected async protected async initializeTargetOrg ( ) { let targetOrgAlias = this . engineContext . compileOptions . targetOrgAlias ; let targetConfirmationRequired = null ; if ( ! targetOrgAlias ) { if ( ( this . recipe . options . targetOrgs as JsonMap [ ] ) . length === 1 ) { targetOrgAlias = this . recipe . options . targetOrgs [ 0 ] . alias ; targetConfirmationRequired = true ; } else { targetOrgAlias = await this . askUserForTargetOrgAlias ( ) ; targetConfirmationRequired = false ; } } let selectedTargetOrg = null ; for ( const targetOrg of this . recipe . options . targetOrgs as JsonMap [ ] ) { if ( targetOrg . alias === targetOrgAlias ) { selectedTargetOrg = targetOrg ; } } if ( selectedTargetOrg === null ) { throw new Error ( `ERROR_COMPILE_RECIPE_FAILED: The specified Target Org Alias '${ targetOrgAlias }'  ` + `does not match any of the targetOrgs in your recipe` ) ; } SfdxFalconDebug . obj ( `${ dbgNs }initializeTargetOrg:selectedTargetOrg:` , selectedTargetOrg , `selectedTargetOrg: ` ) ; this . engineContext . targetOrg = selectedTargetOrg ; if ( targetConfirmationRequired ) { if ( await this . confirmDemoTarget ( ) === false ) { console . log ( '' ) ; throw new Error ( `INSTALLATION_CANCELLED: Installation cancelled at user's request` ) ; } } } method returns { Promise < Array < string > > } description private async private async askUserForSkipGroups ( ) { const iqPromptOne = [ { type : 'confirm' , name : 'proceed' , default : false , message : 'Set advanced options now?' , when : true } , { type : 'confirm' , name : 'reallyProceed' , default : false , message : answerHash => { return `${ chalk . red ( 'Warning:' ) } Only advanced users should set these options. Proceed anyway?` ; } , when : answerHash => { return ( answerHash . proceed ) ; } } ] ; console . log ( '' ) ; const userSelectionsOne = await inquirer . prompt ( iqPromptOne ) ; if ( userSelectionsOne . reallyProceed !== true ) { return this . recipe . options . skipGroups as string [ ] ; } const stepGroupChoices = new Array < YeomanCheckboxChoice > ( ) ; for ( const recipeStepGroup of this . preBuildStepGroups as AppxEngineStepGroup [ ] ) { stepGroupChoices . push ( { name : recipeStepGroup . stepGroupName , value : recipeStepGroup . alias , short : recipeStepGroup . stepGroupName , checked : ( ! ( this . recipe . options . skipGroups as string [ ] ) . includes ( recipeStepGroup . alias ) ) } ) ; } for ( const recipeStepGroup of this . recipe . recipeStepGroups as unknown as AppxEngineStepGroup [ ] ) { stepGroupChoices . push ( { name : recipeStepGroup . stepGroupName , value : recipeStepGroup . alias , short : recipeStepGroup . stepGroupName , checked : ( ! ( this . recipe . options . skipGroups as string [ ] ) . includes ( recipeStepGroup . alias ) ) } ) ; } for ( const recipeStepGroup of this . postBuildStepGroups as AppxEngineStepGroup [ ] ) { stepGroupChoices . push ( { name : recipeStepGroup . stepGroupName , value : recipeStepGroup . alias , short : recipeStepGroup . stepGroupName , checked : ( ! ( this . recipe . options . skipGroups as string [ ] ) . includes ( recipeStepGroup . alias ) ) } ) ; } const iqPromptTwo = [ { type : 'checkbox' , name : 'stepGroupChoices' , message : 'Which install steps should be executed?' , choices : stepGroupChoices , pageSize : 8 , filter : userInput => { for ( const stepGroupChoice of stepGroupChoices ) { if ( userInput . includes ( stepGroupChoice . value ) ) stepGroupChoice . checked = true ; else stepGroupChoice . checked = false ; } return userInput ; } } , { type : 'confirm' , name : 'proceed' , default : false , message : answerHash => { return `Proceed with installation?` ; } , when : true } , { type : 'confirm' , name : 'tryAgain' , message : answerHash => { return `Would you like to change your selections?` ; } , when : answerHash => { return ( ! answerHash . proceed ) ; } } ] ; let userSelectionsTwo ; do { console . log ( '' ) ; userSelectionsTwo = await inquirer . prompt ( iqPromptTwo ) ; } while ( userSelectionsTwo . proceed === false && userSelectionsTwo . tryAgain === true ) ; SfdxFalconDebug . obj ( `${ dbgNs }askUserForSkipGroups:userSelectionsTwo:` , userSelectionsTwo , `userSelectionsTwo: ` ) ; if ( userSelectionsTwo . proceed === false ) { console . log ( '' ) ; throw new Error ( `INSTALLATION_CANCELLED: Installation cancelled at user's request` ) ; } const finalSkipGroups = new Array < string > ( ) ; for ( const stepGroupChoice of stepGroupChoices ) { if ( stepGroupChoice . checked === false ) { finalSkipGroups . push ( stepGroupChoice . value ) ; } } SfdxFalconDebug . obj ( `${ dbgNs }askUserForSkipGroups:finalSkipGroups:` , finalSkipGroups , `finalSkipGroups: ` ) ; return finalSkipGroups ; } method returns { Promise < string > } description private async private async askUserForTargetOrgAlias ( ) { const targetOrgChoices = new Array < YeomanChoice > ( ) ; for ( const targetOrg of this . recipe . options . targetOrgs as JsonMap [ ] ) { targetOrgChoices . push ( { name : `${ targetOrg . orgName } -- ${ targetOrg . description }` , value : targetOrg as any , short : `${ targetOrg . orgName }` } ) ; } targetOrgChoices . push ( new inquirer . Separator ( ) ) ; targetOrgChoices . push ( { name : `Cancel Installation` , value : `CANCEL_INSTALLATION` , short : `Installation Canceled` } ) ; const iqPrompt = [ { type : 'list' , name : 'targetOrg' , message : 'Please select the installation target for this demo' , choices : targetOrgChoices , pageSize : 5 } , { type : 'confirm' , name : 'proceed' , default : false , message : answerHash => { return `The scratch org '${ answerHash . targetOrg . alias }' will be ` + `deleted before installing the demo. Proceed?` ; } , when : answerHash => { return ( answerHash . targetOrg . isScratchOrg && answerHash . targetOrg !== 'CANCEL_INSTALLATION' ) ; } } , { type : 'confirm' , name : 'proceed' , default : false , message : answerHash => { return `The alias '${ answerHash . targetOrg . alias }' must be associated ` + `with a compatible Salesforce org. Proceed with org validation?` ; } , when : answerHash => { return ( ! answerHash . targetOrg . isScratchOrg && answerHash . targetOrg !== 'CANCEL_INSTALLATION' ) ; } } , { type : 'confirm' , name : 'tryAgain' , message : answerHash => { return `Would you like to select a different demo target?` ; } , when : answerHash => { return ( ! answerHash . proceed && answerHash . targetOrg !== 'CANCEL_INSTALLATION' ) ; } } ] ; let userSelections ; do { console . log ( '' ) ; userSelections = await inquirer . prompt ( iqPrompt ) ; } while ( userSelections . proceed === false && userSelections . tryAgain === true ) ; SfdxFalconDebug . obj ( `${ dbgNs }askUserForTargetOrgAlias:userSelections:` , userSelections , `userSelections: ` ) ; if ( userSelections . proceed !== true ) { console . log ( '' ) ; throw new Error ( `INSTALLATION_CANCELLED: Installation cancelled at user's request` ) ; } if ( ! userSelections . targetOrg . alias ) { console . log ( '' ) ; throw new Error ( `ERROR_MISSING_DATA: Missing a value for targetOrg.alias` ) ; } return userSelections . targetOrg . alias ; } method returns { Promise < boolean > } description version private async private async confirmDemoTarget ( ) { if ( typeof this . engineContext . targetOrg === 'undefined' ) { throw new Error ( `ERROR_NO_TARGET_ORG: confirmDemoTarget was called ` + `before this.engineContext.targetOrg received a value` ) ; } const iqPrompt = [ { type : 'confirm' , name : 'proceed' , default : false , message : answerHash => { return `The Recipe '${ this . recipe . recipeName }' will install ` + `'${ this . engineContext . targetOrg . orgName }' using the alias ` + `'${ this . engineContext . targetOrg . alias }'.` + `\n  If this points to an existing scratch org ` + `it will be deleted before installation. Proceed?` ; } , when : ( this . engineContext . targetOrg . isScratchOrg === true ) } , { type : 'confirm' , name : 'proceed' , default : false , message : answerHash => { return `The Recipe '${ this . recipe . recipeName }' will install  ` + `'${ this . engineContext . targetOrg . orgName }' (${ this . engineContext . targetOrg . description }) to the ` + `standard (ie. non-scratch org). The alias ${ this . engineContext . targetOrg . alias } ` + `must be associated with a compatible Salesforce org. Proceed with org validation?` ; } , when : ( this . engineContext . targetOrg . isScratchOrg === false ) } ] ; console . log ( '' ) ; const userSelections = await inquirer . prompt ( iqPrompt ) ; return userSelections . proceed ; } }