diff --git a/deploy/azuredeployer.gbapp/services/AzureDeployerService.ts b/deploy/azuredeployer.gbapp/services/AzureDeployerService.ts index b0d2fc66..99cdab37 100644 --- a/deploy/azuredeployer.gbapp/services/AzureDeployerService.ts +++ b/deploy/azuredeployer.gbapp/services/AzureDeployerService.ts @@ -47,6 +47,7 @@ import * as simplegit from "simple-git/promise"; import { AppServicePlan } from "azure-arm-website/lib/models"; import { GBConfigService } from "../../../deploy/core.gbapp/services/GBConfigService"; +const Spinner = require('cli-spinner').Spinner; const scanf = require("scanf"); const msRestAzure = require("ms-rest-azure"); const git = simplegit(); @@ -68,6 +69,7 @@ export class AzureDeployerService extends GBService { accessToken: string; location: string; public subscriptionId: string; + static apiVersion = "2017-12-01"; constructor(credentials, subscriptionId, location) { super(); @@ -103,26 +105,11 @@ export class AzureDeployerService extends GBService { let instance: any = {}; let culture = "en-us"; - logger.info(`Starting infrastructure deployment...`); + let spinner = new Spinner('%s'); + spinner.start(); + spinner.setSpinnerString("⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈"); let keys: any; - logger.info(`Deploying NLP...`); - let nlp = await this.createNLP(name, `${name}-nlp`, location); - keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name); - let nlpAppId = await this.createLUISApp(name, name, location, culture); - - - await this.deployBootBot( - instance, - name, - proxyAddress, - nlpAppId, - keys.key1, - this.subscriptionId - ); - instance.nlpEndpoint = nlp.endpoint; - instance.nlpKey = keys.key1; - instance.nlpAppId = nlpAppId; logger.info(`Deploying Deploy Group...`); await this.createDeployGroup(name, location); @@ -174,7 +161,13 @@ export class AzureDeployerService extends GBService { instance.searchIndexer = "azuresql-indexer"; instance.searchKey = searchKeys[0].key; - ////////////// LUS + logger.info(`Deploying NLP...`); + let nlp = await this.createNLP(name, `${name}-nlp`, location); + keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name); + let nlpAppId = await this.createLUISApp(name, name, location, culture); + instance.nlpEndpoint = nlp.endpoint; + instance.nlpKey = keys.key1; + instance.nlpAppId = nlpAppId; logger.info(`Deploying Speech...`); let speech = await this.createSpeech(name, `${name}-speech`, location); @@ -208,6 +201,20 @@ export class AzureDeployerService extends GBService { instance.textAnalyticsEndpoint = textAnalytics.endpoint; instance.textAnalyticsKey = keys.key1; + let appId = msRestAzure.generateUuid(); + logger.info(`Deploying Bot...`); + instance = await this.deployBootBot( + instance, + name, + `${proxyAddress}/api/messages/${name}`, + nlpAppId, + keys.key1, + this.subscriptionId, + appId + ); + + spinner.stop(); + return instance; } @@ -217,12 +224,19 @@ export class AzureDeployerService extends GBService { endpoint, nlpAppId, nlpKey, - subscriptionId + subscriptionId, + appId ) { logger.info(`Deploying Bot...`); - await this.internalDeployBot( + + let botId = name + AzureDeployerService.getRndBotId(); + + [ + instance.marketplacePassword, + instance.webchatKey + ] = await this.internalDeployBot( this.accessToken, - name, + botId, name, name, "General BootBot", @@ -230,11 +244,11 @@ export class AzureDeployerService extends GBService { "global", nlpAppId, nlpKey, - subscriptionId + subscriptionId, + appId ); - instance.webchatKey = "********"; - instance.marketplaceId = "0ff1ce73-0aea-442a-a222-dcc340eca294"; - instance.marketplacePassword = "************"; + instance.marketplaceId = appId; + instance.botId = botId; return instance; } @@ -280,29 +294,33 @@ export class AzureDeployerService extends GBService { let res = await httpClient.sendRequest(req); } + /** + * @see https://github.com/Azure/azure-rest-api-specs/blob/master/specification/botservice/resource-manager/Microsoft.BotService/preview/2017-12-01/botservice.json + */ private async internalDeployBot( accessToken, botId, - group, name, + group, description, endpoint, location, nlpAppId, nlpKey, - subscriptionId + subscriptionId, + appId ) { let baseUrl = `https://management.azure.com/`; - this.registerProviders(subscriptionId, baseUrl, accessToken); - let appId = msRestAzure.generateUuid(); + await this.registerProviders(subscriptionId, baseUrl, accessToken); + + let appPassword = AzureDeployerService.getRndPassword(); let parameters = { location: location, sku: { name: "F0" }, - name: name, - id: botId, + name: botId, kind: "sdk", properties: { description: description, @@ -311,30 +329,49 @@ export class AzureDeployerService extends GBService { iconUrl: iconUrl, luisAppIds: [nlpAppId], luisKey: nlpKey, - msaAppId: appId + msaAppId: appId, + msaAppPassword: appPassword } }; + let httpClient = new ServiceClient(); + let query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${ this.provider - }/botServices/${botId}?api-version=2017-12-01`; - let requestUrl = UrlJoin(baseUrl, query); + }/botServices/${botId}?api-version=${AzureDeployerService.apiVersion}`; + let url = UrlJoin(baseUrl, query); + let req = this.createRequestObject( + url, + accessToken, + JSON.stringify(parameters) + ); + let res = await httpClient.sendRequest(req); + query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/Microsoft.BotService/botServices/${botId}/channels/WebChatChannel/listChannelWithKeys?api-version=${ + AzureDeployerService.apiVersion + }`; + url = UrlJoin(baseUrl, query); + req = this.createRequestObject( + url, + accessToken, + JSON.stringify(parameters) + ); + let resChannel = await httpClient.sendRequest(req); + + let key = (resChannel.bodyAsJson as any).properties.properties.sites[0].key; + return [appPassword, key]; + } + + private createRequestObject(url: string, accessToken: string, body) { let req = new WebResource(); req.method = "PUT"; - req.url = requestUrl; + req.url = url; req.headers = {}; req.headers["Content-Type"] = "application/json"; req.headers["accept-language"] = "*"; req.headers["Authorization"] = "Bearer " + accessToken; - - let requestContent = JSON.stringify(parameters); - req.body = requestContent; - - let httpClient = new ServiceClient(); - let res = await httpClient.sendRequest(req); - - return JSON.parse(res.bodyAsJson as string); + req.body = body; + return req; } private async createLUISApp( @@ -350,14 +387,14 @@ export class AzureDeployerService extends GBService { }; let requestUrl = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/apps/`; let req = new WebResource(); - + req.method = "POST"; req.url = requestUrl; req.headers = {}; req.headers["Content-Type"] = "application/json"; req.headers["accept-language"] = "*"; - let authoringKey; + let authoringKey = GBConfigService.get("NLP_AUTHORING_KEY"); let retriveAuthoringKey = () => { if (!authoringKey) { process.stdout.write( @@ -378,7 +415,7 @@ export class AzureDeployerService extends GBService { let httpClient = new ServiceClient(); let res = await httpClient.sendRequest(req); - return res.bodyAsText; + return res.bodyAsJson; } private async createSearch(group, name, location) { @@ -540,6 +577,20 @@ export class AzureDeployerService extends GBService { return `sa${generated}`; } + private static getRndBotId() { + const passwordGenerator = new PasswordGenerator(); + const options = { + upperCaseAlpha: false, + lowerCaseAlpha: true, + number: true, + specialCharacter: false, + minimumLength: 8, + maximumLength: 8 + }; + let generated = passwordGenerator.generatePassword(options); + return `${generated}`; + } + private static getRndPassword() { const passwordGenerator = new PasswordGenerator(); const options = { @@ -548,7 +599,7 @@ export class AzureDeployerService extends GBService { number: true, specialCharacter: true, minimumLength: 8, - maximumLength: 8 + maximumLength: 14 }; let password = passwordGenerator.generatePassword(options); return password; diff --git a/deploy/azuredeployer.gbapp/strings.ts b/deploy/azuredeployer.gbapp/strings.ts index 35481519..ba5e75e0 100644 --- a/deploy/azuredeployer.gbapp/strings.ts +++ b/deploy/azuredeployer.gbapp/strings.ts @@ -1,22 +1,8 @@ export const Messages = { "en-US": { about_suggestions: "Suggestions are welcomed and improve my quality...", - what_about_service: "What about my service?", - glad_you_liked: "I'm glad you liked. I'm here for you.", - we_will_improve: "Let's take note of that, thanks for sharing.", - what_about_me: "What about the service, please rate between 1 and 5.", - thanks: "Thanks!", - im_sorry_lets_try: "I'm sorry. Let's try again...", - great_thanks: "Great, thanks for sharing your thoughts." }, "pt-BR": { about_suggestions: "Sugestões melhoram muito minha qualidade...", - what_about_service:"O que achou do meu atendimento?", - glad_you_liked: "Bom saber que você gostou. Conte comigo.", - we_will_improve: "Vamos registrar sua questão, obrigado pela sinceridade.", - what_about_me: "O que achou do meu atendimento, de 1 a 5?", - thanks: "Obrigado!", - im_sorry_lets_try: "Desculpe-me, vamos tentar novamente.", - great_thanks: "Ótimo, obrigado por contribuir com sua resposta." } }; diff --git a/deploy/core.gbapp/services/GBConfigService.ts b/deploy/core.gbapp/services/GBConfigService.ts index 2676c1b1..28b359e4 100644 --- a/deploy/core.gbapp/services/GBConfigService.ts +++ b/deploy/core.gbapp/services/GBConfigService.ts @@ -36,7 +36,6 @@ import * as fs from "fs"; "use strict"; export class GBConfigService { - static init(): any { try { require("dotenv-extended").load({ @@ -52,9 +51,8 @@ export class GBConfigService { } public writeEntry(name, value) { - if (fs.exists) { - fs.appendFileSync('.env',`${name}=${value}`); + fs.appendFileSync(".env", `${name}=${value}`); } } @@ -63,35 +61,48 @@ export class GBConfigService { if (!value) { switch (key) { + case "CLOUD_USERNAME": + value = undefined; + break; + case "CLOUD_PASSWORD": + value = undefined; + break; + case "CLOUD_SUBSCRIPTIONID": + value = undefined; + break; + case "CLOUD_LOCATION": + value = undefined; + break; + case "NLP_AUTHORING_KEY": + value = undefined; + break; case "STORAGE_DIALECT": value = "sqlite"; break; - case "STORAGE_STORAGE": value = "./guaribas.sqlite"; break; - case "ADDITIONAL_DEPLOY_PATH": value = undefined; break; - case "STORAGE_SYNC": + value = "false"; + break; case "STORAGE_SYNC_ALTER": + value = "false"; + break; case "STORAGE_SYNC_FORCE": value = "false"; break; - case "STORAGE_LOGGING": value = "false"; break; - case "STORAGE_ENCRYPT": value = "true"; break; - default: - logger.info( - `Guaribas General Error: Invalid key on .env file: '${key}'` + logger.warn( + `Invalid key on .env file: '${key}'` ); break; } diff --git a/package.json b/package.json index 02db9120..50ed5eb1 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "chai": "4.2.0", "child_process": "^1.0.2", "chokidar": "2.0.4", + "cli-spinner": "^0.2.8", "csv-parse": "3.1.3", "dotenv-extended": "2.3.0", "express": "4.16.4", diff --git a/src/app.ts b/src/app.ts index 5d66e9c4..122cfda3 100644 --- a/src/app.ts +++ b/src/app.ts @@ -97,8 +97,9 @@ export class GBServer { // Ensures cloud / on-premises infrastructure is setup. - logger.info(`Connecting to the infrastructure...`); + logger.info(`Establishing a development local proxy...`); let proxyAddress = await core.ensureProxy(); + logger.info(`Ensuring services availability...`); let cloudDeployer = await AzureDeployerService.ensureDeployer(); let instance = await cloudDeployer.deployFarm('gbot', 'westus', proxyAddress);