Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: trigger creation bug #2151

Merged
merged 40 commits into from
Mar 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
680f645
fix bug
liweitian Mar 3, 2020
d35b07b
save tmp code
liweitian Mar 3, 2020
3baabd1
add regex intent back
liweitian Mar 3, 2020
f3bb301
update
liweitian Mar 4, 2020
3d09dee
update label text
liweitian Mar 4, 2020
22ea9bc
rename onMessageActivity to onMessageReceivedMessage
alanlong9278 Mar 4, 2020
11303bc
update trigger type validation function
liweitian Mar 4, 2020
0a5aee8
remove commented code
liweitian Mar 4, 2020
d584c7d
rename onMessageActivity to onMessageReceivedMessage & update trigger…
alanlong9278 Mar 4, 2020
80056dd
Merge branch 'trggerCreationBugFix' of https://github.com/liweitian/B…
liweitian Mar 4, 2020
7b10482
update regex field
liweitian Mar 4, 2020
e395e6b
remove commented code
liweitian Mar 4, 2020
b720921
remove commented code
liweitian Mar 4, 2020
1ef1aa0
revert auto-saved file
liweitian Mar 4, 2020
6007691
Merge branch 'trggerCreationBugFix' of https://github.com/liweitian/B…
liweitian Mar 5, 2020
7b8f29a
Merge branch 'triggerCreationStep2' into trggerCreationBugFix
liweitian Mar 5, 2020
5e54b19
update label text
liweitian Mar 5, 2020
713438d
fix bug
liweitian Mar 3, 2020
e8170f5
save tmp code
liweitian Mar 3, 2020
db898b5
add regex intent back
liweitian Mar 3, 2020
ef20191
update
liweitian Mar 4, 2020
b78b9d6
update label text
liweitian Mar 4, 2020
7f1aba7
rename onMessageActivity to onMessageReceivedMessage
alanlong9278 Mar 4, 2020
f94ba2c
update trigger type validation function
liweitian Mar 4, 2020
48182b6
remove commented code
liweitian Mar 4, 2020
eb56714
update regex field
liweitian Mar 4, 2020
539c2a1
remove commented code
liweitian Mar 4, 2020
08f9779
remove commented code
liweitian Mar 4, 2020
95200ef
revert auto-saved file
liweitian Mar 4, 2020
b36db10
update label text
liweitian Mar 5, 2020
63fecad
Merge branch 'master' into trggerCreationBugFix
alanlong9278 Mar 6, 2020
d7938ec
Merge branch 'trggerCreationBugFix' of https://github.com/liweitian/B…
liweitian Mar 8, 2020
233f2ec
update label text
liweitian Mar 8, 2020
c8467fb
update regEx api
liweitian Mar 9, 2020
b624da1
add shellapi updateRegExIntentHandler
liweitian Mar 9, 2020
5f5a313
shell api
alanlong9278 Mar 9, 2020
0f31ae5
inline regex in form editor
alanlong9278 Mar 9, 2020
89afc58
create new inline regex intent when no intent
alanlong9278 Mar 9, 2020
348be66
Merge branch 'master' into trggerCreationBugFix
liweitian Mar 9, 2020
4309287
Merge branch 'master' into trggerCreationBugFix
cwhitten Mar 9, 2020
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
10 changes: 10 additions & 0 deletions Composer/packages/client/src/ShellApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import get from 'lodash/get';
import { isExpression } from './utils';
import * as lgUtil from './utils/lgUtil';
import * as luUtil from './utils/luUtil';
import { updateRegExIntent } from './utils/dialogUtil';
import { StoreContext } from './store';
import ApiClient from './messenger/ApiClient';
import { getDialogData, setDialogData, sanitizeDialogData } from './utils';
Expand Down Expand Up @@ -243,6 +244,14 @@ export const ShellApi: React.FC = () => {
return await updateLuFile({ id, content });
}

async function updateRegExIntentHandler({ id, intentName, pattern }, event) {
if (isEventSourceValid(event) === false) return false;
const dialog = dialogs.find(dialog => dialog.id === id);
if (!dialog) throw new Error(`dialog ${dialogId} not found`);
const newDialog = updateRegExIntent(dialog, intentName, pattern);
return await updateDialog({ id, content: newDialog.content });
}

