Skip to content
This repository was archived by the owner on Jan 15, 2025. It is now read-only.

Commit 7e5e1b8

Browse files
authored
decouple config validation from parseFile (#1002)
* add validate function * reactor to sync * fix resource is changed in memory issue
1 parent 780bf2c commit 7e5e1b8

File tree

4 files changed

+201
-112
lines changed

4 files changed

+201
-112
lines changed

packages/lu/src/parser/composerindex.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
module.exports = {
22
parser: {
33
parseFile: require('./lufile/parseFileContents').parseFile,
4-
validateLUISBlob: require('./luis/luisValidator')
4+
validateLUISBlob: require('./luis/luisValidator'),
5+
validateResource: require('./lufile/parseFileContents').validateResource
56
},
67
sectionHandler: {
78
luParser: require('./lufile/luParser'),

packages/lu/src/parser/lufile/parseFileContents.js

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ const fetch = require('node-fetch');
2121
const qnaFile = require('./../qna/qnamaker/qnaFiles');
2222
const fileToParse = require('./classes/filesToParse');
2323
const luParser = require('./luParser');
24-
const DiagnosticSeverity = require('./diagnostic').DiagnosticSeverity;
25-
const BuildDiagnostic = require('./diagnostic').BuildDiagnostic;
24+
const {BuildDiagnostic, DiagnosticSeverity} = require('./diagnostic');
2625
const EntityTypeEnum = require('./../utils/enums/luisEntityTypes');
2726
const luisEntityTypeMap = require('./../utils/enums/luisEntityTypeNameMap');
2827
const qnaContext = require('../qna/qnamaker/qnaContext');
2928
const qnaPrompt = require('../qna/qnamaker/qnaPrompt');
30-
const { config } = require('process');
29+
const LUResource = require('./luResource');
30+
3131
const plAllowedTypes = ["composite", "ml"];
3232
const featureTypeEnum = {
3333
featureToModel: 'modelName',
@@ -91,6 +91,65 @@ const parseFileContentsModule = {
9191

9292
return parsedContent;
9393
},
94+
/**
95+
* Validate resource based on config.
96+
* @param {LUResource} originalResource Original parsed lu or qna resource
97+
* @param {any} config Features config
98+
* @returns {any[]} Diagnostic errors returned
99+
*/
100+
validateResource: function (originalResource, config) {
101+
config = config || {};
102+
config = {...defaultConfig, ...config};
103+
104+
let resource = JSON.parse(JSON.stringify(originalResource));
105+
if (resource.Errors.filter(error => (error && error.Severity && error.Severity === DiagnosticSeverity.ERROR)).length > 0) {
106+
return []
107+
}
108+
109+
let errors = []
110+
111+
try {
112+
let parsedContent = new parserObj();
113+
114+
// parse model info section
115+
let enableMergeIntents = parseAndHandleModelInfoSection(parsedContent, resource, false, config);
116+
117+
// validate reference section
118+
validateImportSection(resource, config);
119+
120+
// parse nested intent section
121+
parseAndHandleNestedIntentSection(resource, enableMergeIntents);
122+
123+
GetEntitySectionsFromSimpleIntentSections(resource);
124+
125+
// parse entity definition v2 section
126+
let featuresToProcess = parseAndHandleEntityV2(parsedContent, resource, false, undefined, config);
127+
128+
// parse entity section
129+
parseAndHandleEntitySection(parsedContent, resource, false, undefined, config);
130+
131+
// parse entity section
132+
parseAndHandleEntitySection(parsedContent, resource, false, undefined, config);
133+
134+
// validate simple intent section
135+
parseAndHandleSimpleIntentSection(parsedContent, resource, config)
136+
137+
if (featuresToProcess && featuresToProcess.length > 0) {
138+
parseFeatureSections(parsedContent, featuresToProcess, config);
139+
}
140+
141+
} catch(e) {
142+
if (e instanceof exception) {
143+
errors.push(...e.diagnostics)
144+
} else {
145+
errors.push(BuildDiagnostic({
146+
message: e.message
147+
}))
148+
}
149+
}
150+
151+
return errors
152+
},
94153
/**
95154
* Helper function to add an item to collection if it does not exist
96155
* @param {object} collection contents of the current collection
@@ -175,7 +234,7 @@ const parseLuAndQnaWithAntlr = async function (parsedContent, fileContent, log,
175234
}
176235

177236
// parse model info section
178-
let enableMergeIntents = parseAndHandleModelInfoSection(parsedContent, luResource, log);
237+
let enableMergeIntents = parseAndHandleModelInfoSection(parsedContent, luResource, log, config);
179238

180239
// parse reference section
181240
await parseAndHandleImportSection(parsedContent, luResource, config);
@@ -192,7 +251,7 @@ const parseLuAndQnaWithAntlr = async function (parsedContent, fileContent, log,
192251
parseAndHandleEntitySection(parsedContent, luResource, log, locale, config);
193252

194253
// parse simple intent section
195-
await parseAndHandleSimpleIntentSection(parsedContent, luResource, config);
254+
parseAndHandleSimpleIntentSection(parsedContent, luResource, config);
196255

197256
// parse qna section
198257
await parseAndHandleQnaSection(parsedContent, luResource);
@@ -715,6 +774,23 @@ const parseAndHandleImportSection = async function (parsedContent, luResource, c
715774
}
716775
}
717776
}
777+
/**
778+
* Reference parser code to parse reference section.
779+
* @param {LUResouce} luResource resources extracted from lu file content
780+
* @throws {exception} Throws on errors. exception object includes errCode and text.
781+
*/
782+
const validateImportSection = function (luResource, config) {
783+
// handle reference
784+
let luImports = luResource.Sections.filter(s => s.SectionType === SectionType.IMPORTSECTION);
785+
if (luImports && luImports.length > 0) {
786+
if (!config.enableExternalReferences) {
787+
const error = BuildDiagnostic({
788+
message: 'Do not support External References. Please make sure enableExternalReferences is set to true.'
789+
});
790+
throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error]));
791+
}
792+
}
793+
}
718794
/**
719795
* Helper function to handle @ reference in patterns
720796
* @param {String} utterance
@@ -800,7 +876,7 @@ const parseAndHandleNestedIntentSection = function (luResource, enableMergeInten
800876
* @param {LUResouce} luResource resources extracted from lu file content
801877
* @throws {exception} Throws on errors. exception object includes errCode and text.
802878
*/
803-
const parseAndHandleSimpleIntentSection = async function (parsedContent, luResource, config) {
879+
const parseAndHandleSimpleIntentSection = function (parsedContent, luResource, config) {
804880
// handle intent
805881
let intents = luResource.Sections.filter(s => s.SectionType === SectionType.SIMPLEINTENTSECTION);
806882
let hashTable = {}
@@ -833,7 +909,7 @@ const parseAndHandleSimpleIntentSection = async function (parsedContent, luResou
833909

834910
utterance = `${utterance.slice(0, index)}(${reference.Path})`
835911
}
836-
let parsedLinkUriInUtterance = await helpers.parseLinkURI(utterance);
912+
let parsedLinkUriInUtterance = helpers.parseLinkURISync(utterance);
837913
// examine and add these to filestoparse list.
838914
parsedContent.additionalFilesToParse.push(new fileToParse(parsedLinkUriInUtterance.fileName, false));
839915
}
@@ -2042,13 +2118,21 @@ const parseAndHandleQnaSection = async function (parsedContent, luResource) {
20422118
* @param {parserObj} Object with that contains list of additional files to parse, parsed LUIS object and parsed QnA object
20432119
* @param {LUResouce} luResource resources extracted from lu file content
20442120
* @param {boolean} log indicates if we need verbose logging.
2121+
* @param {any} config config to indicate which features are enabled
20452122
* @throws {exception} Throws on errors. exception object includes errCode and text.
20462123
*/
2047-
const parseAndHandleModelInfoSection = function (parsedContent, luResource, log) {
2124+
const parseAndHandleModelInfoSection = function (parsedContent, luResource, log, config) {
20482125
// handle model info
20492126
let enableMergeIntents = true;
20502127
let modelInfos = luResource.Sections.filter(s => s.SectionType === SectionType.MODELINFOSECTION);
20512128
if (modelInfos && modelInfos.length > 0) {
2129+
if (!config.enableModelDescription) {
2130+
const error = BuildDiagnostic({
2131+
message: `Do not support Model Description. Please make sure enableModelDescription is set to true.`
2132+
})
2133+
throw (new exception(retCode.errorCode.INVALID_INPUT, error.toString(), [error]));
2134+
}
2135+
20522136
for (const modelInfo of modelInfos) {
20532137
let line = modelInfo.ModelInfo
20542138
let kvPair = line.split(/@(app|kb|intent|entity|enableSections|enableMergeIntents|patternAnyEntity|parser).(.*)=/g).map(item => item.trim());

packages/lu/src/parser/utils/helpers.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,28 @@ const helpers = {
9292
}
9393
return splitReference.groups;
9494
},
95+
/**
96+
* Helper function to parse link URIs in utterances
97+
* @param {String} utterance
98+
* @returns {Object} Object that contains luFile and ref. ref can be Intent-Name or ? or * or **
99+
* @throws {exception} Throws on errors. exception object includes errCode and text.
100+
*/
101+
parseLinkURISync: function (utterance) {
102+
let linkValueList = utterance.trim().match(new RegExp(/\(.*?\)/g));
103+
let linkValue = linkValueList[0].replace('(', '').replace(')', '');
104+
if (linkValue === '') throw (new exception(retCode.errorCode.INVALID_LU_FILE_REF, `[ERROR]: Invalid LU File Ref: "${utterance}"`));
105+
// reference can either be #<Intent-Name> or #? or /*#? or /**#? or #*utterance* or #<Intent-Name>*patterns*
106+
let splitRegExp = new RegExp(/^(?<fileName>.*?)(?<segment>#|\*+)(?<path>.*?)$/gim);
107+
let splitReference = splitRegExp.exec(linkValue);
108+
if (!splitReference) throw (new exception(retCode.errorCode.INVALID_LU_FILE_REF, `[ERROR]: Invalid LU File Ref: "${utterance}".\n Reference needs a qualifier - either a #Intent-Name or #? or *#? or **#? or #*utterances* etc.`));
109+
if (splitReference.groups.segment.includes('*')) {
110+
if (splitReference.groups.path === '') {
111+
throw (new exception(retCode.errorCode.INVALID_LU_FILE_REF, `[ERROR]: Invalid LU File Ref: "${utterance}".\n '*' and '**' can only be used with QnA qualitifier. e.g. *#? and **#?`));
112+
}
113+
splitReference.groups.fileName += '*';
114+
}
115+
return splitReference.groups;
116+
},
95117
/**
96118
* Helper function to do a filter operation based search over an Array
97119
* @param {Array} srcList Object to filter on

0 commit comments

Comments
 (0)