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
1 change: 1 addition & 0 deletions Composer/packages/lib/shared/src/types/indexers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export enum FileExtensions {
Lg = '.lg',
Qna = '.qna',
Setting = 'appsettings.json',
FormDialogSchema = '.form-dialog',
}

export interface FileInfo {
Expand Down
3 changes: 2 additions & 1 deletion Composer/packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"@bfc/plugin-loader": "*",
"@bfc/shared": "*",
"@microsoft/bf-dispatcher": "^4.10.0-preview.141651",
"@microsoft/bf-generate-library": "^4.10.0-daily.20200827.161452",
"@microsoft/bf-lu": "^4.10.0-dev.20200808.5a7c973",
"archiver": "^3.0.0",
"axios": "^0.19.2",
Expand Down Expand Up @@ -99,4 +100,4 @@
"resolutions": {
"bl": "^2.2.1"
}
}
}
57 changes: 57 additions & 0 deletions Composer/packages/server/src/controllers/formDialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { Request, Response } from 'express';
import { PluginLoader } from '@bfc/plugin-loader';
import { schemas, expandPropertyDefinition } from '@microsoft/bf-generate-library';

import { BotProjectService } from '../services/project';

const expandJsonSchemaProperty = async (req: Request, res: Response) => {
const { propertyName, schema } = req.body;
const result = await expandPropertyDefinition(propertyName, schema);

if (result !== undefined) {
res.status(200).json(result);
} else {
res.status(404).json({
message: 'Failed to get form dialog schema/property.',
});
}
};

const getTemplateSchemas = async (req: Request, res: Response) => {
const result = await schemas();

if (result !== undefined) {
res.status(200).json(result);
} else {
res.status(404).json({
message: 'Failed to retrieve form dialog template schemas.',
});
}
};

const generate = async (req: Request, res: Response) => {
const projectId = req.params.projectId;
const user = await PluginLoader.getUserFromRequest(req);

const currentProject = await BotProjectService.getProjectById(projectId, user);
if (currentProject !== undefined) {
const { name } = req.body;

await currentProject.generateDialog(name);
const updatedProject = await BotProjectService.getProjectById(projectId, user);
res.status(200).json({ id: projectId, ...updatedProject.getProject() });
} else {
res.status(404).json({
message: `Could not generate form dialog. Project ${projectId} not found.`,
});
}
};

export const FormDialogController = {
getTemplateSchemas,
generate,
expandJsonSchemaProperty,
};
53 changes: 52 additions & 1 deletion Composer/packages/server/src/models/bot/botProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import fs from 'fs';

import axios from 'axios';
import { autofixReferInDialog } from '@bfc/indexers';
import { getNewDesigner, FileInfo, Skill, Diagnostic, IBotProject, DialogSetting } from '@bfc/shared';
import { getNewDesigner, FileInfo, Skill, Diagnostic, IBotProject, DialogSetting, FileExtensions } from '@bfc/shared';
import { UserIdentity, pluginLoader } from '@bfc/plugin-loader';
import { FeedbackType, generate } from '@microsoft/bf-generate-library';

import { Path } from '../../utility/path';
import { copyDir } from '../../utility/storage';
Expand Down Expand Up @@ -488,6 +489,56 @@ export class BotProject implements IBotProject {
return qnaEndpointKey;
};

