Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Composer/packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@
"@uifabric/icons": "^7.3.4",
"@uifabric/react-hooks": "^7.3.4",
"@uifabric/styling": "^7.10.4",
"adaptive-expressions": "^4.8.0-preview-110700",
"adaptive-expressions": "^4.10.0-preview-133287",
"axios": "^0.19.2",
"botbuilder-lg": "^4.9.0-preview-119754",
"botbuilder-lg": "^4.10.0-preview-133287",
"format-message": "^6.2.3",
"immer": "^5.2.0",
"jwt-decode": "^2.2.0",
Expand Down
17 changes: 17 additions & 0 deletions Composer/packages/lib/indexers/__tests__/lgUtil.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,23 @@ describe('update lg template', () => {
expect(templates[0].name).toEqual('Exit');
expect(templates[0].body).toEqual('-Bye');
});

it('should update lg template with error', () => {
const content = `# Exit
-Thanks for using todo bot.\${

# Greeting
-What's up bro`;

const templates0 = Templates.parseText(content).toArray();
expect(templates0.length).toEqual(2);
const template = { name: 'Exit', parameters: [], body: '-Bye' };
const newContent = updateTemplate(content, 'Exit', template);
const templates = Templates.parseText(newContent).toArray();
expect(templates.length).toEqual(2);
expect(templates[0].name).toEqual('Exit');
expect(templates[0].body).toEqual('-Bye');
});
});

