2018-10-14 19:58:54 -03:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * \
| ( ) _ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | , _ ) ( _ ) ___ ___ _ |
| ( '_`\ ( ' __ ) / '_` ) /' _ ` \ /' _ ` _ ` \ /'_ ` ) | | | | /',__)/ ' _ `\ /' _ ` \ |
| | ( _ ) ) | | ( ( _ | | ( ( _ ) || ( ) ( ) | ( ( _ | || | _ | | \ __ , \ | ( ) | ( ( _ ) ) |
| | , __ / '(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( ) _ ) | |
| ( _ ) \ ___ / ' |
| |
| General Bots Copyright ( c ) Pragmatismo . io . All rights reserved . |
| Licensed under the AGPL - 3.0 . |
2018-11-11 19:09:18 -02:00
| |
2018-10-14 19:58:54 -03:00
| 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 . |
| |
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2018-11-11 19:09:18 -02:00
/ * *
* @fileoverview General Bots server core .
* /
'use strict' ;
2018-10-14 19:58:54 -03:00
2018-11-12 12:20:44 -02:00
import { CognitiveServicesManagementClient } from 'azure-arm-cognitiveservices' ;
import { CognitiveServicesAccount } from 'azure-arm-cognitiveservices/lib/models' ;
2018-10-14 19:58:54 -03:00
import {
ResourceManagementClient ,
SubscriptionClient
2018-11-12 12:20:44 -02:00
} from 'azure-arm-resource' ;
import { SearchManagementClient } from 'azure-arm-search' ;
import { SqlManagementClient } from 'azure-arm-sql' ;
import { WebSiteManagementClient } from 'azure-arm-website' ;
import { AppServicePlan } from 'azure-arm-website/lib/models' ;
import { GBService , IGBInstance } from 'botlib' ;
import { HttpMethods , ServiceClient , WebResource } from 'ms-rest-js' ;
import { GBDeployer } from 'packages/core.gbapp/services/GBDeployer' ;
import * as simplegit from 'simple-git/promise' ;
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService' ;
import { GBCorePackage } from '../../../packages/core.gbapp' ;
import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService' ;
const Spinner = require ( 'cli-spinner' ) . Spinner ;
const scanf = require ( 'scanf' ) ;
2018-10-14 19:58:54 -03:00
const git = simplegit ( ) ;
2018-11-12 12:20:44 -02:00
const logger = require ( '../../../src/logger' ) ;
const UrlJoin = require ( 'url-join' ) ;
2018-10-15 19:05:43 -03:00
const iconUrl =
2018-11-12 12:20:44 -02:00
'https://github.com/pragmatismo-io/BotServer/blob/master/docs/images/generalbots-logo-squared.png' ;
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 {
2018-11-12 12:20:44 -02:00
public static apiVersion = '2017-12-01' ;
public instance : IGBInstance ;
public resourceClient : ResourceManagementClient.ResourceManagementClient ;
public webSiteClient : WebSiteManagementClient ;
public storageClient : SqlManagementClient ;
public cognitiveClient : CognitiveServicesManagementClient ;
public searchClient : SearchManagementClient ;
public provider = 'Microsoft.BotService' ;
public subscriptionClient : SubscriptionClient.SubscriptionClient ;
public accessToken : string ;
public location : string ;
2018-10-22 15:33:23 -03:00
public subscriptionId : string ;
2018-11-12 12:20:44 -02:00
public farmName : any ;
public deployer : GBDeployer ;
2018-10-14 19:58:54 -03:00
2018-11-12 12:20:44 -02:00
constructor ( deployer : GBDeployer ) {
2018-11-04 17:26:29 -02:00
super ( ) ;
this . deployer = deployer ;
}
2018-10-15 19:05:43 -03:00
public static async getSubscriptions ( credentials ) {
2018-11-12 12:20:44 -02:00
const subscriptionClient = new SubscriptionClient . default ( credentials ) ;
2018-10-15 19:05:43 -03:00
return subscriptionClient . subscriptions . list ( ) ;
2018-10-14 19:58:54 -03:00
}
2018-11-12 12:20:44 -02:00
public 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-25 18:13:51 -03:00
public async deployFarm ( proxyAddress : string ) : Promise < IGBInstance > {
2018-11-12 12:20:44 -02:00
const 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
2018-11-12 12:20:44 -02:00
const spinner = new Spinner ( '%s' ) ;
2018-10-24 12:06:47 -03:00
spinner . start ( ) ;
2018-11-12 12:20:44 -02:00
spinner . setSpinnerString ( '|/-\\' ) ;
2018-10-15 21:03:17 -03:00
2018-10-22 15:33:23 -03:00
let keys : any ;
2018-11-12 12:20:44 -02:00
const 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-10-15 21:03:17 -03:00
logger . info ( ` Deploying Bot Server... ` ) ;
2018-11-12 12:20:44 -02:00
const serverFarm = await this . createHostingPlan (
2018-10-14 19:58:54 -03:00
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-12 12:20:44 -02:00
const administratorLogin = ` sa ${ GBAdminService . getRndReadableIdentifier ( ) } ` ;
const administratorPassword = GBAdminService . getRndPassword ( ) ;
const storageServer = ` ${ name } -storage-server ` ;
const 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-11-12 12:20:44 -02: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... ` ) ;
2018-11-12 12:20:44 -02:00
const searchName = ` ${ name } -search ` ;
2018-10-25 18:13:51 -03:00
await this . createSearch ( name , searchName , instance . cloudLocation ) ;
2018-11-12 12:20:44 -02:00
const searchKeys = await this . searchClient . adminKeys . get (
2018-10-22 15:33:23 -03:00
name ,
searchName
) ;
2018-10-15 21:03:17 -03:00
instance . searchHost = ` ${ searchName } .search.windows.net ` ;
2018-11-12 12:20:44 -02:00
instance . searchIndex = 'azuresql-index' ;
instance . searchIndexer = 'azuresql-indexer' ;
2018-11-04 17:26:29 -02:00
instance . searchKey = searchKeys . primaryKey ;
this . deployer . rebuildIndex ( instance ) ;
2018-10-14 19:58:54 -03:00
2018-10-15 21:03:17 -03:00
logger . info ( ` Deploying Speech... ` ) ;
2018-11-12 12:20:44 -02:00
const speech = await this . createSpeech (
2018-10-25 18:13:51 -03:00
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-11-12 12:20:44 -02:00
const spellChecker = await this . createSpellChecker (
2018-10-14 19:58:54 -03:00
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-11-12 12:20:44 -02:00
const textAnalytics = await this . createTextAnalytics (
2018-10-14 19:58:54 -03:00
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... ` ) ;
2018-11-12 12:20:44 -02:00
const nlp = await this . createNLP ( name , ` ${ name } -nlp ` , instance . cloudLocation ) ;
2018-10-25 18:13:51 -03:00
keys = await this . cognitiveClient . accounts . listKeys ( name , nlp . name ) ;
2018-11-12 12:20:44 -02:00
const nlpAppId = await this . createLUISApp (
2018-10-25 18:13:51 -03:00
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 ) {
2018-11-12 12:20:44 -02:00
const username = GBConfigService . get ( 'CLOUD_USERNAME' ) ;
const password = GBConfigService . get ( 'CLOUD_PASSWORD' ) ;
const subscriptionId = GBConfigService . get ( 'CLOUD_SUBSCRIPTIONID' ) ;
2018-10-25 18:13:51 -03:00
2018-11-12 12:20:44 -02:00
const credentials = await GBAdminService . getADALCredentialsFromUsername (
2018-10-25 18:13:51 -03:00
username ,
password
) ;
2018-11-12 12:20:44 -02:00
const storageClient = new SqlManagementClient ( credentials , subscriptionId ) ;
2018-10-25 18:13:51 -03:00
2018-11-12 12:20:44 -02:00
const ip = await publicIp . v4 ( ) ;
const params = {
2018-10-25 18:13:51 -03:00
startIpAddress : ip ,
endIpAddress : ip
} ;
await storageClient . firewallRules . createOrUpdate (
groupName ,
serverName ,
2018-11-12 12:20:44 -02:00
'gb' ,
2018-10-25 18:13:51 -03:00
params
) ;
}
2018-11-12 12:20:44 -02:00
public async deployBootBot (
instance ,
botId ,
endpoint ,
nlpAppId ,
nlpKey ,
subscriptionId
) {
let appId = GBConfigService . get ( 'MARKETPLACE_ID' ) ;
let appPassword = GBConfigService . get ( 'MARKETPLACE_SECRET' ) ;
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'
) ;
}
const retriveAppId = ( ) = > {
if ( ! appId ) {
process . stdout . write ( 'Generated Application Id (MARKETPLACE_ID):' ) ;
appId = scanf ( '%s' ) . replace ( /(\n|\r)+$/ , '' ) ;
}
} ;
const retriveAppPassword = ( ) = > {
if ( ! appPassword ) {
process . stdout . write ( 'Generated Password (MARKETPLACE_SECRET):' ) ;
appPassword = scanf ( '%s' ) . replace ( /(\n|\r)+$/ , '' ) ;
}
} ;
retriveAppId ( ) ;
retriveAppPassword ( ) ;
await this . internalDeployBot (
instance ,
this . accessToken ,
botId ,
botId ,
botId ,
'General BootBot' ,
endpoint ,
'global' ,
nlpAppId ,
nlpKey ,
appId ,
appPassword ,
subscriptionId
) ;
instance . marketplaceId = appId ;
instance . marketplacePassword = appPassword ;
instance . botId = botId ;
return instance ;
}
public async updateBotProxy ( botId , group , endpoint ) {
const baseUrl = ` https://management.azure.com/ ` ;
const username = GBConfigService . get ( 'CLOUD_USERNAME' ) ;
const password = GBConfigService . get ( 'CLOUD_PASSWORD' ) ;
const subscriptionId = GBConfigService . get ( 'CLOUD_SUBSCRIPTIONID' ) ;
const accessToken = await GBAdminService . getADALTokenFromUsername (
username ,
password
) ;
const httpClient = new ServiceClient ( ) ;
const parameters = {
properties : {
endpoint : endpoint
}
} ;
const query = ` subscriptions/ ${ subscriptionId } /resourceGroups/ ${ group } /providers/ ${
this . provider
} / botServices / $ { botId } ? api - version = $ { AzureDeployerService . apiVersion } ` ;
const url = UrlJoin ( baseUrl , query ) ;
const req = this . createRequestObject (
url ,
accessToken ,
'PATCH' ,
JSON . stringify ( parameters )
) ;
const res = await httpClient . sendRequest ( req ) ;
if ( ! ( res . bodyAsJson as any ) . id ) {
throw res . bodyAsText ;
}
logger . info ( ` Bot proxy updated at: ${ endpoint } . ` ) ;
}
public async deployGeneralBotsToAzure() {
const status = await git . status ( ) ;
// TODO: Copy github to webapp.
}
2018-10-25 18:13:51 -03:00
private async ensureConfiguration ( instance : IGBInstance ) {
2018-11-12 12:20:44 -02:00
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' ) ;
2018-10-25 18:13:51 -03:00
// No .env so asks for cloud credentials to start a new farm.
if ( ! username || ! password || ! subscriptionId || ! location || ! botId ) {
process . stdout . write (
2018-11-12 12:20:44 -02:00
'A empty enviroment is detected. To start automatic deploy, please enter some information:\n'
2018-10-25 18:13:51 -03:00
) ;
}
2018-11-12 12:20:44 -02:00
const retriveUsername = ( ) = > {
2018-10-25 18:13:51 -03:00
if ( ! username ) {
2018-11-04 17:26:29 -02:00
process . stdout . write ( ` ${ GBAdminService . GB_PROMPT } CLOUD_USERNAME: ` ) ;
2018-11-12 12:20:44 -02:00
username = scanf ( '%s' ) . replace ( /(\n|\r)+$/ , '' ) ;
2018-10-25 18:13:51 -03:00
}
} ;
2018-11-12 12:20:44 -02:00
const retrivePassword = ( ) = > {
2018-10-25 18:13:51 -03:00
if ( ! password ) {
2018-11-04 17:26:29 -02:00
process . stdout . write ( ` ${ GBAdminService . GB_PROMPT } CLOUD_PASSWORD: ` ) ;
2018-11-12 12:20:44 -02:00
password = scanf ( '%s' ) . replace ( /(\n|\r)+$/ , '' ) ;
2018-10-25 18:13:51 -03:00
}
} ;
2018-11-12 12:20:44 -02:00
const retrieveBotId = ( ) = > {
2018-10-25 18:13:51 -03:00
if ( ! botId ) {
process . stdout . write (
2018-11-04 17:26:29 -02:00
` ${ GBAdminService . GB_PROMPT } 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
) ;
2018-11-04 17:26:29 -02:00
process . stdout . write ( ` ${ GBAdminService . GB_PROMPT } BOT_ID: ` ) ;
2018-11-12 12:20:44 -02:00
botId = scanf ( '%s' ) . replace ( /(\n|\r)+$/ , '' ) ; // TODO: Update this regexp to match description of it.
2018-10-25 18:13:51 -03:00
}
} ;
2018-11-12 12:20:44 -02:00
let authoringKey = GBConfigService . get ( 'NLP_AUTHORING_KEY' ) ;
const retriveAuthoringKey = ( ) = > {
2018-10-25 18:13:51 -03:00
if ( ! authoringKey ) {
process . stdout . write (
2018-11-04 17:26:29 -02:00
` ${ GBAdminService . GB_PROMPT } Due to this opened issue: https://github.com/Microsoft/botbuilder-tools/issues/550 \ n `
2018-10-25 18:13:51 -03:00
) ;
2018-11-04 17:26:29 -02:00
process . stdout . write ( ` ${ GBAdminService . GB_PROMPT } Please enter your LUIS Authoring Key: ` ) ;
2018-11-12 12:20:44 -02: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-11-12 12:20:44 -02:00
const credentials = await GBAdminService . getADALCredentialsFromUsername (
2018-10-25 18:13:51 -03:00
username ,
password
) ;
if ( ! subscriptionId ) {
2018-11-12 12:20:44 -02:00
const map = { } ;
2018-10-25 18:13:51 -03:00
let index = 1 ;
2018-11-12 12:20:44 -02:00
const list = await AzureDeployerService . getSubscriptions ( credentials ) ;
2018-10-25 18:13:51 -03:00
list . forEach ( element = > {
console . log (
` ${ index } : ${ element . displayName } ( ${ element . subscriptionId } ) `
) ;
map [ index ++ ] = element ;
} ) ;
let subscriptionIndex ;
2018-11-12 12:20:44 -02:00
const retrieveSubscription = ( ) = > {
2018-10-25 18:13:51 -03:00
if ( ! subscriptionIndex ) {
2018-11-12 12:20:44 -02:00
process . stdout . write ( 'CLOUD_SUBSCRIPTIONID (type a number):' ) ;
subscriptionIndex = scanf ( '%d' ) ;
2018-10-25 18:13:51 -03:00
}
} ;
while ( ! subscriptionIndex ) {
retrieveSubscription ( ) ;
}
subscriptionId = map [ subscriptionIndex ] . subscriptionId ;
}
2018-11-12 12:20:44 -02:00
const retriveLocation = ( ) = > {
2018-10-25 18:13:51 -03:00
if ( ! location ) {
2018-11-12 12:20:44 -02:00
process . stdout . write ( 'CLOUD_LOCATION (eg. \'westus\'):' ) ;
location = scanf ( '%s' ) ;
2018-10-25 18:13:51 -03:00
}
} ;
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-14 19:58:54 -03:00
private async createStorageServer (
group ,
name ,
administratorLogin ,
administratorPassword ,
serverName ,
location
) {
2018-11-12 12:20:44 -02:00
const params = {
2018-10-14 19:58:54 -03:00
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 ) {
2018-11-12 12:20:44 -02:00
const query = ` subscriptions/ ${ subscriptionId } /providers/ ${
2018-10-14 19:58:54 -03:00
this . provider
} / register ? api - version = 2018 - 02 - 01 ` ;
2018-11-12 12:20:44 -02:00
const requestUrl = UrlJoin ( baseUrl , query ) ;
2018-10-14 19:58:54 -03:00
2018-11-12 12:20:44 -02:00
const req = new WebResource ( ) ;
req . method = 'POST' ;
2018-10-14 19:58:54 -03:00
req . url = requestUrl ;
req . headers = { } ;
2018-11-12 12:20:44 -02:00
req . headers [ 'Content-Type' ] = 'application/json; charset=utf-8' ;
req . headers [ 'accept-language' ] = '*' ;
req . headers . Authorization = 'Bearer ' + accessToken ;
2018-10-14 19:58:54 -03:00
2018-11-12 12:20:44 -02:00
const httpClient = new ServiceClient ( ) ;
const 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 ) = > {
2018-11-12 12:20:44 -02:00
const baseUrl = ` https://management.azure.com/ ` ;
2018-10-25 21:57:28 -03:00
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-11-12 12:20:44 -02:00
const parameters = {
2018-10-25 21:57:28 -03:00
location : location ,
sku : {
2018-11-12 12:20:44 -02:00
name : 'F0'
2018-10-25 21:57:28 -03:00
} ,
name : botId ,
2018-11-12 12:20:44 -02:00
kind : 'bot' ,
2018-10-25 21:57:28 -03:00
properties : {
description : description ,
displayName : name ,
endpoint : endpoint ,
iconUrl : iconUrl ,
2018-11-04 09:19:03 -02: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-12 12:20:44 -02: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-11-12 12:20:44 -02:00
const httpClient = new ServiceClient ( ) ;
2018-10-25 21:57:28 -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 ,
2018-11-12 12:20:44 -02:00
'PUT' ,
2018-10-25 21:57:28 -03:00
JSON . stringify ( parameters )
) ;
2018-11-12 12:20:44 -02:00
const 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-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-12 12:20:44 -02:00
'GET' ,
2018-10-28 21:56:51 -03:00
JSON . stringify ( parameters )
) ;
2018-11-12 12:20:44 -02:00
const resChannel = await httpClient . sendRequest ( req ) ;
const key = ( resChannel . bodyAsJson as any ) . properties . properties
2018-10-28 21:56:51 -03:00
. sites [ 0 ] . key ;
instance . webchatKey = key ;
resolve ( instance ) ;
} catch ( error ) {
reject ( error ) ;
}
2018-11-12 12:20:44 -02:00
} , 20000 ) ;
2018-10-28 21:56:51 -03:00
} ) ;
}
private createRequestObject (
url : string ,
accessToken : string ,
verb : HttpMethods ,
body : string
) {
2018-11-12 12:20:44 -02:00
const 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 = { } ;
2018-11-12 12:20:44 -02:00
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
) {
2018-11-12 12:20:44 -02:00
const parameters = {
2018-10-22 15:33:23 -03:00
name : name ,
description : description ,
culture : culture
} ;
2018-10-24 12:06:47 -03:00
2018-11-12 12:20:44 -02:00
const body = JSON . stringify ( parameters ) ;
const apps = await this . makeNlpRequest (
2018-10-25 18:13:51 -03:00
location ,
authoringKey ,
null ,
2018-11-12 12:20:44 -02:00
'GET' ,
'apps'
2018-10-25 18:13:51 -03:00
) ;
2018-11-12 12:20:44 -02:00
const app = ( apps . bodyAsJson as any ) . filter ( x = > x . name == name ) [ 0 ] ;
2018-10-25 18:13:51 -03:00
let id : string ;
if ( ! app ) {
2018-11-12 12:20:44 -02:00
const res = await this . makeNlpRequest (
2018-10-25 18:13:51 -03:00
location ,
authoringKey ,
body ,
2018-11-12 12:20:44 -02:00
'POST' ,
'apps'
2018-10-25 18:13:51 -03:00
) ;
id = res . bodyAsText ;
} else {
id = app . id ;
}
return id ;
}
private async makeNlpRequest (
location : string ,
authoringKey : string ,
body : string ,
method : HttpMethods ,
resource : string
) {
2018-11-12 12:20:44 -02:00
const req = new WebResource ( ) ;
2018-10-25 18:13:51 -03:00
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 = { } ;
2018-11-12 12:20:44 -02:00
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-11-12 12:20:44 -02:00
const httpClient = new ServiceClient ( ) ;
const 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 ) {
2018-11-12 12:20:44 -02:00
const params = {
sku : { name : 'free' } ,
2018-10-14 19:58:54 -03:00
location : location
} ;
return this . searchClient . services . createOrUpdate ( group , name , params ) ;
}
private async createStorage ( group , serverName , name , location ) {
2018-11-12 12:20:44 -02:00
const params = {
sku : { name : 'Free' } ,
createMode : 'Default' ,
2018-10-14 19:58:54 -03:00
location : location
} ;
return this . storageClient . databases . createOrUpdate (
group ,
serverName ,
name ,
params
) ;
}
private async createCognitiveServices (
group ,
name ,
location ,
kind
) : Promise < CognitiveServicesAccount > {
2018-11-12 12:20:44 -02:00
const params = {
sku : { name : 'F0' } ,
createMode : 'Default' ,
2018-10-14 19:58:54 -03:00
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 ,
2018-11-12 12:20:44 -02:00
'SpeechServices'
2018-10-14 19:58:54 -03:00
) ;
}
private async createNLP (
group ,
name ,
location
) : Promise < CognitiveServicesAccount > {
2018-11-12 12:20:44 -02:00
return await this . createCognitiveServices ( group , name , location , 'LUIS' ) ;
2018-10-14 19:58:54 -03:00
}
private async createSpellChecker (
group ,
name ,
location
) : Promise < CognitiveServicesAccount > {
return await this . createCognitiveServices (
group ,
name ,
2018-11-12 12:20:44 -02:00
'global' ,
'Bing.SpellCheck.v7'
2018-10-14 19:58:54 -03:00
) ;
}
private async createTextAnalytics (
group ,
name ,
location
) : Promise < CognitiveServicesAccount > {
return await this . createCognitiveServices (
group ,
name ,
location ,
2018-11-12 12:20:44 -02:00
'TextAnalytics'
2018-10-14 19:58:54 -03:00
) ;
}
2018-10-15 21:03:17 -03:00
private async createDeployGroup ( name , location ) {
2018-11-12 12:20:44 -02:00
const params = { location : location } ;
2018-10-14 19:58:54 -03:00
return this . resourceClient . resourceGroups . createOrUpdate ( name , params ) ;
}
private async createHostingPlan (
group ,
name ,
location
) : Promise < AppServicePlan > {
2018-11-12 12:20:44 -02:00
const params = {
2018-10-14 19:58:54 -03:00
serverFarmWithRichSkuName : name ,
location : location ,
sku : {
2018-11-12 12:20:44 -02:00
name : 'F1' ,
2018-10-14 19:58:54 -03:00
capacity : 1 ,
2018-11-12 12:20:44 -02:00
tier : 'Free'
2018-10-14 19:58:54 -03:00
}
} ;
return this . webSiteClient . appServicePlans . createOrUpdate (
group ,
name ,
params
) ;
}
private async createServer ( farmId , group , name , location ) {
2018-11-12 12:20:44 -02:00
const parameters = {
2018-10-14 19:58:54 -03:00
location : location ,
serverFarmId : farmId
} ;
return this . webSiteClient . webApps . createOrUpdate ( group , name , parameters ) ;
}
private async updateWebisteConfig ( group , serverFarmId , name , location ) {
2018-11-12 12:20:44 -02:00
const siteConfig = {
2018-10-14 19:58:54 -03:00
location : location ,
serverFarmId : serverFarmId ,
numberOfWorkers : 1 ,
2018-11-12 12:20:44 -02:00
phpVersion : '5.5'
2018-10-14 19:58:54 -03:00
} ;
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
) ;
}
}