Skip to content

Commit

Permalink
fix: use custom schema when creating new dialogs (#2405)
Browse files Browse the repository at this point in the history
* remove appschema, make dialog factory aware of custom schema

* fix qna default values
  • Loading branch information
a-b-r-o-w-n committed Mar 27, 2020
1 parent dc30228 commit 7e9da2a
Show file tree
Hide file tree
Showing 20 changed files with 184 additions and 3,314 deletions.
6 changes: 3 additions & 3 deletions BotProject/Templates/CSharp/Schemas/sdk.schema
Original file line number Diff line number Diff line change
Expand Up @@ -9538,21 +9538,21 @@
"type": "string",
"title": "KnowledgeBase Id",
"description": "KnowledgeBase Id of your QnA Maker KnowledgeBase.",
"default": "settings.qna.knowledgebaseid"
"default": "=settings.qna.knowledgebaseid"
},
"endpointKey": {
"$role": "expression",
"type": "string",
"title": "Endpoint Key",
"description": "Endpoint key for the QnA Maker KB.",
"default": "settings.qna.endpointkey"
"default": "=settings.qna.endpointkey"
},
"hostname": {
"$role": "expression",
"type": "string",
"title": "Hostname",
"description": "Hostname for your QnA Maker service.",
"default": "settings.qna.hostname",
"default": "=settings.qna.hostname",
"examples": [
"https://yourserver.azurewebsites.net/qnamaker"
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ interface TriggerCreationModalProps {
export const TriggerCreationModal: React.FC<TriggerCreationModalProps> = props => {
const { isOpen, onDismiss, onSubmit, dialogId } = props;
const { state } = useContext(StoreContext);
const { dialogs, luFiles, locale, projectId } = state;
const { dialogs, luFiles, locale, projectId, schemas } = state;
const luFile = luFiles.find(({ id }) => id === `${dialogId}.${locale}`);
const dialogFile = dialogs.find(dialog => dialog.id === dialogId);
const isRegEx = get(dialogFile, 'content.recognizer.$type', '') === regexRecognizerKey;
Expand Down Expand Up @@ -129,7 +129,7 @@ export const TriggerCreationModal: React.FC<TriggerCreationModalProps> = props =

const content = get(luFile, 'content', '');
const luFileId = luFile?.id || `${dialogId}.${locale}`;
const newDialog = generateNewDialog(dialogs, dialogId, formData);
const newDialog = generateNewDialog(dialogs, dialogId, formData, schemas.sdk?.content);
if (formData.$type === intentTypeKey && !isRegEx) {
const newContent = addIntent(content, { Name: formData.intent, Body: formData.triggerPhrases });
const updateLuFile = {
Expand Down
20 changes: 10 additions & 10 deletions Composer/packages/client/src/pages/design/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import formatMessage from 'format-message';
import { globalHistory } from '@reach/router';
import get from 'lodash/get';
import { PromptTab } from '@bfc/shared';
import { seedNewDialog, SDKTypes, DialogInfo } from '@bfc/shared';
import { DialogFactory, SDKTypes, DialogInfo } from '@bfc/shared';

import { VisualEditorAPI } from '../../messenger/FrameAPI';
import { TestController } from '../../TestController';
Expand Down Expand Up @@ -81,7 +81,7 @@ const getTabFromFragment = () => {

function DesignPage(props) {
const { state, actions } = useContext(StoreContext);
const { dialogs, designPageLocation, breadcrumb, visualEditorSelection, projectId } = state;
const { dialogs, designPageLocation, breadcrumb, visualEditorSelection, projectId, schemas } = state;
const {
removeDialog,
setDesignPageLocation,
Expand Down Expand Up @@ -288,14 +288,14 @@ function DesignPage(props) {
}, [dialogs, breadcrumb]);

async function onSubmit(data: { name: string; description: string }) {
const seededContent = seedNewDialog(
SDKTypes.AdaptiveDialog,
{ name: data.name, description: data.description },
{
generator: `${data.name}.lg`,
},
state.actionsSeed || []
);
const seededContent = new DialogFactory(schemas.sdk?.content).create(SDKTypes.AdaptiveDialog, {
$designer: { name: data.name, description: data.description },
generator: `${data.name}.lg`,
});
if (seededContent.triggers && seededContent.triggers[0]) {
seededContent.triggers[0].actions = state.actionsSeed;
}

await actions.createDialog({ id: data.name, content: seededContent });
}

Expand Down
69 changes: 34 additions & 35 deletions Composer/packages/client/src/utils/dialogUtil.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { ConceptLabels, DialogGroup, SDKTypes, dialogGroups, seedNewDialog, DialogInfo } from '@bfc/shared';
import { ConceptLabels, DialogGroup, SDKTypes, dialogGroups, DialogInfo, DialogFactory } from '@bfc/shared';
import get from 'lodash/get';
import set from 'lodash/set';
import cloneDeep from 'lodash/cloneDeep';
Expand Down Expand Up @@ -45,57 +45,38 @@ export const activityTypeKey: string = SDKTypes.OnActivity;
export const messageTypeKey: string = SDKTypes.OnMessageEventActivity;
export const regexRecognizerKey: string = SDKTypes.RegexRecognizer;

export function getFriendlyName(data) {
if (get(data, '$designer.name')) {
return get(data, '$designer.name');
}

if (get(data, 'intent')) {
return `${get(data, 'intent')}`;
}

if (ConceptLabels[data.$type] && ConceptLabels[data.$type].title) {
return ConceptLabels[data.$type].title;
}

return data.$type;
}

export function insert(content, path: string, position: number | undefined, data: any) {
function insert(content, path: string, position: number | undefined, data: any) {
const current = get(content, path, []);
const insertAt = typeof position === 'undefined' ? current.length : position;
current.splice(insertAt, 0, data);
set(content, path, current);
return content;
}

export function generateNewTrigger(data: TriggerFormData) {
function generateNewTrigger(data: TriggerFormData, factory: DialogFactory) {
const optionalAttributes: { intent?: string; event?: string } = {};
if (data.specifiedType) {
data.$type = data.specifiedType;
}
if (data.intent) {
optionalAttributes.intent = data.intent;
}
const newStep = {
$type: data.$type,
...seedNewDialog(data.$type, {}, optionalAttributes),
};
const newStep = factory.create(data.$type as SDKTypes, optionalAttributes);
return newStep;
}

export function generateRegexExpression(intent: string, pattern: string) {
function generateRegexExpression(intent: string, pattern: string) {
return { intent, pattern };
}

export function createTrigger(dialog: DialogInfo, data: TriggerFormData): DialogInfo {
function createTrigger(dialog: DialogInfo, data: TriggerFormData, factory: DialogFactory): DialogInfo {
const dialogCopy = cloneDeep(dialog);
const trigger = generateNewTrigger(data);
const trigger = generateNewTrigger(data, factory);
insert(dialogCopy.content, 'triggers', undefined, trigger);
return dialogCopy;
}

export function createRegExIntent(dialog: DialogInfo, intent: string, pattern: string): DialogInfo {
function createRegExIntent(dialog: DialogInfo, intent: string, pattern: string): DialogInfo {
const regex = generateRegexExpression(intent, pattern);
const dialogCopy = cloneDeep(dialog);
insert(dialogCopy.content, 'recognizer.intents', undefined, regex);
Expand All @@ -116,7 +97,7 @@ export function updateRegExIntent(dialog: DialogInfo, intent: string, pattern: s

//it is possible that we cannot find a RegEx. Because it will clear all regEx when we
//switch to another recognizer type
export function deleteRegExIntent(dialog: DialogInfo, intent: string): DialogInfo {
function deleteRegExIntent(dialog: DialogInfo, intent: string): DialogInfo {
const dialogCopy = cloneDeep(dialog);
const regexIntents = get(dialogCopy, 'content.recognizer.intents', []);
const index = regexIntents.findIndex(ri => ri.intent === intent);
Expand All @@ -126,11 +107,17 @@ export function deleteRegExIntent(dialog: DialogInfo, intent: string): DialogInf
return dialogCopy;
}

export function generateNewDialog(dialogs: DialogInfo[], dialogId: string, data: TriggerFormData): DialogInfo {
export function generateNewDialog(
dialogs: DialogInfo[],
dialogId: string,
data: TriggerFormData,
schema: any
): DialogInfo {
//add new trigger
const dialog = dialogs.find(dialog => dialog.id === dialogId);
if (!dialog) throw new Error(`dialog ${dialogId} does not exist`);
let updatedDialog = createTrigger(dialog, data);
const factory = new DialogFactory(schema);
let updatedDialog = createTrigger(dialog, data, factory);

//add regex expression
if (data.regexEx) {
Expand All @@ -143,10 +130,6 @@ export function createSelectedPath(selected: number) {
return `triggers[${selected}]`;
}

export function createFocusedPath(selected: number, focused: number) {
return `triggers[${selected}].actions[${focused}]`;
}

export function deleteTrigger(dialogs: DialogInfo[], dialogId: string, index: number) {
let dialogCopy = getDialog(dialogs, dialogId);
if (!dialogCopy) return null;
Expand Down Expand Up @@ -232,13 +215,29 @@ export function getMessageTypes(): IDropdownOption[] {
return messageTypes;
}

export function getDialogsMap(dialogs: DialogInfo[]): DialogsMap {
function getDialogsMap(dialogs: DialogInfo[]): DialogsMap {
return dialogs.reduce((result, dialog) => {
result[dialog.id] = dialog.content;
return result;
}, {});
}

export function getFriendlyName(data) {
if (get(data, '$designer.name')) {
return get(data, '$designer.name');
}

if (get(data, 'intent')) {
return `${get(data, 'intent')}`;
}

if (ConceptLabels[data.$type] && ConceptLabels[data.$type].title) {
return ConceptLabels[data.$type].title;
}

return data.$type;
}

const getLabel = (dialog: DialogInfo, dataPath: string) => {
const data = get(dialog, dataPath);
if (!data) return '';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { DialogFactory } from '@bfc/shared';

import { insert, deleteNode, queryNode, getParentPaths } from '../../src/utils/jsonTracker';

const factory = new DialogFactory({});

describe('queryNode', () => {
describe('can query correct result', () => {
const dialog = { foo: { bar: [{ $type: 'firstOne' }, { $type: 'secondOne' }] } };
Expand Down Expand Up @@ -44,7 +48,7 @@ describe('insert', () => {
});

it('inserts into the correct position', () => {
const updated = insert(dialog, path, 1, 'newOne');
const updated = insert(dialog, path, 1, 'newOne', factory);
expect(updated.foo.bar).toEqual([
{
$type: 'firstOne',
Expand All @@ -60,7 +64,7 @@ describe('insert', () => {
});

it('inserts into front if position is less than 0', () => {
const updated = insert(dialog, path, -2, 'newOne');
const updated = insert(dialog, path, -2, 'newOne', factory);
expect(updated.foo.bar).toEqual([
{
$type: 'newOne',
Expand All @@ -76,7 +80,7 @@ describe('insert', () => {
});

it('inserts into end if position is greater than length', () => {
const updated = insert(dialog, path, 10, 'newOne');
const updated = insert(dialog, path, 10, 'newOne', factory);
expect(updated.foo.bar).toEqual([
{
$type: 'firstOne',
Expand All @@ -92,7 +96,7 @@ describe('insert', () => {
});

it('inserts at end if position is undefined', () => {
const updated = insert(dialog, path, undefined, 'newOne');
const updated = insert(dialog, path, undefined, 'newOne', factory);
expect(updated.foo.bar).toEqual([
{
$type: 'firstOne',
Expand All @@ -112,7 +116,7 @@ describe('insert', () => {
it('inserts a new array with one element', () => {
const path = 'foo.bar';

const updated = insert(dialog, path, 0, 'newOne');
const updated = insert(dialog, path, 0, 'newOne', factory);

expect(updated.foo.bar).toEqual([{ $type: 'newOne', $designer: { id: expect.any(String) } }]);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { seedNewDialog, SDKTypes, dialogGroups, DialogGroup } from '@bfc/shared';
import { DialogFactory, dialogGroups, DialogGroup } from '@bfc/shared';

import { EdgeMenu } from '../../../src/components/menus/EdgeMenu';
import { JsonBlock } from '../components/json-block';
Expand All @@ -15,6 +15,7 @@ export class VisualSDKDemo extends Component {
state = {
actions: this.seedInitialActions(),
};
factory = new DialogFactory({});

seedInitialActions() {
const initialTypes = [
Expand All @@ -26,13 +27,13 @@ export class VisualSDKDemo extends Component {
...dialogGroups[DialogGroup.CODE].types,
...dialogGroups[DialogGroup.LOG].types,
];
const initalActions = initialTypes.map(t => seedNewDialog(t));
const initalActions = initialTypes.map(t => this.factory.create(t));
return initalActions;
}

insertActionPreview($type) {
this.setState({
actions: [seedNewDialog($type), ...this.state.actions],
actions: [this.factory.create($type), ...this.state.actions],
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ const buildEdgeMenuItemsFromClipboardContext = (
DialogGroup.LOG,
],
true,
(e, item) => onClick(item ? item.$type : null),
(e, item) => onClick(item ? item.data.$type : null),
context.dialogFactory,
filter
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React from 'react';
import React, { useContext } from 'react';
import { createStepMenu, DialogGroup } from '@bfc/shared';

import { NodeRendererContext } from '../../store/NodeRendererContext';

import { IconMenu } from './IconMenu';

interface EventMenuProps {
Expand All @@ -12,8 +14,12 @@ interface EventMenuProps {
}

export const EventMenu: React.FC<EventMenuProps> = ({ label, onClick, ...rest }): JSX.Element => {
const eventMenuItems = createStepMenu([DialogGroup.EVENTS], false, (e, item): any =>
onClick(item ? item.$type : null)
const { dialogFactory } = useContext(NodeRendererContext);
const eventMenuItems = createStepMenu(
[DialogGroup.EVENTS],
false,
(e, item): any => onClick(item ? item.data.$type : null),
dialogFactory
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
deleteActions,
LgTemplateRef,
LgMetaData,
seedNewDialog,
ExternalResourceHandlerAsync,
walkLgResourcesInActionList,
} from '@bfc/shared';
Expand Down Expand Up @@ -66,6 +65,7 @@ export const ObiEditor: FC<ObiEditorProps> = ({
updateLgTemplate,
removeLgTemplates,
removeLuIntent,
dialogFactory,
} = useContext(NodeRendererContext);

const dereferenceLg: ExternalResourceHandlerAsync<string> = async (
Expand Down Expand Up @@ -158,15 +158,15 @@ export const ObiEditor: FC<ObiEditorProps> = ({
};
} else {
handler = e => {
const dialog = insert(data, e.id, e.position, e.$type);
const dialog = insert(data, e.id, e.position, e.$type, dialogFactory);
onChange(dialog);
onFocusSteps([`${e.id}[${e.position || 0}]`]);
};
}
break;
case NodeEventTypes.InsertEvent:
handler = e => {
const dialog = insert(data, e.id, e.position, e.$type);
const dialog = insert(data, e.id, e.position, e.$type, dialogFactory);
onChange(dialog);
onFocusEvent(`${e.id}[${e.position || 0}]`);
};
Expand Down Expand Up @@ -232,7 +232,7 @@ export const ObiEditor: FC<ObiEditorProps> = ({

const [, arrayPath, actionIndexStr] = indexes;
const startIndex = parseInt(actionIndexStr);
const placeholderAction = seedNewDialog(SDKTypes.BeginDialog, undefined, { dialog: newDialog });
const placeholderAction = dialogFactory.create(SDKTypes.BeginDialog, { dialog: newDialog });
const insertResult = insertAction(deleteResult, arrayPath, startIndex, placeholderAction);
onChange(insertResult);
});
Expand Down
Loading

0 comments on commit 7e9da2a

Please sign in to comment.