describe('add lg template', () => {
Expand Down
4 changes: 2 additions & 2 deletions Composer/packages/lib/indexers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
"dependencies": {
"@bfc/shared": "*",
"@microsoft/bf-lu": "4.9.0",
"adaptive-expressions": "^4.8.0-preview-110700",
"botbuilder-lg": "^4.9.0-preview-119754",
"adaptive-expressions": "^4.10.0-preview-133287",
"botbuilder-lg": "^4.10.0-preview-133287",
"lodash": "^4.17.15"
}
}
12 changes: 11 additions & 1 deletion Composer/packages/lib/indexers/src/dialogUtils/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ export const ExpressionType = {
integer: 'integer',
boolean: 'boolean',
string: 'string',
array: 'array',
};

const ExpressionTypeMapString = {
[ReturnType.Number]: 'number',
[ReturnType.String]: 'string',
[ReturnType.Boolean]: 'boolean',
[ReturnType.Object]: 'object',
[ReturnType.Array]: 'array',
};

const isExpression = (value: string | boolean | number, types: string[]): boolean => {
Expand All @@ -17,9 +26,10 @@ const isExpression = (value: string | boolean | number, types: string[]): boolea
};

//The return type should match the schema type
//TODO: returnType is number, schem type is string, need map or unify
const checkReturnType = (returnType: ReturnType, types: string[]): string => {
return returnType === ReturnType.Object ||
~types.indexOf(returnType) ||
~types.indexOf(ExpressionTypeMapString[returnType]) ||
(returnType === ReturnType.Number && ~types.indexOf(ExpressionType.integer))
? ''
: formatMessage('the expression type is not match');
Expand Down
255 changes: 0 additions & 255 deletions Composer/packages/server/src/models/bot/botProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,6 @@ export class BotProject {

public init = async () => {
this.diagnostics = [];
// those 2 migrate methods shall be removed after a period of time
await this._reformProjectStructure();
try {
await this._replaceDashInTemplateName();
} catch (_e) {
// when re-index opened bot, file write may error
}
this.settings = await this.getEnvSettings('', false);
const { skillsParsed, diagnostics } = await extractSkillManifestUrl(this.settings?.skill || []);
this.skills = skillsParsed;
Expand Down Expand Up @@ -544,254 +537,6 @@ export class BotProject {
return fileList;
};

/**
* Reform bot project structure
* /[dialog]
[dialog].dialog
/language-generation
/[locale]
[dialog].[locale].lg
/language-understanding
/[locale]
[dialog].[locale].lu
*
*/
private _reformProjectStructure = async () => {
let isOldBotStructure = false;

const BOTNAME = this.name.toLowerCase();
const LOCALE = this.locale;

const TemplateVariables = {
BOTNAME,
LOCALE,
DIALOGNAME: '',
};

const files: { [key: string]: string }[] = [];

// Reform all files according to above defined structure.
const patterns = ['**/*.dialog', '**/*.lg', '**/*.lu', '**/*.schema', '**/*.json'];
for (const pattern of patterns) {
const root = this.dataDir;
const paths = await this.fileStorage.glob(pattern, root);
for (const filePath of paths.sort()) {
const realFilePath: string = Path.join(root, filePath);
if ((await this.fileStorage.stat(realFilePath)).isFile) {
let content: string = await this.fileStorage.readFile(realFilePath);
const name = Path.basename(filePath);

// mark as old bot structure, then will continue do move.
if (name === 'Main.dialog') {
isOldBotStructure = true;
}

// convert file name from camel to lowercase
const fileId = name.split('.')[0].toLowerCase();
let targetRelativePath;
let pathEndPoint = '';
const fileType = Path.extname(filePath);
let dialogName = fileId === 'main' ? BOTNAME : fileId;

// nested dialogs
// e.g foo/bar/bar.dialog
// - > foo/dialogs/bar.dialog
// TODO: need optimize.
const filePathDirs = filePath.replace('ComposerDialogs/', '').split('/');
if (filePathDirs.length > 2) {
dialogName = filePathDirs[filePathDirs.length - 2].toLowerCase();
const parrentDialogName = filePathDirs[filePathDirs.length - 3].toLowerCase();
pathEndPoint = Path.join(pathEndPoint, 'dialogs', parrentDialogName);
}

// wrap path dialogs/[dialogId]
if (fileId !== 'main' && fileId !== 'common') {
pathEndPoint = Path.join(pathEndPoint, BotStructureTemplate.dialogs.folder);
}
// rename Main.* to botname.*
TemplateVariables.DIALOGNAME = dialogName;

if (fileType === '.dialog') {
content = autofixReferInDialog(dialogName, content);

targetRelativePath = templateInterpolate(
Path.join(pathEndPoint, BotStructureTemplate.dialogs.entry),
TemplateVariables
);
} else if (fileType === '.lg') {
if (name === 'common.lg') {
targetRelativePath = templateInterpolate(BotStructureTemplate.common.lg, TemplateVariables);
} else {
targetRelativePath = templateInterpolate(
Path.join(pathEndPoint, BotStructureTemplate.dialogs.lg),
TemplateVariables
);
}
} else if (fileType === '.lu') {
targetRelativePath = templateInterpolate(
Path.join(pathEndPoint, BotStructureTemplate.dialogs.lu),
TemplateVariables
);
} else if (fileType === '.schema') {
targetRelativePath = templateInterpolate(BotStructureTemplate.schema, { FILENAME: name });
} else if (fileType === '.json') {
targetRelativePath = templateInterpolate(BotStructureTemplate.settings, { FILENAME: name });
}

files.push({ targetRelativePath, realFilePath, content });
}
}
}

if (isOldBotStructure === false) {
return;
}

// move files from /coolbot/ComposerDialogs/* to /coolbot/*
const targetBotPath = this.dataDir;
for (const file of files) {
const { targetRelativePath, realFilePath, content } = file;
const absolutePath = Path.join(targetBotPath, targetRelativePath);
await this.fileStorage.removeFile(realFilePath);

try {
const dirPath = Path.dirname(realFilePath);
await this.fileStorage.rmDir(dirPath);
} catch (_error) {
// pass , dir may not empty
}

await this.ensureDirExists(Path.dirname(absolutePath));
await this.fileStorage.writeFile(absolutePath, content);
}
};

private _replaceDashInTemplateName = async () => {
const files: { [key: string]: string }[] = [];
const patterns = ['**/*.dialog', '**/*.lg', '**/*.json'];
const replacers = [
(line: string) => {
return line.replace('bfdactivity-', 'SendActivity_');
},
(line: string) => {
return line.replace('bfdprompt-', 'TextInput_Prompt_');
},
(line: string) => {
return line.replace('bfdinvalidPrompt-', 'TextInput_InvalidPrompt_');
},
(line: string) => {
return line.replace('bfdunrecognizedPrompt-', 'TextInput_UnrecognizedPrompt_');
},
(line: string) => {
return line.replace('bfddefaultValueResponse-', 'TextInput_DefaultValueResponse_');
},
];

for (const pattern of patterns) {
const root = this.dataDir;
const paths = await this.fileStorage.glob(pattern, root);
for (const filePath of paths.sort()) {
let fileChanged = false;
const realFilePath: string = Path.join(root, filePath);
if ((await this.fileStorage.stat(realFilePath)).isFile) {
let content: string = await this.fileStorage.readFile(realFilePath);
const fileType = Path.extname(filePath);
const newContentLines: string[] = [];
if (fileType === '.lg') {
const templateNamePattern = /^\s*#\s*.*/;
const templateBodyLinePattern = /^\s*-.*/;
const lines = content.split('\n');
for (const line of lines) {
// lg name line
if (templateNamePattern.test(line) && line.includes('-')) {
let newLine = line;
replacers.map((replacer) => {
newLine = replacer(newLine);
});
newLine = newLine.replace('-', '_');
newContentLines.push(newLine);
fileChanged = true;

// lg body line
} else if (templateBodyLinePattern.test(line) && (line.includes('@{') || line.includes('${'))) {
let newContentLine = line;
replacers.map((replacer) => {
newContentLine = replacer(newContentLine);
});
newContentLines.push(newContentLine);
fileChanged = true;
} else {
newContentLines.push(line);
}
}

content = newContentLines.join('\n');
}

if (fileType === '.dialog') {
const lines = content.split('\n');
const callingTempaltePattern = /^\s*"[\w]+":\s*"\$\{.*\}"/;
for (const line of lines) {
if (callingTempaltePattern.test(line) && line.includes('-')) {
let newLine = line;
replacers.map((replacer) => {
newLine = replacer(newLine);
});
newContentLines.push(newLine);
fileChanged = true;
} else {
newContentLines.push(line);
}
}

content = newContentLines.join('\n');
}

// card
if (fileType === '.json' && Path.basename(filePath) !== 'appsettings.json') {
const lines = content.split('\n');
const activityInJson = /^\s*"activity":\s*"\[.*\]"/;
for (const line of lines) {
if (activityInJson.test(line) && line.includes('-')) {
let newLine = line;
replacers.map((replacer) => {
newLine = replacer(newLine);
});
newLine = newLine.replace('-', '_');

newContentLines.push(newLine);
fileChanged = true;
} else {
newContentLines.push(line);
}
}

content = newContentLines.join('\n');
}

if (fileChanged) {
files.push({ realFilePath, content });
}
}
}
}

for (const file of files) {
const { realFilePath, content } = file;
await this.fileStorage.removeFile(realFilePath);

try {
const dirPath = Path.dirname(realFilePath);
await this.fileStorage.rmDir(dirPath);
} catch (_error) {
// pass , dir may not empty
}

await this.ensureDirExists(Path.dirname(realFilePath));
await this.fileStorage.writeFile(realFilePath, content);
}
};

private _getSchemas = async (): Promise<FileInfo[]> => {
if (!(await this.exists())) {
throw new Error(`${this.dir} is not a valid path`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
},
"dependencies": {
"@bfc/indexers": "*",
"botbuilder-lg": "^4.9.0-preview-119754",
"botbuilder-lg": "^4.10.0-preview-133287",
"request-light": "^0.2.2",
"vscode-languageserver": "^5.3.0-next"
},
Expand Down
Loading