public async generateDialog(name: string) {
const defaultLocale = this.settings?.defaultLanguage || defaultLanguage;
const relativePath = defaultFilePath(this.name, defaultLocale, `${name}${FileExtensions.FormDialogSchema}`);
const schemaPath = Path.resolve(this.dir, relativePath);

const dialogPath = defaultFilePath(this.name, defaultLocale, `${name}${FileExtensions.Dialog}`);
const outDir = Path.dirname(Path.resolve(this.dir, dialogPath));

const feedback = (type: FeedbackType, message: string): void => {
// eslint-disable-next-line no-console
console.log(`${type} - ${message}`);
};

const generateParams = {
schemaPath,
prefix: name,
outDir,
metaSchema: undefined,
allLocales: undefined,
templateDirs: [],
force: false,
merge: true,
singleton: true,
feedback,
};

// schema path = path to the JSON schema file defining the form data
// prefix - the dialog name to prefix on generated assets
// outDir - the directory where the dialog assets will be saved
// metaSchema - deprecated
// allLocales - the additional locales for which to generate assets
// templateDirs - paths to directories containing customized templates
// force - if assets are overwritten causing any user customizations to be lost
// merge - if generated assets should be merged with any user customized assets
// singleton - if the generated assets should be merged into a single dialog
// feeback - a callback for status and progress and generation happens
await generate(
generateParams.schemaPath,
generateParams.prefix,
generateParams.outDir,
generateParams.metaSchema,
generateParams.allLocales,
generateParams.templateDirs,
generateParams.force,
generateParams.merge,
generateParams.singleton,
generateParams.feedback
);
}

private async removeLocalRuntimeData(projectId) {
const method = 'localpublish';
if (pluginLoader.extensions.publish[method]?.methods?.stopBot) {
Expand Down
9 changes: 8 additions & 1 deletion Composer/packages/server/src/models/bot/botStructure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const BotStructureTemplate = {
qna: 'dialogs/${DIALOGNAME}/knowledge-base/en-us/${DIALOGNAME}.en-us.qna',
dialogSchema: 'dialogs/${DIALOGNAME}/${DIALOGNAME}.dialog.schema',
},
formDialogs: 'form-dialogs/${FORMDIALOGNAME}',
skillManifests: 'manifests/${MANIFESTFILENAME}',
};

Expand Down Expand Up @@ -52,13 +53,19 @@ export const defaultFilePath = (botName: string, defaultLocale: string, filename
const LOCALE = locale;

// 1. Even appsettings.json hit FileExtensions.Manifest, but it never use this do created.
// 2. When exprot bot as a skill, name is `EchoBot-4-2-1-preview-1-manifest.json`
// 2. When export bot as a skill, name is `EchoBot-4-2-1-preview-1-manifest.json`
if (fileType === FileExtensions.Manifest) {
return templateInterpolate(BotStructureTemplate.skillManifests, {
MANIFESTFILENAME: filename,
});
}

if (fileType === FileExtensions.FormDialogSchema) {
return templateInterpolate(BotStructureTemplate.formDialogs, {
FORMDIALOGNAME: filename,
});
}

// common.lg
if (fileId === CommonFileId && fileType === FileExtensions.Lg) {
return templateInterpolate(BotStructureTemplate.common.lg, {
Expand Down
6 changes: 6 additions & 0 deletions Composer/packages/server/src/router/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { StorageController } from '../controllers/storage';
import { PublishController } from '../controllers/publisher';
import { AssetController } from '../controllers/asset';
import { EjectController } from '../controllers/eject';
import { FormDialogController } from '../controllers/formDialog';
import * as PluginsController from '../controllers/plugins';

import { UtilitiesController } from './../controllers/utilities';
Expand All @@ -31,6 +32,11 @@ router.post('/projects/:projectId/qnaSettings/set', ProjectController.setQnASett
router.post('/projects/:projectId/project/saveAs', ProjectController.saveProjectAs);
router.get('/projects/:projectId/export', ProjectController.exportProject);

// form dialog generation apis
router.post('/formDialogs/expandJsonSchemaProperty', FormDialogController.expandJsonSchemaProperty);
router.get('/formDialogs/templateSchemas', FormDialogController.getTemplateSchemas);
router.post('/formDialogs/:projectId/generate', FormDialogController.generate);

// update the boilerplate content
router.get('/projects/:projectId/boilerplateVersion', ProjectController.checkBoilerplateVersion);
router.post('/projects/:projectId/updateBoilerplate', ProjectController.updateBoilerplate);
Expand Down
Loading