async function fileHandler(fileTargetType, fileChangeType, { id, content }, event) {
if (isEventSourceValid(event) === false) return false;

Expand Down Expand Up @@ -345,6 +354,7 @@ export const ShellApi: React.FC = () => {
apiClient.registerApi('addLuIntent', addLuIntentHandler);
apiClient.registerApi('updateLuIntent', updateLuIntentHandler);
apiClient.registerApi('removeLuIntent', removeLuIntentHandler);
apiClient.registerApi('updateRegExIntent', updateRegExIntentHandler);
apiClient.registerApi('navTo', navTo);
apiClient.registerApi('onFocusEvent', focusEvent);
apiClient.registerApi('onFocusSteps', focusSteps);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import get from 'lodash/get';
import { LuEditor } from '@bfc/code-editor';

import {
addNewTrigger,
generateNewDialog,
getTriggerTypes,
TriggerFormData,
TriggerFormDataErrors,
Expand All @@ -28,16 +28,21 @@ import {
getEventTypes,
getActivityTypes,
getMessageTypes,
regexRecognizerKey,
} from '../../utils/dialogUtil';
import { addIntent } from '../../utils/luUtil';
import { StoreContext } from '../../store';

import { styles, dropdownStyles, dialogWindow, intent } from './styles';

const nameRegex = /^[a-zA-Z0-9-_.]+$/;
const validateForm = (data: TriggerFormData): TriggerFormDataErrors => {
const validateForm = (
data: TriggerFormData,
isRegEx: boolean,
regExIntents: [{ intent: string; pattern: string }]
): TriggerFormDataErrors => {
const errors: TriggerFormDataErrors = {};
const { $type, specifiedType, intent, triggerPhrases } = data;
const { $type, specifiedType, intent, triggerPhrases, regexEx } = data;

if ($type === eventTypeKey && !specifiedType) {
errors.specifiedType = formatMessage('Please select a event type');
Expand All @@ -47,17 +52,29 @@ const validateForm = (data: TriggerFormData): TriggerFormDataErrors => {
errors.specifiedType = formatMessage('Please select an activity type');
}

if ($type === messageTypeKey && !specifiedType) {
errors.specifiedType = formatMessage('Please select a message type');
}

if (!$type) {
errors.$type = formatMessage('Please select a trigger type');
}

if (!intent || !nameRegex.test(intent)) {
if ($type === intentTypeKey && (!intent || !nameRegex.test(intent))) {
errors.intent = formatMessage(
'Spaces and special characters are not allowed. Use letters, numbers, -, or _., numbers, -, and _'
);
}

if (!triggerPhrases) {
if ($type === intentTypeKey && isRegEx && regExIntents.find(ri => ri.intent === intent)) {
errors.intent = `regEx ${intent} is already defined`;
}

if ($type === intentTypeKey && isRegEx && !regexEx) {
errors.regexEx = formatMessage('Please input regEx pattern');
}

if ($type === intentTypeKey && !isRegEx && !triggerPhrases) {
errors.triggerPhrases = formatMessage('Please input trigger phrases');
}
if (data.errors.triggerPhrases) {
Expand All @@ -66,7 +83,7 @@ const validateForm = (data: TriggerFormData): TriggerFormDataErrors => {
return errors;
};

interface LuFilePayload {
export interface LuFilePayload {
id: string;
content: string;
}
Expand All @@ -75,7 +92,7 @@ interface TriggerCreationModalProps {
dialogId: string;
isOpen: boolean;
onDismiss: () => void;
onSubmit: (dialog: DialogInfo, luFilePayload: LuFilePayload) => void;
onSubmit: (dialog: DialogInfo, luFilePayload?: LuFilePayload) => void;
}

const initialFormData: TriggerFormData = {
Expand All @@ -84,6 +101,7 @@ const initialFormData: TriggerFormData = {
specifiedType: '',
intent: '',
triggerPhrases: '',
regexEx: '',
};

const triggerTypeOptions: IDropdownOption[] = getTriggerTypes();
Expand All @@ -94,10 +112,12 @@ export const TriggerCreationModal: React.FC<TriggerCreationModalProps> = props =
const { state } = useContext(StoreContext);
const { dialogs, luFiles } = state;
const luFile = luFiles.find(lu => lu.id === dialogId);

const dialogFile = dialogs.find(dialog => dialog.id === dialogId);
const isRegEx = get(dialogFile, 'content.recognizer.$type', '') === regexRecognizerKey;
const regexIntents = get(dialogFile, 'content.recognizer.intents', []);
const onClickSubmitButton = e => {
e.preventDefault();
const errors = validateForm(formData);
const errors = validateForm(formData, isRegEx, regexIntents);

if (Object.keys(errors).length) {
setFormData({
Expand All @@ -108,13 +128,17 @@ export const TriggerCreationModal: React.FC<TriggerCreationModalProps> = props =
}

const content = get(luFile, 'content', '');
const newContent = addIntent(content, { Name: formData.intent, Body: formData.triggerPhrases });
const updateLuFile = {
id: dialogId,
content: newContent,
};
const newDialog = addNewTrigger(dialogs, dialogId, formData);
onSubmit(newDialog, updateLuFile);
const newDialog = generateNewDialog(dialogs, dialogId, formData);
if (formData.$type === intentTypeKey && !isRegEx) {
const newContent = addIntent(content, { Name: formData.intent, Body: formData.triggerPhrases });
const updateLuFile = {
id: dialogId,
content: newContent,
};
onSubmit(newDialog, updateLuFile);
} else {
onSubmit(newDialog);
}
onDismiss();
};

Expand All @@ -130,6 +154,10 @@ export const TriggerCreationModal: React.FC<TriggerCreationModalProps> = props =
setFormData({ ...formData, intent: name });
};

const onChangeRegEx = (e, pattern) => {
setFormData({ ...formData, regexEx: pattern });
};

const onTriggerPhrasesChange = (body: string) => {
const errors = formData.errors;
const content = '#' + formData.intent + '\n' + body;
Expand All @@ -142,7 +170,9 @@ export const TriggerCreationModal: React.FC<TriggerCreationModalProps> = props =
const activityTypes: IDropdownOption[] = getActivityTypes();
const messageTypes: IDropdownOption[] = getMessageTypes();

const showIntentFields = formData.$type === intentTypeKey;
const showIntentName = formData.$type === intentTypeKey;
const showRegExDropDown = formData.$type === intentTypeKey && isRegEx;
const showTriggerPhrase = formData.$type === intentTypeKey && !isRegEx;
const showEventDropDown = formData.$type === eventTypeKey;
const showActivityDropDown = formData.$type === activityTypeKey;
const showMessageDropDown = formData.$type === messageTypeKey;
Expand Down Expand Up @@ -172,7 +202,6 @@ export const TriggerCreationModal: React.FC<TriggerCreationModalProps> = props =
data-testid={'triggerTypeDropDown'}
defaultSelectedKey={intentTypeKey}
/>

{showEventDropDown && (
<Dropdown
placeholder={formatMessage('Select a event type')}
Expand Down Expand Up @@ -206,17 +235,30 @@ export const TriggerCreationModal: React.FC<TriggerCreationModalProps> = props =
data-testid={'messageTypeDropDown'}
/>
)}
{showIntentFields && (
{showIntentName && (
<TextField
label={formatMessage('What is the name of this trigger')}
label={
isRegEx
? formatMessage('What is the name of this trigger (RegEx)')
: formatMessage('What is the name of this trigger (Luis)')
}
styles={intent}
onChange={onNameChange}
errorMessage={formData.errors.intent}
data-testid="TriggerName"
/>
)}
{showIntentFields && <Label>{formatMessage('Trigger Phrases')}</Label>}
{showIntentFields && (

{showRegExDropDown && (
<TextField
label={formatMessage('Please input regex pattern')}
onChange={onChangeRegEx}
errorMessage={formData.errors.regexEx}
data-testid={'RegExDropDown'}
/>
)}
{showTriggerPhrase && <Label>{formatMessage('Trigger phrases')}</Label>}
{showTriggerPhrase && (
<LuEditor
onChange={onTriggerPhrasesChange}
value={formData.triggerPhrases}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ const shellApi: ShellApi = {
return apiClient.apiCall('removeLuIntent', { id, intentName });
},

updateRegExIntent: (id, intentName, pattern) => {
return apiClient.apiCall('updateRegExIntent', { id, intentName, pattern });
},

createDialog: () => {
return apiClient.apiCall('createDialog');
},
Expand Down
18 changes: 11 additions & 7 deletions Composer/packages/client/src/pages/design/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ import { globalHistory } from '@reach/router';
import get from 'lodash/get';
import { PromptTab } from '@bfc/shared';
import { getNewDesigner, seedNewDialog } from '@bfc/shared';
import { DialogInfo } from '@bfc/indexers';

import { VisualEditorAPI } from '../../messenger/FrameAPI';
import { TestController } from '../../TestController';
import { BASEPATH, DialogDeleting } from '../../constants';
import { createSelectedPath, deleteTrigger, getbreadcrumbLabel } from '../../utils';
import { TriggerCreationModal } from '../../components/ProjectTree/TriggerCreationModal';
import { TriggerCreationModal, LuFilePayload } from '../../components/ProjectTree/TriggerCreationModal';
import { Conversation } from '../../components/Conversation';
import { DialogStyle } from '../../components/Modal/styles';
import { OpenConfirmModal } from '../../components/Modal/Confirm';
Expand Down Expand Up @@ -171,18 +172,21 @@ function DesignPage(props) {
setTriggerModalVisibility(true);
};

const onTriggerCreationSubmit = (dialog, luFile) => {
const onTriggerCreationSubmit = (dialog: DialogInfo, luFile?: LuFilePayload) => {
const dialogPayload = {
id: dialog.id,
content: dialog.content,
};
const luFilePayload = {
id: luFile.id,
content: luFile.content,
};
if (luFile) {
const luFilePayload = {
id: luFile.id,
content: luFile.content,
};
actions.updateLuFile(luFilePayload);
}

const index = get(dialog, 'content.triggers', []).length - 1;
actions.selectTo(`triggers[${index}]`);
actions.updateLuFile(luFilePayload);
actions.updateDialog(dialogPayload);
};

Expand Down
58 changes: 48 additions & 10 deletions Composer/packages/client/src/utils/dialogUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ export interface TriggerFormData {
specifiedType: string;
intent: string;
triggerPhrases: string;
regexEx: string;
}

export interface TriggerFormDataErrors {
$type?: string;
intent?: string;
specifiedType?: string;
triggerPhrases?: string;
regexEx?: string;
}

export function getDialog(dialogs: DialogInfo[], dialogId: string) {
Expand Down Expand Up @@ -60,8 +62,15 @@ export function getFriendlyName(data) {
return data.$type;
}

export function insert(content, path: string, position: number | undefined, data: TriggerFormData) {
export 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) {
const optionalAttributes: { intent?: string; event?: string } = {};
if (data.specifiedType) {
data.$type = data.specifiedType;
Expand All @@ -73,23 +82,52 @@ export function insert(content, path: string, position: number | undefined, data
$type: data.$type,
...seedNewDialog(data.$type, {}, optionalAttributes),
};
return newStep;
}

const insertAt = typeof position === 'undefined' ? current.length : position;

current.splice(insertAt, 0, newStep);
export function generateRegexExpression(intent: string, pattern: string) {
return { intent, pattern };
}

set(content, path, current);
export function createNewTrigger(dialog: DialogInfo, data: TriggerFormData): DialogInfo {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type DialogInfo I believe is an indexed dialog type, but here you didn't
reindex it, and seems no need to reindex it. So I would suggest return content: string.

const dialogCopy = cloneDeep(dialog);
const trigger = generateNewTrigger(data);
insert(dialogCopy.content, 'triggers', undefined, trigger);
return dialogCopy;
}

return content;
export function createRegExIntent(dialog: DialogInfo, intent: string, pattern: string): DialogInfo {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above.

const regex = generateRegexExpression(intent, pattern);
const dialogCopy = cloneDeep(dialog);
insert(dialogCopy.content, 'recognizer.intents', undefined, regex);
return dialogCopy;
}

export function addNewTrigger(dialogs: DialogInfo[], dialogId: string, data: TriggerFormData): DialogInfo {
const dialogCopy = getDialog(dialogs, dialogId);
if (!dialogCopy) throw new Error(`dialog ${dialogId} does not exist`);
insert(dialogCopy.content, 'triggers', undefined, data);
export function updateRegExIntent(dialog: DialogInfo, intent: string, pattern: string): DialogInfo {
let dialogCopy = cloneDeep(dialog);
const regexIntents = get(dialogCopy, 'content.recognizer.intents', []);
const targetIntent = regexIntents.find(ri => ri.intent === intent);
if (!targetIntent) {
dialogCopy = createRegExIntent(dialog, intent, pattern);
} else {
targetIntent.pattern = pattern;
}
return dialogCopy;
}

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

//add regex expression
if (data.regexEx) {
updatedDialog = createRegExIntent(updatedDialog, data.intent, data.regexEx);
}
return updatedDialog;
}

export function createSelectedPath(selected: number) {
return `triggers[${selected}]`;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ const mockShellApi = [
'addLuIntent',
'updateLuIntent',
'removeLuIntent',
'updateRegExIntent',
'validateExpression',
'onFocusSteps',
'onFocusEvent',
Expand Down
Loading