44 */
55
66import { CLIError , Command , flags } from '@microsoft/bf-cli-command'
7+ import { processFlags } from '../../utils/qnamakerbase'
8+
79const path = require ( 'path' )
10+ const fs = require ( 'fs-extra' )
811const username = require ( 'username' )
912const exception = require ( '@microsoft/bf-lu/lib/parser/utils/exception' )
1013const file = require ( '@microsoft/bf-lu/lib/utils/filehelper' )
@@ -27,59 +30,89 @@ export default class QnamakerBuild extends Command {
2730 static flags : any = {
2831 help : flags . help ( { char : 'h' } ) ,
2932 in : flags . string ( { char : 'i' , description : 'Source .qna file or folder' } ) ,
30- subscriptionKey : flags . string ( { char : 's' , description : 'QnA maker subscription key' , required : true } ) ,
31- botName : flags . string ( { char : 'b' , description : 'Bot name' , required : true } ) ,
33+ subscriptionKey : flags . string ( { char : 's' , description : 'QnA maker subscription key' } ) ,
34+ botName : flags . string ( { char : 'b' , description : 'Bot name' } ) ,
3235 region : flags . string ( { description : 'Overrides public endpoint https://<region>.api.cognitive.microsoft.com/qnamaker/v4.0/' , default : 'westus' } ) ,
3336 out : flags . string ( { char : 'o' , description : 'Output folder name to write out .dialog files. If not specified, knowledge base ids will be output to console' } ) ,
3437 defaultCulture : flags . string ( { description : 'Culture code for the content. Infer from .qna if available. Defaults to en-us if not set' } ) ,
3538 fallbackLocale : flags . string ( { description : 'Locale to be used at the fallback if no locale specific recognizer is found. Only valid if --out is set' } ) ,
3639 suffix : flags . string ( { description : 'Environment name as a suffix identifier to include in qnamaker kb name. Defaults to current logged in user alias' } ) ,
3740 dialog : flags . string ( { description : 'Dialog recognizer type [multiLanguage|crosstrained]' , default : 'multiLanguage' } ) ,
3841 force : flags . boolean ( { char : 'f' , description : 'If --out flag is provided, overwrites relevant dialog file' , default : false } ) ,
42+ qnaConfig : flags . string ( { description : 'Path to config for qna build which can contain switches for arguments' } ) ,
3943 log : flags . boolean ( { description : 'write out log messages to console' , default : false } ) ,
4044 }
4145
4246 async run ( ) {
4347 try {
4448 const { flags} : any = this . parse ( QnamakerBuild )
4549
50+ // Luconfig overrides flags
51+ let files : string [ ] = [ ]
52+ if ( flags . qnaConfig ) {
53+ const configFilePath = path . resolve ( flags . qnaConfig )
54+ if ( await fs . exists ( configFilePath ) ) {
55+ const configObj = JSON . parse ( await file . getContentFromFile ( configFilePath ) )
56+ for ( let prop of Object . keys ( configObj ) ) {
57+ if ( prop === 'models' ) {
58+ files = configObj . models . map ( ( m : string ) => path . isAbsolute ( m ) ? m : path . join ( path . dirname ( configFilePath ) , m ) )
59+ } else if ( prop === 'out' ) {
60+ flags . out = path . isAbsolute ( configObj . out ) ? configObj . out : path . join ( path . dirname ( configFilePath ) , configObj . out )
61+ } else {
62+ flags [ prop ] = configObj [ prop ]
63+ }
64+ }
65+ }
66+ }
67+
68+ // Flags override userConfig
69+ let qnamakerBuildFlags = Object . keys ( QnamakerBuild . flags )
70+ qnamakerBuildFlags . push ( 'endpoint' )
71+
72+ let { inVal, subscriptionKey, botName, region, out, defaultCulture, fallbackLocale, suffix, dialog, force, log, endpoint}
73+ = await processFlags ( flags , qnamakerBuildFlags , this . config . configDir )
74+
4675 flags . stdin = await this . readStdin ( )
4776
48- if ( ! flags . stdin && ! flags . in ) {
77+ if ( ! flags . stdin && ! inVal && files . length === 0 ) {
4978 throw new CLIError ( 'Missing input. Please use stdin or pass a file or folder location with --in flag' )
5079 }
5180
52- if ( flags . dialog && flags . dialog !== recognizerType . MULTILANGUAGE && flags . dialog !== recognizerType . CROSSTRAINED ) {
81+ if ( ! subscriptionKey ) {
82+ throw new CLIError ( 'Missing qnamaker subscription key. Please pass subscription key with --subscriptionKey flag or specify via bf config:set:qnamaker --subscriptionKey.' )
83+ }
84+
85+ if ( ! botName ) {
86+ throw new CLIError ( 'Missing bot name. Please pass bot name with --botName flag or specify via --qnaConfig.' )
87+ }
88+
89+ if ( dialog && dialog !== recognizerType . MULTILANGUAGE && dialog !== recognizerType . CROSSTRAINED ) {
5390 throw new CLIError ( 'Recognizer type specified by --dialog is not right. Please specify [multiLanguage|crosstrained]' )
5491 }
5592
56- flags . defaultCulture = flags . defaultCulture && flags . defaultCulture !== '' ? flags . defaultCulture : 'en-us'
57- flags . region = flags . region && flags . region !== '' ? flags . region : 'westus'
58- flags . suffix = flags . suffix && flags . suffix !== '' ? flags . suffix : await username ( ) || 'development'
59- flags . fallbackLocale = flags . fallbackLocale && flags . fallbackLocale !== '' ? flags . fallbackLocale : 'en-us'
93+ defaultCulture = defaultCulture && defaultCulture !== '' ? defaultCulture : 'en-us'
94+ region = region && region !== '' ? region : 'westus'
95+ suffix = suffix && suffix !== '' ? suffix : await username ( ) || 'development'
96+ fallbackLocale = fallbackLocale && fallbackLocale !== '' ? fallbackLocale : 'en-us'
6097
61- const endpoint = `https://${ flags . region } .api.cognitive.microsoft.com/qnamaker/v4.0`
98+ endpoint = endpoint && endpoint !== '' ? endpoint : `https://${ region } .api.cognitive.microsoft.com/qnamaker/v4.0`
6299
63100 // create builder class
64101 const builder = new Builder ( ( input : string ) => {
65- if ( flags . log ) this . log ( input )
102+ if ( log ) this . log ( input )
66103 } )
67104
68105 let qnaContents : any [ ] = [ ]
69106 let recognizers = new Map < string , any > ( )
70107 let multiRecognizer : any
71108 let settings : any
72-
73- const dialogFilePath = ( flags . stdin || ! flags . in ) ? process . cwd ( ) : flags . in . endsWith ( fileExtEnum . QnAFile ) ? path . dirname ( path . resolve ( flags . in ) ) : path . resolve ( flags . in )
74109
75- let files : string [ ] = [ ]
76-
77- if ( flags . in && flags . in !== '' ) {
78- if ( flags . log ) this . log ( 'Loading files...\n' )
110+ if ( ( inVal && inVal !== '' ) || files . length > 0 ) {
111+ if ( log ) this . log ( 'Loading files...\n' )
79112
80113 // get qna files from flags.in.
81- if ( flags . in && flags . in !== '' ) {
82- const qnaFiles : string [ ] = await file . getLuFiles ( flags . in , true , fileExtEnum . QnAFile )
114+ if ( inVal && inVal !== '' ) {
115+ const qnaFiles : string [ ] = await file . getLuFiles ( inVal , true , fileExtEnum . QnAFile )
83116 files . push ( ...qnaFiles )
84117 }
85118
@@ -88,37 +121,37 @@ export default class QnamakerBuild extends Command {
88121
89122 // load qna contents from qna files
90123 // load existing recognizers, multiRecogniers and settings or create default ones
91- const loadedResources = await builder . loadContents ( files , dialogFilePath , flags . botName , flags . suffix , flags . region , flags . defaultCulture )
124+ const loadedResources = await builder . loadContents ( files , botName , suffix , region , defaultCulture )
92125 qnaContents = loadedResources . qnaContents
93126 recognizers = loadedResources . recognizers
94127 multiRecognizer = loadedResources . multiRecognizer
95128 settings = loadedResources . settings
96129 } else {
97130 // load qna content from stdin and create default recognizer, multiRecognier and settings
98- if ( flags . log ) this . log ( 'Load qna content from stdin\n' )
99- const content = new Content ( flags . stdin , new qnaOptions ( flags . botName , true , flags . defaultCulture , path . join ( process . cwd ( ) , 'stdin' ) ) )
131+ if ( log ) this . log ( 'Load qna content from stdin\n' )
132+ const content = new Content ( flags . stdin , new qnaOptions ( botName , true , defaultCulture , path . join ( process . cwd ( ) , 'stdin' ) ) )
100133 qnaContents . push ( content )
101- multiRecognizer = new MultiLanguageRecognizer ( path . join ( process . cwd ( ) , `${ flags . botName } .qna.dialog` ) , { } )
102- settings = new Settings ( path . join ( process . cwd ( ) , `qnamaker.settings.${ flags . suffix } .${ flags . region } .json` ) , { } )
134+ multiRecognizer = new MultiLanguageRecognizer ( path . join ( process . cwd ( ) , `${ botName } .qna.dialog` ) , { } )
135+ settings = new Settings ( path . join ( process . cwd ( ) , `qnamaker.settings.${ suffix } .${ region } .json` ) , { } )
103136 const recognizer = Recognizer . load ( content . path , content . name , path . join ( process . cwd ( ) , `${ content . name } .dialog` ) , settings , { } )
104137 recognizers . set ( content . name , recognizer )
105138 }
106139
107140 // update or create and then publish qnamaker kb based on loaded resources
108- if ( flags . log ) this . log ( 'Handling qnamaker knowledge bases...' )
109- const dialogContents = await builder . build ( qnaContents , recognizers , flags . subscriptionKey , endpoint , flags . botName , flags . suffix , flags . fallbackLocale , multiRecognizer , settings )
141+ if ( log ) this . log ( 'Handling qnamaker knowledge bases...' )
142+ const dialogContents = await builder . build ( qnaContents , recognizers , subscriptionKey , endpoint , botName , suffix , fallbackLocale , multiRecognizer , settings )
110143
111144 // get endpointKeys
112- const endpointKeysInfo = await builder . getEndpointKeys ( flags . subscriptionKey , endpoint )
145+ const endpointKeysInfo = await builder . getEndpointKeys ( subscriptionKey , endpoint )
113146 const endpointKeys : any = {
114147 "primaryEndpointKey" : endpointKeysInfo . primaryEndpointKey ,
115148 "secondaryEndpointKey" : endpointKeysInfo . secondaryEndpointKey
116149 }
117150
118151 // write dialog assets based on config
119- if ( flags . out ) {
120- const outputFolder = path . resolve ( flags . out )
121- const writeDone = await builder . writeDialogAssets ( dialogContents , flags . force , outputFolder , flags . dialog , files )
152+ if ( out ) {
153+ const outputFolder = path . resolve ( out )
154+ const writeDone = await builder . writeDialogAssets ( dialogContents , force , outputFolder , dialog , files )
122155 if ( writeDone ) {
123156 this . log ( `Successfully wrote .dialog files to ${ outputFolder } \n` )
124157 this . log ( 'QnA knowledge base endpointKeys:' )
0 commit comments