From 49ec82df2d902602a8d78e4e6b3eabc2fde72f9d Mon Sep 17 00:00:00 2001 From: zhixzhan Date: Mon, 27 Apr 2020 19:36:24 +0800 Subject: [PATCH 1/7] use cached projects --- Composer/packages/server/src/services/project.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Composer/packages/server/src/services/project.ts b/Composer/packages/server/src/services/project.ts index 0576609c6c..34f901b942 100644 --- a/Composer/packages/server/src/services/project.ts +++ b/Composer/packages/server/src/services/project.ts @@ -183,6 +183,10 @@ export class BotProjectService { public static getProjectById = async (projectId: string, user?: UserIdentity) => { BotProjectService.initialize(); + // use indexed project + const indexedCurrentProject = BotProjectService.currentBotProjects.find(({ id }) => id === projectId); + if (indexedCurrentProject) return indexedCurrentProject; + if (!BotProjectService.projectLocationMap?.[projectId]) { throw new Error('project not found in cache'); } else { From ca3ebd85f7aaef03604502bfa70845a08da06a76 Mon Sep 17 00:00:00 2001 From: zhixzhan Date: Tue, 28 Apr 2020 14:26:51 +0800 Subject: [PATCH 2/7] don't need to call init after project loaded --- .../server/src/controllers/project.ts | 4 ---- .../packages/server/src/services/project.ts | 24 +++++++++++++------ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Composer/packages/server/src/controllers/project.ts b/Composer/packages/server/src/controllers/project.ts index 1e7fa8a2a1..35552378d3 100644 --- a/Composer/packages/server/src/controllers/project.ts +++ b/Composer/packages/server/src/controllers/project.ts @@ -50,7 +50,6 @@ async function createProject(req: Request, res: Response) { const currentProject = await BotProjectService.getProjectById(id, user); if (currentProject !== undefined) { await currentProject.updateBotInfo(name, description); - await currentProject.init(); const project = currentProject.getProject(); log('Project created successfully.'); res.status(200).json({ @@ -72,7 +71,6 @@ async function getProjectById(req: Request, res: Response) { const currentProject = await BotProjectService.getProjectById(projectId, user); if (currentProject !== undefined && (await currentProject.exists())) { - await currentProject.init(); const project = currentProject.getProject(); res.status(200).json({ id: projectId, @@ -109,7 +107,6 @@ async function openProject(req: Request, res: Response) { const id = await BotProjectService.openProject(location, user); const currentProject = await BotProjectService.getProjectById(id, user); if (currentProject !== undefined) { - await currentProject.init(); const project = currentProject.getProject(); res.status(200).json({ id: currentProject.id, @@ -151,7 +148,6 @@ async function saveProjectAs(req: Request, res: Response) { const currentProject = await BotProjectService.getProjectById(id, user); if (currentProject !== undefined) { await currentProject.updateBotInfo(name, description); - await currentProject.init(); const project = currentProject.getProject(); res.status(200).json({ id, diff --git a/Composer/packages/server/src/services/project.ts b/Composer/packages/server/src/services/project.ts index 34f901b942..5af12caa27 100644 --- a/Composer/packages/server/src/services/project.ts +++ b/Composer/packages/server/src/services/project.ts @@ -4,6 +4,7 @@ import merge from 'lodash/merge'; import find from 'lodash/find'; import { importResolverGenerator, ResolverResource } from '@bfc/shared'; +import extractMemoryPaths from '@bfc/indexers/lib/dialogUtils/extractMemoryPaths'; import { UserIdentity } from '@bfc/plugin-loader'; import { BotProject } from '../models/bot/botProject'; @@ -35,7 +36,7 @@ export class BotProjectService { public static lgImportResolver(source: string, id: string, projectId: string): ResolverResource { BotProjectService.initialize(); - const project = BotProjectService.currentBotProjects.find(({ id }) => id === projectId); + const project = BotProjectService.getIndexedProjectById(projectId); if (!project) throw new Error('project not found'); const resource = project.files.reduce((result: ResolverResource[], file) => { const { name, content } = file; @@ -50,7 +51,7 @@ export class BotProjectService { public static luImportResolver(source: string, id: string, projectId: string): ResolverResource { BotProjectService.initialize(); - const project = BotProjectService.currentBotProjects.find(({ id }) => id === projectId); + const project = BotProjectService.getIndexedProjectById(projectId); if (!project) throw new Error('project not found'); const resource = project.files.reduce((result: ResolverResource[], file) => { const { name, content } = file; @@ -88,9 +89,13 @@ export class BotProjectService { 'turn.repeatedIds', 'turn.activityProcessed', ]; + const projectVariables = BotProjectService.getIndexedProjectById(projectId) + ?.files.filter(file => file.name.endsWith('.dialog')) + .map(({ content }) => extractMemoryPaths(content)); + const userDefined: string[] = - BotProjectService.currentBotProjects[projectId]?.dialogs.reduce((result: string[], dialog) => { - result = [...dialog.userDefinedVariables, ...result]; + projectVariables?.reduce((result: string[], variables) => { + result = [...variables, ...result]; return result; }, []) || []; return [...defaultProperties, ...userDefined]; @@ -180,12 +185,17 @@ export class BotProjectService { Store.set('projectLocationMap', BotProjectService.projectLocationMap); }; - public static getProjectById = async (projectId: string, user?: UserIdentity) => { - BotProjectService.initialize(); - + public static getIndexedProjectById(projectId): BotProject | undefined { // use indexed project const indexedCurrentProject = BotProjectService.currentBotProjects.find(({ id }) => id === projectId); if (indexedCurrentProject) return indexedCurrentProject; + } + + public static getProjectById = async (projectId: string, user?: UserIdentity): Promise => { + BotProjectService.initialize(); + + const cachedProject = BotProjectService.getIndexedProjectById(projectId); + if (cachedProject) return cachedProject; if (!BotProjectService.projectLocationMap?.[projectId]) { throw new Error('project not found in cache'); From ca522fa37bd09d8f31ec6b82536f9370d9096f4a Mon Sep 17 00:00:00 2001 From: zhixzhan Date: Tue, 28 Apr 2020 14:59:41 +0800 Subject: [PATCH 3/7] use a real-time refreshed files list, fix concurrency deletes --- Composer/packages/server/src/models/bot/botProject.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Composer/packages/server/src/models/bot/botProject.ts b/Composer/packages/server/src/models/bot/botProject.ts index 2a532b0c36..5ae08cb944 100644 --- a/Composer/packages/server/src/models/bot/botProject.ts +++ b/Composer/packages/server/src/models/bot/botProject.ts @@ -230,7 +230,7 @@ export class BotProject { }; public deleteFile = async (name: string) => { - if (Path.resolve(name) === 'Main') { + if (Path.basename(name, '.dialog') === this.name) { throw new Error(`Main dialog can't be removed`); } @@ -313,7 +313,7 @@ export class BotProject { try { await this.fileStorage.rmDir(folderPath); } catch (e) { - console.log(e); + // pass } } }; @@ -383,6 +383,8 @@ export class BotProject { if (index === -1) { throw new Error(`no such file at ${relativePath}`); } + this.files[index].content = content; + const absolutePath = `${this.dir}/${relativePath}`; // only write if the file has actually changed @@ -394,8 +396,6 @@ export class BotProject { // instead of calling stat again which could be expensive const stats = await this.fileStorage.stat(absolutePath); - this.files[index].content = content; - return stats.lastModified; }; @@ -406,10 +406,10 @@ export class BotProject { if (index === -1) { throw new Error(`no such file at ${relativePath}`); } + this.files.splice(index, 1); const absolutePath = `${this.dir}/${relativePath}`; await this.fileStorage.removeFile(absolutePath); - this.files.splice(index, 1); }; // ensure dir exist, dir is a absolute dir path From b108608c3e3a9a7429d5bac8a2282556e7a123ec Mon Sep 17 00:00:00 2001 From: zhixzhan Date: Tue, 28 Apr 2020 15:28:52 +0800 Subject: [PATCH 4/7] update test --- .../packages/server/__tests__/controllers/project.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Composer/packages/server/__tests__/controllers/project.test.ts b/Composer/packages/server/__tests__/controllers/project.test.ts index def2c60f4b..c85e08af94 100644 --- a/Composer/packages/server/__tests__/controllers/project.test.ts +++ b/Composer/packages/server/__tests__/controllers/project.test.ts @@ -159,7 +159,7 @@ describe('dialog operation', () => { const mockReq = { params: { projectId }, query: {}, - body: { name: 'test.dialog', content: '' }, + body: { name: 'test2.dialog', content: '' }, } as Request; await ProjectController.createFile(mockReq, mockRes); expect(mockRes.status).toHaveBeenCalledWith(200); @@ -167,7 +167,7 @@ describe('dialog operation', () => { it('should remove dialog', async () => { const mockReq = { - params: { name: 'test.dialog', projectId }, + params: { name: 'test2.dialog', projectId }, query: {}, body: {}, } as Request; From 06f50202a05e56827f8a76ff03e6d9b83a5ab84b Mon Sep 17 00:00:00 2001 From: zhixzhan Date: Wed, 29 Apr 2020 11:18:24 +0800 Subject: [PATCH 5/7] create/saveAs/open project should call init --- Composer/packages/server/src/controllers/project.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Composer/packages/server/src/controllers/project.ts b/Composer/packages/server/src/controllers/project.ts index 06dbc4f971..046971b4a1 100644 --- a/Composer/packages/server/src/controllers/project.ts +++ b/Composer/packages/server/src/controllers/project.ts @@ -50,6 +50,7 @@ async function createProject(req: Request, res: Response) { const currentProject = await BotProjectService.getProjectById(id, user); if (currentProject !== undefined) { await currentProject.updateBotInfo(name, description); + await currentProject.init(); if (schemaUrl) { await currentProject.saveSchemaToProject(schemaUrl, locationRef.path); } @@ -111,6 +112,7 @@ async function openProject(req: Request, res: Response) { const id = await BotProjectService.openProject(location, user); const currentProject = await BotProjectService.getProjectById(id, user); if (currentProject !== undefined) { + await currentProject.init(); const project = currentProject.getProject(); res.status(200).json({ id: currentProject.id, @@ -152,6 +154,7 @@ async function saveProjectAs(req: Request, res: Response) { const currentProject = await BotProjectService.getProjectById(id, user); if (currentProject !== undefined) { await currentProject.updateBotInfo(name, description); + await currentProject.init(); const project = currentProject.getProject(); res.status(200).json({ id, From ee11d09355259ddfed2fdc351da9d9a639f11e65 Mon Sep 17 00:00:00 2001 From: zhixzhan Date: Wed, 29 Apr 2020 11:24:10 +0800 Subject: [PATCH 6/7] clean up --- Composer/packages/server/src/controllers/project.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/server/src/controllers/project.ts b/Composer/packages/server/src/controllers/project.ts index 046971b4a1..0899ae339b 100644 --- a/Composer/packages/server/src/controllers/project.ts +++ b/Composer/packages/server/src/controllers/project.ts @@ -50,10 +50,10 @@ async function createProject(req: Request, res: Response) { const currentProject = await BotProjectService.getProjectById(id, user); if (currentProject !== undefined) { await currentProject.updateBotInfo(name, description); - await currentProject.init(); if (schemaUrl) { await currentProject.saveSchemaToProject(schemaUrl, locationRef.path); } + await currentProject.init(); const project = currentProject.getProject(); log('Project created successfully.'); From 2e7437403311279e2f8653c8757014360bef40a2 Mon Sep 17 00:00:00 2001 From: zhixzhan Date: Wed, 29 Apr 2020 12:44:36 +0800 Subject: [PATCH 7/7] update --- Composer/packages/server/src/models/bot/botProject.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/server/src/models/bot/botProject.ts b/Composer/packages/server/src/models/bot/botProject.ts index df7f635818..6ddae1d3b9 100644 --- a/Composer/packages/server/src/models/bot/botProject.ts +++ b/Composer/packages/server/src/models/bot/botProject.ts @@ -402,12 +402,12 @@ export class BotProject { if (index === -1) { throw new Error(`no such file at ${relativePath}`); } - this.files[index].content = content; const absolutePath = `${this.dir}/${relativePath}`; // only write if the file has actually changed if (this.files[index].content !== content) { + this.files[index].content = content; await this.fileStorage.writeFile(absolutePath, content); }