import * as path from "path" ; import * as vscode from "vscode" ; import { OperationCanceledError } from "../common/Error/OperationCanceledError" ; import { OperationFailedError } from "../common/Error/OperationFailedErrors/OperationFailedError" ; import { ConfigKey , EventNames , FileNames , ScaffoldType } from "../constants" ; import { FileUtility } from "../FileUtility" ; import { TelemetryContext , TelemetryWorker } from "../telemetry" ; import * as utils from "../utils" ; import { checkAzureLogin } from "./Apis" ; import { Compilable } from "./Interfaces/Compilable" ; import { Component , ComponentType } from "./Interfaces/Component" ; import { Deployable } from "./Interfaces/Deployable" ; import { Device } from "./Interfaces/Device" ; import { ProjectHostType } from "./Interfaces/ProjectHostType" ; import { ProjectTemplateType , TemplateFileInfo } from "./Interfaces/ProjectTemplate" ; import { Provisionable } from "./Interfaces/Provisionable" ; import { Uploadable } from "./Interfaces/Uploadable" ; import { DirectoryNotFoundError } from "../common/Error/OperationFailedErrors/DirectoryNotFoundError" ; const importLazy = require ( "import-lazy" ) ; const azureUtilityModule = importLazy ( ( ) => require ( "./AzureUtility" ) ) ( ) ; export enum OpenScenario { createNewProject , configureProject } export abstract class IoTWorkbenchProjectBase { protected extensionContext : vscode . ExtensionContext ; protected channel : vscode . OutputChannel ; protected telemetryContext : TelemetryContext ; protected projectRootPath = "" ; protected iotWorkbenchProjectFilePath = "" ; protected componentList : Component [ ] ; protected projectHostType : ProjectHostType = ProjectHostType . Unknown ; param scaffoldType param projectFileRootPath static async getProjectType ( scaffoldType , projectFileRootPath ) { if ( ! projectFileRootPath ) { return ProjectHostType . Unknown ; } const iotWorkbenchProjectFile = path . join ( projectFileRootPath , FileNames . iotWorkbenchProjectFileName ) ; if ( ! ( await FileUtility . fileExists ( scaffoldType , iotWorkbenchProjectFile ) ) ) { return ProjectHostType . Unknown ; } const iotWorkbenchProjectFileString = ( ( await FileUtility . readFile ( scaffoldType , iotWorkbenchProjectFile , "utf8" ) ) as string ) . trim ( ) ; if ( iotWorkbenchProjectFileString ) { const projectConfig = JSON . parse ( iotWorkbenchProjectFileString ) ; if ( projectConfig && projectConfig [ `${ ConfigKey . projectHostType }` ] !== undefined ) { const projectHostType = utils . getEnumKeyByEnumValue ( ProjectHostType , projectConfig [ `${ ConfigKey . projectHostType }` ] ) ; return projectHostType ; } } const devcontainerFolderPath = path . join ( projectFileRootPath , FileNames . devcontainerFolderName ) ; if ( await FileUtility . directoryExists ( scaffoldType , devcontainerFolderPath ) ) { return ProjectHostType . Container ; } else { return ProjectHostType . Workspace ; } } canProvision ( comp ) { return ( comp as Provisionable ) . provision !== undefined ; } canDeploy ( comp ) { return ( comp as Deployable ) . deploy !== undefined ; } canCompile ( comp ) { return ( comp as Compilable ) . compile !== undefined ; } canUpload ( comp ) { return ( comp as Uploadable ) . upload !== undefined ; } constructor ( context , channel , telemetryContext ) { this . componentList = [ ] ; this . extensionContext = context ; this . channel = channel ; this . telemetryContext = telemetryContext ; } abstract async load ( scaffoldType , initLoad ? ) ; abstract async create ( templateFilesInfo , projectType , boardId , openInNewWindow ) ; async compile ( ) { for ( const item of this . componentList ) { if ( this . canCompile ( item ) ) { await item . checkPrerequisites ( "compile device code" ) ; const res = await item . compile ( ) ; if ( ! res ) { vscode . window . showErrorMessage ( "Unable to compile the device code, please check output window for detail." ) ; } } } return true ; } async upload ( ) { for ( const item of this . componentList ) { if ( this . canUpload ( item ) ) { await item . checkPrerequisites ( "upload device code" ) ; const res = await item . upload ( ) ; if ( ! res ) { vscode . window . showErrorMessage ( "Unable to upload the sketch, please check output window for detail." ) ; } } } return true ; } async provision ( ) { const provisionItemList = [ ] ; for ( const item of this . componentList ) { if ( this . canProvision ( item ) ) { await item . checkPrerequisites ( "provision" ) ; provisionItemList . push ( item . name ) ; } } if ( provisionItemList . length === 0 ) { vscode . window . showInformationMessage ( "Congratulations! There is no Azure service to provision in this project." ) ; return false ; } let subscriptionId = "" ; let resourceGroup = "" ; if ( provisionItemList . length > 0 ) { await checkAzureLogin ( ) ; azureUtilityModule . AzureUtility . init ( this . extensionContext , this . projectRootPath , this . channel ) ; resourceGroup = await azureUtilityModule . AzureUtility . getResourceGroup ( ) ; subscriptionId = azureUtilityModule . AzureUtility . subscriptionId ; if ( ! resourceGroup || ! subscriptionId ) { return false ; } } else { return false ; } for ( const item of this . componentList ) { const _provisionItemList = [ ] ; if ( this . canProvision ( item ) ) { for ( let i = 0 ; i < provisionItemList . length ; i ++ ) { if ( provisionItemList [ i ] === item . name ) { _provisionItemList [ i ] = `>> ${ i + 1 }. ${ provisionItemList [ i ] }` ; } else { _provisionItemList [ i ] = `${ i + 1 }. ${ provisionItemList [ i ] }` ; } } const selection = await vscode . window . showQuickPick ( [ { label : _provisionItemList . join ( "   -   " ) , description : "" , detail : "Click to continue" } ] , { ignoreFocusOut : true , placeHolder : "Provision process" } ) ; if ( ! selection ) { return false ; } const res = await item . provision ( ) ; if ( ! res ) { throw new OperationCanceledError ( "Provision cancelled." ) ; } } } return true ; } async deploy ( ) { let azureLoggedIn = false ; const deployItemList = [ ] ; for ( const item of this . componentList ) { if ( this . canDeploy ( item ) ) { await item . checkPrerequisites ( "deploy device code" ) ; deployItemList . push ( item . name ) ; } } if ( deployItemList && deployItemList . length <= 0 ) { await vscode . window . showInformationMessage ( "Congratulations! The project does not contain any Azure components to be deployed." ) ; return ; } if ( ! azureLoggedIn ) { azureLoggedIn = await checkAzureLogin ( ) ; } for ( const item of this . componentList ) { const _deployItemList = [ ] ; if ( this . canDeploy ( item ) ) { for ( let i = 0 ; i < deployItemList . length ; i ++ ) { if ( deployItemList [ i ] === item . name ) { _deployItemList [ i ] = `>> ${ i + 1 }. ${ deployItemList [ i ] }` ; } else { _deployItemList [ i ] = `${ i + 1 }. ${ deployItemList [ i ] }` ; } } const selection = await vscode . window . showQuickPick ( [ { label : _deployItemList . join ( "   -   " ) , description : "" , detail : "Click to continue" } ] , { ignoreFocusOut : true , placeHolder : "Deploy process" } ) ; if ( ! selection ) { throw new OperationCanceledError ( `Component deployment cancelled.` ) ; } const res = await item . deploy ( ) ; if ( ! res ) { throw new OperationFailedError ( "deploy iot workbench project" , `Failed to deploy component ${ item . name }` , "" ) ; } } } vscode . window . showInformationMessage ( "Azure deploy succeeded." ) ; } /**
     * Configure project environment: Scaffold configuration files with the given
     * template files.
     */ async configureProjectEnvironmentCore ( deviceRootPath , scaffoldType ) { for ( const component of this . componentList ) { if ( component . getComponentType ( ) === ComponentType . Device ) { const device = component as Device ; await device . configDeviceEnvironment ( deviceRootPath , scaffoldType ) ; } } } abstract async openProject ( scaffoldType , openInNewWindow , openScenario ) ; async configDeviceSettings ( ) { for ( const component of this . componentList ) { if ( component . getComponentType ( ) === ComponentType . Device ) { const device = component as Device ; await device . configDeviceSettings ( ) ; } } return true ; } /**
     * Send telemetry when the IoT project is load when VS Code opens
     */ sendLoadEventTelemetry ( context ) { const telemetryWorker = TelemetryWorker . getInstance ( context ) ; try { telemetryWorker . sendEvent ( EventNames . projectLoadEvent , this . telemetryContext ) ; } catch { } } param scaffoldType async validateProjectRootPathExists ( operation , scaffoldType ) { if ( ! ( await FileUtility . directoryExists ( scaffoldType , this . projectRootPath ) ) ) { throw new DirectoryNotFoundError ( operation , `project root path ${ this . projectRootPath }` , "Please initialize the project first." ) ; } } }