2018-10-14 19:58:54 -03:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \
| ( ) _ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | , _ ) ( _ ) ___ ___ _ |
| ( '_`\ ( ' __ ) / '_` ) /' _ ` \ /' _ ` _ ` \ /'_ ` ) | | | | /',__)/ ' _ `\ /' _ ` \ |
| | ( _ ) ) | | ( ( _ | | ( ( _ ) || ( ) ( ) | ( ( _ | || | _ | | \ __ , \ | ( ) | ( ( _ ) ) |
| | , __ / '(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( ) _ ) | |
| ( _ ) \ ___ / ' |
| |
| General Bots Copyright ( c ) Pragmatismo . io . All rights reserved . |
| Licensed under the AGPL - 3.0 . |
| |
| According to our dual licensing model , this program can be used either |
| under the terms of the GNU Affero General Public License , version 3 , |
| or under a proprietary license . |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program . |
| |
| This program is distributed in the hope that it will be useful , |
| but WITHOUT ANY WARRANTY , without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the |
| GNU Affero General Public License for more details . |
| |
| "General Bots" is a registered trademark of Pragmatismo . io . |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license . Therefore any rights , title and interest in |
| our trademarks remain entirely with us . |
| |
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
"use strict" ;
import { GBService , IGBInstance } from "botlib" ;
import {
ResourceManagementClient ,
SubscriptionClient
} from "azure-arm-resource" ;
import { WebSiteManagementClient } from "azure-arm-website" ;
import { SqlManagementClient } from "azure-arm-sql" ;
import { CognitiveServicesManagementClient } from "azure-arm-cognitiveservices" ;
import { CognitiveServicesAccount } from "azure-arm-cognitiveservices/lib/models" ;
import { SearchManagementClient } from "azure-arm-search" ;
2018-10-25 18:13:51 -03:00
import { WebResource , ServiceClient , HttpMethods } from "ms-rest-js" ;
2018-10-14 19:58:54 -03:00
import * as simplegit from "simple-git/promise" ;
import { AppServicePlan } from "azure-arm-website/lib/models" ;
2018-10-30 19:52:40 -03:00
import { GBConfigService } from "../../../packages/core.gbapp/services/GBConfigService" ;
import { GBAdminService } from "../../../packages/admin.gbapp/services/GBAdminService" ;
2018-11-02 22:41:55 -03:00
import { GBCorePackage } from "packages/core.gbapp" ;
2018-10-15 21:03:17 -03:00
2018-10-25 18:13:51 -03:00
const Spinner = require ( "cli-spinner" ) . Spinner ;
2018-10-15 19:05:43 -03:00
const scanf = require ( "scanf" ) ;
2018-10-14 19:58:54 -03:00
const git = simplegit ( ) ;
const logger = require ( "../../../src/logger" ) ;
const UrlJoin = require ( "url-join" ) ;
2018-10-15 19:05:43 -03:00
const iconUrl =
"https://github.com/pragmatismo-io/BotServer/blob/master/docs/images/generalbots-logo-squared.png" ;
2018-10-25 18:13:51 -03:00
const publicIp = require ( "public-ip" ) ;
2018-10-15 21:03:17 -03:00
2018-10-14 19:58:54 -03:00
export class AzureDeployerService extends GBService {
instance : IGBInstance ;
resourceClient : ResourceManagementClient.ResourceManagementClient ;
webSiteClient : WebSiteManagementClient ;
storageClient : SqlManagementClient ;
cognitiveClient : CognitiveServicesManagementClient ;
searchClient : SearchManagementClient ;
provider = "Microsoft.BotService" ;
subscriptionClient : SubscriptionClient.SubscriptionClient ;
2018-10-15 19:05:43 -03:00
accessToken : string ;
location : string ;
2018-10-22 15:33:23 -03:00
public subscriptionId : string ;
2018-10-24 12:06:47 -03:00
static apiVersion = "2017-12-01" ;
2018-10-25 18:13:51 -03:00
farmName : any ;
2018-10-14 19:58:54 -03:00
2018-10-15 19:05:43 -03:00
public static async getSubscriptions ( credentials ) {
let subscriptionClient = new SubscriptionClient . default ( credentials ) ;
return subscriptionClient . subscriptions . list ( ) ;
2018-10-14 19:58:54 -03:00
}
2018-10-25 18:13:51 -03:00
public async deployFarm ( proxyAddress : string ) : Promise < IGBInstance > {
2018-10-22 15:33:23 -03:00
let culture = "en-us" ;
2018-10-14 19:58:54 -03:00
2018-10-25 18:13:51 -03:00
// Tries do get information from .env file otherwise asks in command-line.
2018-11-01 15:11:23 -03:00
2018-10-25 18:13:51 -03:00
let instance : IGBInstance = { } ;
instance = await this . ensureConfiguration ( instance ) ;
2018-11-01 15:11:23 -03:00
instance . marketplacePassword = GBAdminService . getRndPassword ( ) ;
2018-10-25 18:13:51 -03:00
let spinner = new Spinner ( "%s" ) ;
2018-10-24 12:06:47 -03:00
spinner . start ( ) ;
2018-10-25 18:13:51 -03:00
spinner . setSpinnerString ( "|/-\\" ) ;
2018-10-15 21:03:17 -03:00
2018-10-22 15:33:23 -03:00
let keys : any ;
2018-10-25 18:13:51 -03:00
let name = instance . botId ;
2018-10-15 21:03:17 -03:00
2018-10-25 18:13:51 -03:00
logger . info ( ` Deploying Deploy Group (It may take a few minutes)... ` ) ;
await this . createDeployGroup ( name , instance . cloudLocation ) ;
2018-10-14 19:58:54 -03:00
2018-11-01 15:11:23 -03:00
instance = await this . deployBootBot (
instance ,
name ,
` ${ proxyAddress } /api/messages/ ${ name } ` ,
instance . nlpAppId ,
instance . nlpKey ,
instance . cloudSubscriptionId
) ;
2018-10-15 21:03:17 -03:00
logger . info ( ` Deploying Bot Server... ` ) ;
2018-10-14 19:58:54 -03:00
let serverFarm = await this . createHostingPlan (
name ,
` ${ name } -server-plan ` ,
2018-10-25 18:13:51 -03:00
instance . cloudLocation
) ;
await this . createServer (
serverFarm . id ,
name ,
` ${ name } -server ` ,
instance . cloudLocation
2018-10-14 19:58:54 -03:00
) ;
2018-10-17 19:57:22 -03:00
logger . info ( ` Deploying Bot Storage... ` ) ;
2018-11-01 15:11:23 -03:00
let administratorLogin = ` sa ${ GBAdminService . getRndReadableIdentifier ( ) } ` ;
let administratorPassword = GBAdminService . getRndPassword ( ) ;
2018-10-25 18:13:51 -03:00
let storageServer = ` ${ name } -storage-server ` ;
let storageName = ` ${ name } -storage ` ;
2018-10-14 19:58:54 -03:00
await this . createStorageServer (
name ,
2018-10-25 18:13:51 -03:00
storageServer ,
2018-10-14 19:58:54 -03:00
administratorLogin ,
administratorPassword ,
2018-10-25 18:13:51 -03:00
storageServer ,
instance . cloudLocation
2018-10-14 19:58:54 -03:00
) ;
await this . createStorage (
name ,
2018-10-25 18:13:51 -03:00
storageServer ,
storageName ,
instance . cloudLocation
2018-10-14 19:58:54 -03:00
) ;
instance . storageUsername = administratorLogin ;
instance . storagePassword = administratorPassword ;
2018-10-25 18:13:51 -03:00
instance . storageName = storageName ;
2018-10-14 19:58:54 -03:00
instance . storageDialect = "mssql" ;
2018-10-25 18:13:51 -03:00
instance . storageServer = storageServer ;
2018-10-14 19:58:54 -03:00
2018-10-15 21:03:17 -03:00
logger . info ( ` Deploying Search... ` ) ;
let searchName = ` ${ name } -search ` ;
2018-10-25 18:13:51 -03:00
await this . createSearch ( name , searchName , instance . cloudLocation ) ;
2018-10-22 15:33:23 -03:00
let searchKeys = await this . searchClient . queryKeys . listBySearchService (
name ,
searchName
) ;
2018-10-15 21:03:17 -03:00
instance . searchHost = ` ${ searchName } .search.windows.net ` ;
2018-10-15 19:05:43 -03:00
instance . searchIndex = "azuresql-index" ;
instance . searchIndexer = "azuresql-indexer" ;
2018-10-22 15:33:23 -03:00
instance . searchKey = searchKeys [ 0 ] . key ;
2018-10-14 19:58:54 -03:00
2018-10-15 21:03:17 -03:00
logger . info ( ` Deploying Speech... ` ) ;
2018-10-25 18:13:51 -03:00
let speech = await this . createSpeech (
name ,
` ${ name } -speech ` ,
instance . cloudLocation
) ;
2018-10-14 19:58:54 -03:00
keys = await this . cognitiveClient . accounts . listKeys ( name , speech . name ) ;
instance . speechKeyEndpoint = speech . endpoint ;
instance . speechKey = keys . key1 ;
2018-10-15 21:03:17 -03:00
logger . info ( ` Deploying SpellChecker... ` ) ;
2018-10-14 19:58:54 -03:00
let spellChecker = await this . createSpellChecker (
name ,
` ${ name } -spellchecker ` ,
2018-10-25 18:13:51 -03:00
instance . cloudLocation
2018-10-14 19:58:54 -03:00
) ;
keys = await this . cognitiveClient . accounts . listKeys (
name ,
spellChecker . name
) ;
instance . spellCheckerKey = keys . key1 ;
instance . spellCheckerEndpoint = spellChecker . endpoint ;
2018-10-15 21:03:17 -03:00
logger . info ( ` Deploying Text Analytics... ` ) ;
2018-10-14 19:58:54 -03:00
let textAnalytics = await this . createTextAnalytics (
name ,
` ${ name } -textanalytics ` ,
2018-10-25 18:13:51 -03:00
instance . cloudLocation
2018-10-14 19:58:54 -03:00
) ;
keys = await this . cognitiveClient . accounts . listKeys (
name ,
textAnalytics . name
) ;
2018-10-15 19:05:43 -03:00
instance . textAnalyticsEndpoint = textAnalytics . endpoint ;
2018-10-14 19:58:54 -03:00
instance . textAnalyticsKey = keys . key1 ;
2018-10-25 18:13:51 -03:00
logger . info ( ` Deploying NLP... ` ) ;
let nlp = await this . createNLP ( name , ` ${ name } -nlp ` , instance . cloudLocation ) ;
keys = await this . cognitiveClient . accounts . listKeys ( name , nlp . name ) ;
let nlpAppId = await this . createLUISApp (
name ,
name ,
instance . cloudLocation ,
culture ,
instance . nlpAuthoringKey
) ;
2018-11-01 15:11:23 -03:00
2018-10-25 18:13:51 -03:00
instance . nlpEndpoint = nlp . endpoint ;
instance . nlpKey = keys . key1 ;
instance . nlpAppId = nlpAppId ;
2018-10-24 12:06:47 -03:00
logger . info ( ` Deploying Bot... ` ) ;
instance = await this . deployBootBot (
instance ,
name ,
` ${ proxyAddress } /api/messages/ ${ name } ` ,
2018-11-01 15:11:23 -03:00
instance . nlpAppId ,
instance . nlpKey ,
instance . cloudSubscriptionId
2018-10-24 12:06:47 -03:00
) ;
spinner . stop ( ) ;
2018-10-25 18:13:51 -03:00
return instance ;
}
public async openStorageFirewall ( groupName , serverName ) {
let username = GBConfigService . get ( "CLOUD_USERNAME" ) ;
let password = GBConfigService . get ( "CLOUD_PASSWORD" ) ;
let subscriptionId = GBConfigService . get ( "CLOUD_SUBSCRIPTIONID" ) ;
2018-10-30 19:52:40 -03:00
let credentials = await GBAdminService . getADALCredentialsFromUsername (
2018-10-25 18:13:51 -03:00
username ,
password
) ;
2018-10-30 19:52:40 -03:00
let storageClient = new SqlManagementClient ( credentials , subscriptionId ) ;
2018-10-25 18:13:51 -03:00
let ip = await publicIp . v4 ( ) ;
let params = {
startIpAddress : ip ,
endIpAddress : ip
} ;
await storageClient . firewallRules . createOrUpdate (
groupName ,
serverName ,
"gb" ,
params
) ;
}
private async ensureConfiguration ( instance : IGBInstance ) {
let username = GBConfigService . get ( "CLOUD_USERNAME" ) ;
let password = GBConfigService . get ( "CLOUD_PASSWORD" ) ;
let subscriptionId = GBConfigService . get ( "CLOUD_SUBSCRIPTIONID" ) ;
let location = GBConfigService . get ( "CLOUD_LOCATION" ) ;
let botId = GBConfigService . get ( "BOT_ID" ) ;
// No .env so asks for cloud credentials to start a new farm.
if ( ! username || ! password || ! subscriptionId || ! location || ! botId ) {
process . stdout . write (
2018-10-25 21:57:28 -03:00
"A empty enviroment is detected. To start automatic deploy, please enter some information:\n"
2018-10-25 18:13:51 -03:00
) ;
}
let retriveUsername = ( ) = > {
if ( ! username ) {
process . stdout . write ( "CLOUD_USERNAME:" ) ;
2018-10-28 21:56:51 -03:00
username = scanf ( "%s" ) . replace ( /(\n|\r)+$/ , "" ) ;
2018-10-25 18:13:51 -03:00
}
} ;
let retrivePassword = ( ) = > {
if ( ! password ) {
process . stdout . write ( "CLOUD_PASSWORD:" ) ;
2018-10-28 21:56:51 -03:00
password = scanf ( "%s" ) . replace ( /(\n|\r)+$/ , "" ) ;
2018-10-25 18:13:51 -03:00
}
} ;
let retrieveBotId = ( ) = > {
if ( ! botId ) {
process . stdout . write (
2018-10-28 21:56:51 -03:00
"Bot Id must only contain lowercase letters, digits or dashes, cannot start or end with or contain consecutive dashes and is limited from 4 to 42 characters long.\n"
2018-10-25 18:13:51 -03:00
) ;
process . stdout . write ( "BOT_ID:" ) ;
2018-10-28 21:56:51 -03:00
botId = scanf ( "%s" ) . replace ( /(\n|\r)+$/ , "" ) ;
2018-10-25 18:13:51 -03:00
}
} ;
let authoringKey = GBConfigService . get ( "NLP_AUTHORING_KEY" ) ;
let retriveAuthoringKey = ( ) = > {
if ( ! authoringKey ) {
process . stdout . write (
"Due to this opened issue: https://github.com/Microsoft/botbuilder-tools/issues/550\n"
) ;
process . stdout . write ( "Please enter your LUIS Authoring Key:" ) ;
2018-10-28 21:56:51 -03:00
authoringKey = scanf ( "%s" ) . replace ( /(\n|\r)+$/ , "" ) ;
2018-10-25 18:13:51 -03:00
}
} ;
while ( ! authoringKey ) {
retriveAuthoringKey ( ) ;
}
while ( ! botId ) {
retrieveBotId ( ) ;
}
while ( ! username ) {
retriveUsername ( ) ;
}
while ( ! password ) {
retrivePassword ( ) ;
}
// Connects to the cloud and retrives subscriptions.
2018-10-30 19:52:40 -03:00
let credentials = await GBAdminService . getADALCredentialsFromUsername (
2018-10-25 18:13:51 -03:00
username ,
password
) ;
if ( ! subscriptionId ) {
let map = { } ;
let index = 1 ;
let list = await AzureDeployerService . getSubscriptions ( credentials ) ;
list . forEach ( element = > {
console . log (
` ${ index } : ${ element . displayName } ( ${ element . subscriptionId } ) `
) ;
map [ index ++ ] = element ;
} ) ;
let subscriptionIndex ;
let retrieveSubscription = ( ) = > {
if ( ! subscriptionIndex ) {
process . stdout . write ( "CLOUD_SUBSCRIPTIONID (type a number):" ) ;
subscriptionIndex = scanf ( "%d" ) ;
}
} ;
while ( ! subscriptionIndex ) {
retrieveSubscription ( ) ;
}
subscriptionId = map [ subscriptionIndex ] . subscriptionId ;
}
let retriveLocation = ( ) = > {
if ( ! location ) {
2018-10-28 21:56:51 -03:00
process . stdout . write ( "CLOUD_LOCATION (eg. 'westus'):" ) ;
2018-10-25 18:13:51 -03:00
location = scanf ( "%s" ) ;
}
} ;
while ( ! location ) {
retriveLocation ( ) ;
}
// Prepares the first instance on bot farm.
instance . botId = botId ;
instance . cloudUsername = username ;
instance . cloudPassword = password ;
instance . cloudSubscriptionId = subscriptionId ;
instance . cloudLocation = location ;
instance . nlpAuthoringKey = authoringKey ;
2018-10-30 19:52:40 -03:00
instance . adminPass = GBAdminService . getRndPassword ( ) ;
2018-10-25 18:13:51 -03:00
this . resourceClient = new ResourceManagementClient . default (
credentials ,
subscriptionId
) ;
this . webSiteClient = new WebSiteManagementClient (
credentials ,
subscriptionId
) ;
this . storageClient = new SqlManagementClient ( credentials , subscriptionId ) ;
this . cognitiveClient = new CognitiveServicesManagementClient (
credentials ,
subscriptionId
) ;
this . searchClient = new SearchManagementClient ( credentials , subscriptionId ) ;
this . accessToken = credentials . tokenCache . _entries [ 0 ] . accessToken ;
2018-10-24 12:06:47 -03:00
2018-10-15 19:05:43 -03:00
return instance ;
}
2018-10-22 15:33:23 -03:00
public async deployBootBot (
2018-10-15 21:03:17 -03:00
instance ,
2018-10-25 21:57:28 -03:00
botId ,
2018-10-15 21:03:17 -03:00
endpoint ,
nlpAppId ,
nlpKey ,
2018-11-01 15:11:23 -03:00
subscriptionId
2018-10-15 21:03:17 -03:00
) {
2018-11-01 15:11:23 -03:00
let appId = GBConfigService . get ( "MSAPP_ID" ) ;
let appPassword = GBConfigService . get ( "MSAPP_PASSWORD" ) ;
if ( ! appId || ! appPassword ) {
process . stdout . write (
"Sorry, this part cannot be automated yet due to Microsoft schedule, please go to https://apps.dev.microsoft.com/portal/register-app to generate manually an App ID and App Secret.\n"
) ;
}
let retriveAppId = ( ) = > {
if ( ! appId ) {
process . stdout . write ( "Generated Application Id (MSAPP_ID):" ) ;
appId = scanf ( "%s" ) . replace ( /(\n|\r)+$/ , "" ) ;
}
} ;
let retriveAppPassword = ( ) = > {
if ( ! appPassword ) {
process . stdout . write ( "Generated Password (MSAPP_PASSWORD):" ) ;
appPassword = scanf ( "%s" ) . replace ( /(\n|\r)+$/ , "" ) ;
}
} ;
retriveAppId ( ) ;
retriveAppPassword ( ) ;
2018-10-28 21:56:51 -03:00
await this . internalDeployBot (
instance ,
2018-10-15 19:05:43 -03:00
this . accessToken ,
2018-10-24 12:06:47 -03:00
botId ,
2018-10-25 21:57:28 -03:00
botId ,
botId ,
2018-10-15 19:05:43 -03:00
"General BootBot" ,
endpoint ,
"global" ,
nlpAppId ,
nlpKey ,
2018-11-01 15:11:23 -03:00
appId ,
appPassword ,
subscriptionId
2018-10-15 19:05:43 -03:00
) ;
2018-10-24 12:06:47 -03:00
instance . marketplaceId = appId ;
2018-11-01 15:11:23 -03:00
instance . marketplacePassword = appPassword ;
2018-10-24 12:06:47 -03:00
instance . botId = botId ;
2018-10-15 19:05:43 -03:00
return instance ;
2018-10-14 19:58:54 -03:00
}
private async createStorageServer (
group ,
name ,
administratorLogin ,
administratorPassword ,
serverName ,
location
) {
var params = {
location : location ,
administratorLogin : administratorLogin ,
administratorLoginPassword : administratorPassword ,
fullyQualifiedDomainName : ` ${ serverName } .database.windows.net `
} ;
return this . storageClient . servers . createOrUpdate ( group , name , params ) ;
}
private async registerProviders ( subscriptionId , baseUrl , accessToken ) {
let query = ` subscriptions/ ${ subscriptionId } /providers/ ${
this . provider
} / register ? api - version = 2018 - 02 - 01 ` ;
let requestUrl = UrlJoin ( baseUrl , query ) ;
let req = new WebResource ( ) ;
req . method = "POST" ;
req . url = requestUrl ;
req . headers = { } ;
req . headers [ "Content-Type" ] = "application/json; charset=utf-8" ;
req . headers [ "accept-language" ] = "*" ;
req . headers [ "Authorization" ] = "Bearer " + accessToken ;
let httpClient = new ServiceClient ( ) ;
let res = await httpClient . sendRequest ( req ) ;
2018-11-01 15:11:23 -03:00
// TODO: Check res for error.
2018-10-14 19:58:54 -03:00
}
2018-10-24 12:06:47 -03:00
/ * *
* @see https : //github.com/Azure/azure-rest-api-specs/blob/master/specification/botservice/resource-manager/Microsoft.BotService/preview/2017-12-01/botservice.json
* /
2018-10-28 21:56:51 -03:00
private async internalDeployBot (
instance ,
2018-10-14 19:58:54 -03:00
accessToken ,
botId ,
name ,
2018-10-24 12:06:47 -03:00
group ,
2018-10-15 19:05:43 -03:00
description ,
endpoint ,
2018-10-14 19:58:54 -03:00
location ,
2018-10-15 19:05:43 -03:00
nlpAppId ,
nlpKey ,
2018-11-01 15:11:23 -03:00
appId ,
appPassword ,
subscriptionId
2018-10-14 19:58:54 -03:00
) {
2018-10-25 21:57:28 -03:00
return new Promise ( async ( resolve , reject ) = > {
let baseUrl = ` https://management.azure.com/ ` ;
await this . registerProviders ( subscriptionId , baseUrl , accessToken ) ;
2018-10-24 12:06:47 -03:00
2018-11-01 15:11:23 -03:00
instance . marketplaceId = appId ;
instance . marketplacePassword = appPassword ;
2018-11-02 22:41:55 -03:00
instance . engineName = GBCorePackage . CurrentEngineName ;
2018-10-14 19:58:54 -03:00
2018-10-25 21:57:28 -03:00
let parameters = {
location : location ,
sku : {
name : "F0"
} ,
name : botId ,
2018-10-30 19:52:40 -03:00
kind : "bot" ,
2018-10-25 21:57:28 -03:00
properties : {
description : description ,
displayName : name ,
endpoint : endpoint ,
iconUrl : iconUrl ,
2018-11-01 15:11:23 -03:00
//luisAppIds: [nlpAppId],
//luisKey: nlpKey,
2018-10-25 21:57:28 -03:00
msaAppId : appId ,
2018-10-30 19:52:40 -03:00
msaAppPassword : appPassword ,
2018-11-01 15:11:23 -03:00
//enabledChannels: ["webchat"], // , "skype", "facebook"],
configuredChannels : [ "webchat" ] // , "skype", "facebook"]
2018-10-25 21:57:28 -03:00
}
} ;
2018-10-14 19:58:54 -03:00
2018-10-25 21:57:28 -03:00
let httpClient = new ServiceClient ( ) ;
let query = ` subscriptions/ ${ subscriptionId } /resourceGroups/ ${ group } /providers/ ${
this . provider
} / botServices / $ { botId } ? api - version = $ { AzureDeployerService . apiVersion } ` ;
let url = UrlJoin ( baseUrl , query ) ;
let req = this . createRequestObject (
url ,
accessToken ,
2018-10-28 21:56:51 -03:00
"PUT" ,
2018-10-25 21:57:28 -03:00
JSON . stringify ( parameters )
) ;
let res = await httpClient . sendRequest ( req ) ;
2018-10-28 21:56:51 -03:00
if ( ! ( res . bodyAsJson as any ) . id ) {
reject ( res . bodyAsText ) ;
return ;
}
2018-11-01 15:11:23 -03:00
2018-10-28 21:56:51 -03:00
logger . info ( ` Bot creation request done waiting for key generation... ` ) ;
2018-11-01 15:11:23 -03:00
resolve ( instance ) ;
2018-10-25 21:57:28 -03:00
setTimeout ( async ( ) = > {
2018-10-28 21:56:51 -03:00
try {
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 ,
2018-11-01 15:11:23 -03:00
"GET" ,
2018-10-28 21:56:51 -03:00
JSON . stringify ( parameters )
) ;
let resChannel = await httpClient . sendRequest ( req ) ;
let key = ( resChannel . bodyAsJson as any ) . properties . properties
. sites [ 0 ] . key ;
instance . webchatKey = key ;
resolve ( instance ) ;
} catch ( error ) {
reject ( error ) ;
}
} , 20000 ) ;
} ) ;
}
2018-10-30 19:52:40 -03:00
public async updateBotProxy ( botId , group , endpoint ) {
2018-10-28 21:56:51 -03:00
let baseUrl = ` https://management.azure.com/ ` ;
let username = GBConfigService . get ( "CLOUD_USERNAME" ) ;
let password = GBConfigService . get ( "CLOUD_PASSWORD" ) ;
let subscriptionId = GBConfigService . get ( "CLOUD_SUBSCRIPTIONID" ) ;
2018-11-01 15:11:23 -03:00
let accessToken = await GBAdminService . getADALTokenFromUsername (
username ,
password
) ;
2018-10-30 19:52:40 -03:00
let httpClient = new ServiceClient ( ) ;
2018-10-24 12:06:47 -03:00
2018-10-28 21:56:51 -03:00
let parameters = {
properties : {
endpoint : endpoint
}
} ;
2018-10-24 12:06:47 -03:00
2018-10-28 21:56:51 -03:00
let query = ` subscriptions/ ${ subscriptionId } /resourceGroups/ ${ group } /providers/ ${
this . provider
} / botServices / $ { botId } ? api - version = $ { AzureDeployerService . apiVersion } ` ;
let url = UrlJoin ( baseUrl , query ) ;
let req = this . createRequestObject (
url ,
accessToken ,
"PATCH" ,
JSON . stringify ( parameters )
) ;
let res = await httpClient . sendRequest ( req ) ;
if ( ! ( res . bodyAsJson as any ) . id ) {
throw res . bodyAsText ;
}
logger . info ( ` Bot proxy updated at: ${ endpoint } . ` ) ;
2018-10-24 12:06:47 -03:00
}
2018-10-14 19:58:54 -03:00
2018-10-28 21:56:51 -03:00
private createRequestObject (
url : string ,
accessToken : string ,
verb : HttpMethods ,
body : string
) {
2018-10-14 19:58:54 -03:00
let req = new WebResource ( ) ;
2018-10-28 21:56:51 -03:00
req . method = verb ;
2018-10-24 12:06:47 -03:00
req . url = url ;
2018-10-14 19:58:54 -03:00
req . headers = { } ;
req . headers [ "Content-Type" ] = "application/json" ;
req . headers [ "accept-language" ] = "*" ;
req . headers [ "Authorization" ] = "Bearer " + accessToken ;
2018-10-24 12:06:47 -03:00
req . body = body ;
return req ;
2018-10-14 19:58:54 -03:00
}
2018-10-22 15:33:23 -03:00
private async createLUISApp (
name : string ,
description : string ,
location : string ,
2018-10-25 18:13:51 -03:00
culture : string ,
authoringKey : string
2018-10-22 15:33:23 -03:00
) {
let parameters = {
name : name ,
description : description ,
culture : culture
} ;
2018-10-24 12:06:47 -03:00
2018-10-25 18:13:51 -03:00
let body = JSON . stringify ( parameters ) ;
let apps = await this . makeNlpRequest (
location ,
authoringKey ,
null ,
"GET" ,
"apps"
) ;
let app = ( apps . bodyAsJson as any ) . filter ( x = > x . name == name ) [ 0 ] ;
let id : string ;
if ( ! app ) {
let res = await this . makeNlpRequest (
location ,
authoringKey ,
body ,
"POST" ,
"apps"
) ;
id = res . bodyAsText ;
} else {
id = app . id ;
}
return id ;
}
private async makeNlpRequest (
location : string ,
authoringKey : string ,
body : string ,
method : HttpMethods ,
resource : string
) {
let req = new WebResource ( ) ;
req . method = method ;
req . url = ` https:// ${ location } .api.cognitive.microsoft.com/luis/api/v2.0/ ${ resource } ` ;
2018-10-22 15:33:23 -03:00
req . headers = { } ;
req . headers [ "Content-Type" ] = "application/json" ;
req . headers [ "accept-language" ] = "*" ;
req . headers [ "Ocp-Apim-Subscription-Key" ] = authoringKey ;
2018-10-25 18:13:51 -03:00
req . body = body ;
2018-10-22 15:33:23 -03:00
let httpClient = new ServiceClient ( ) ;
let res = await httpClient . sendRequest ( req ) ;
2018-10-25 18:13:51 -03:00
return res ;
2018-10-22 15:33:23 -03:00
}
2018-10-14 19:58:54 -03:00
private async createSearch ( group , name , location ) {
var params = {
sku : { name : "free" } ,
location : location
} ;
return this . searchClient . services . createOrUpdate ( group , name , params ) ;
}
private async createStorage ( group , serverName , name , location ) {
var params = {
sku : { name : "Free" } ,
createMode : "Default" ,
location : location
} ;
return this . storageClient . databases . createOrUpdate (
group ,
serverName ,
name ,
params
) ;
}
private async createCognitiveServices (
group ,
name ,
location ,
kind
) : Promise < CognitiveServicesAccount > {
let params = {
sku : { name : "F0" } ,
createMode : "Default" ,
location : location ,
kind : kind ,
properties : { }
} ;
return await this . cognitiveClient . accounts . create ( group , name , params ) ;
}
private async createSpeech (
group ,
name ,
location
) : Promise < CognitiveServicesAccount > {
return await this . createCognitiveServices (
group ,
name ,
location ,
"SpeechServices"
) ;
}
private async createNLP (
group ,
name ,
location
) : Promise < CognitiveServicesAccount > {
return await this . createCognitiveServices ( group , name , location , "LUIS" ) ;
}
private async createSpellChecker (
group ,
name ,
location
) : Promise < CognitiveServicesAccount > {
return await this . createCognitiveServices (
group ,
name ,
"global" ,
"Bing.SpellCheck.v7"
) ;
}
private async createTextAnalytics (
group ,
name ,
location
) : Promise < CognitiveServicesAccount > {
return await this . createCognitiveServices (
group ,
name ,
location ,
"TextAnalytics"
) ;
}
2018-10-15 21:03:17 -03:00
private async createDeployGroup ( name , location ) {
2018-10-14 19:58:54 -03:00
var params = { location : location } ;
return this . resourceClient . resourceGroups . createOrUpdate ( name , params ) ;
}
private async createHostingPlan (
group ,
name ,
location
) : Promise < AppServicePlan > {
let params = {
serverFarmWithRichSkuName : name ,
location : location ,
sku : {
name : "F1" ,
capacity : 1 ,
tier : "Free"
}
} ;
return this . webSiteClient . appServicePlans . createOrUpdate (
group ,
name ,
params
) ;
}
private async createServer ( farmId , group , name , location ) {
var parameters = {
location : location ,
serverFarmId : farmId
} ;
return this . webSiteClient . webApps . createOrUpdate ( group , name , parameters ) ;
}
private async updateWebisteConfig ( group , serverFarmId , name , location ) {
var siteConfig = {
location : location ,
serverFarmId : serverFarmId ,
numberOfWorkers : 1 ,
phpVersion : "5.5"
} ;
2018-10-30 19:52:40 -03:00
// TODO: Copy .env to app settings.
2018-10-14 19:58:54 -03:00
return this . webSiteClient . webApps . createOrUpdateConfiguration (
group ,
name ,
siteConfig
) ;
}
async deployGeneralBotsToAzure() {
let status = await git . status ( ) ;
2018-10-30 19:52:40 -03:00
// TODO: Copy github to webapp.
2018-10-14 19:58:54 -03:00
}
2018-10-15 21:03:17 -03:00
2018-10-22 15:33:23 -03:00
static getKBSearchSchema ( indexName ) {
return {
name : indexName ,
fields : [
{
name : "questionId" ,
type : "Edm.String" ,
searchable : false ,
filterable : false ,
retrievable : true ,
sortable : false ,
facetable : false ,
key : true
} ,
{
name : "subject1" ,
type : "Edm.String" ,
searchable : true ,
filterable : false ,
retrievable : false ,
sortable : false ,
facetable : false ,
key : false
} ,
{
name : "subject2" ,
type : "Edm.String" ,
searchable : true ,
filterable : false ,
retrievable : false ,
sortable : false ,
facetable : false ,
key : false
} ,
{
name : "subject3" ,
type : "Edm.String" ,
searchable : true ,
filterable : false ,
retrievable : false ,
sortable : false ,
facetable : false ,
key : false
} ,
{
name : "subject4" ,
type : "Edm.String" ,
searchable : true ,
filterable : false ,
retrievable : false ,
sortable : false ,
facetable : false ,
key : false
} ,
{
name : "content" ,
type : "Edm.String" ,
searchable : true ,
filterable : false ,
retrievable : false ,
sortable : false ,
facetable : false ,
key : false
} ,
{
name : "answerId" ,
type : "Edm.Int32" ,
searchable : false ,
filterable : false ,
retrievable : true ,
sortable : false ,
facetable : false ,
key : false
} ,
{
name : "instanceId" ,
type : "Edm.Int32" ,
searchable : false ,
filterable : true ,
retrievable : true ,
sortable : false ,
facetable : false ,
key : false
} ,
{
name : "packageId" ,
type : "Edm.Int32" ,
searchable : false ,
filterable : true ,
retrievable : true ,
sortable : false ,
facetable : false ,
key : false
}
] ,
scoringProfiles : [ ] ,
defaultScoringProfile : null ,
corsOptions : null
} ;
}
2018-10-14 19:58:54 -03:00
}