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

feat: multiple lg #1861

Merged
merged 49 commits into from
Feb 14, 2020
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0367b45
create lg when create dialog
zhixzhan Jan 6, 2020
42f7582
sync lg/lu when dialog create & remove
zhixzhan Jan 7, 2020
defa6dc
break common.lg into each dialog
zhixzhan Jan 7, 2020
6c9acf6
Merge branch 'master' of https://github.com/microsoft/BotFramework-Co…
zhixzhan Jan 8, 2020
df26c40
support import lg
zhixzhan Jan 9, 2020
c4a8ac2
add lg import resolver in client
zhixzhan Jan 15, 2020
5de2bab
Merge branch 'master' of https://github.com/microsoft/BotFramework-Co…
zhixzhan Jan 15, 2020
ed705b6
lg import resolver in server and lsp
zhixzhan Jan 15, 2020
bfe7d92
update tests
zhixzhan Jan 16, 2020
841e352
refine import resolver
zhixzhan Jan 16, 2020
7ce0d07
renaming
zhixzhan Jan 16, 2020
7aeea1c
handle imported file templates
zhixzhan Jan 17, 2020
34d8da2
update tests
zhixzhan Jan 17, 2020
d039e7f
Merge branch 'master' into managed-lg
zhixzhan Jan 17, 2020
97f0c83
avoid lg parse crash
zhixzhan Jan 19, 2020
fe2b926
when create lg, default import common.lg
zhixzhan Jan 20, 2020
b3f9cb8
update test
zhixzhan Jan 20, 2020
6a581c1
refine lsp diagnostics
zhixzhan Jan 20, 2020
6e5f316
do not build full content in import resolver
zhixzhan Jan 20, 2020
8bb61fa
visual editor add multiple lg support
zhixzhan Jan 20, 2020
9d407a2
update import case
zhixzhan Jan 20, 2020
bec5513
auto move generated lg template in to dialog.lg
zhixzhan Jan 21, 2020
a2a6ff4
Merge branch 'master' into managed-lg
zhixzhan Feb 4, 2020
ece8906
store use lg import resolver do check
zhixzhan Feb 4, 2020
b72c57d
fix error text line number
zhixzhan Feb 4, 2020
904c82f
Merge branch 'master' into managed-lg
zhixzhan Feb 5, 2020
b43e830
update bot sample use multiple lg
zhixzhan Feb 5, 2020
5a93d53
auto fix generator in sample bot dialog
zhixzhan Feb 5, 2020
a6489aa
support import from id
zhixzhan Feb 6, 2020
e2a5ee8
patch
zhixzhan Feb 6, 2020
8fb68e3
update lg demo id import
zhixzhan Feb 6, 2020
8bd4872
Merge branch 'master' into managed-lg
zhixzhan Feb 6, 2020
7d500f3
when create dialog, default add lg referrence
zhixzhan Feb 6, 2020
c57b22b
id import common
zhixzhan Feb 6, 2020
422bc3a
handle template create
zhixzhan Feb 6, 2020
2059627
Merge branch 'master' of https://github.com/microsoft/BotFramework-Co…
zhixzhan Feb 10, 2020
59d21cc
import common.lg
zhixzhan Feb 10, 2020
15d29ff
upgrade to lgTemplateJsonPath interface
zhixzhan Feb 10, 2020
55e1013
bugfix
zhixzhan Feb 10, 2020
8542a82
fix lg import syntax highlight problem
cosmicshuai Feb 10, 2020
470edf4
Merge branch 'master' into managed-lg
zhixzhan Feb 11, 2020
2b38da3
optimize
zhixzhan Feb 11, 2020
8f52315
update reducer
zhixzhan Feb 11, 2020
cda21f0
Merge branch 'master' into managed-lg
zhixzhan Feb 12, 2020
6fdba8d
build lg with \r\n newline
zhixzhan Feb 13, 2020
fc2e1a3
Merge branch 'master' into managed-lg
zhixzhan Feb 13, 2020
a571b2a
Merge branch 'master' into managed-lg
zhixzhan Feb 14, 2020
5cd861f
fix split newline
zhixzhan Feb 14, 2020
c8d6ed7
Merge branch 'managed-lg' of https://github.com/zhixzhan/BotFramework…
zhixzhan Feb 14, 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
6 changes: 2 additions & 4 deletions Composer/packages/client/src/ShellApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ export const ShellApi: React.FC = () => {
function getLgTemplates({ id }, event) {
if (isEventSourceValid(event) === false) return false;
if (id === undefined) throw new Error('must have a file id');
const file = lgFiles.find(file => file.id === id);
const focusedDialogId = focusPath.split('#').shift() || id;
const file = lgFiles.find(file => file.id === focusedDialogId);
if (!file) throw new Error(`lg file ${id} not found`);
return file.templates;
}
Expand Down Expand Up @@ -161,9 +162,6 @@ export const ShellApi: React.FC = () => {
templateName,
template,
});

const content = lgUtil.updateTemplate(file.content, templateName, template);
return lgUtil.checkLgContent(content, id);
}

function copyLgTemplateHandler({ id, fromTemplateName, toTemplateName }, event) {
Expand Down
2 changes: 1 addition & 1 deletion Composer/packages/client/src/pages/design/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ function DesignPage(props) {
}, [dialogs, breadcrumb]);

async function onSubmit(data: { name: string; description: string }) {
const content = getNewDesigner(data.name, data.description);
const content = { ...getNewDesigner(data.name, data.description), generator: `${data.name}.lg` };
const seededContent = seedNewDialog('Microsoft.AdaptiveDialog', content.$designer, content);
await actions.createDialog({ id: data.name, content: seededContent });
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ interface CodeEditorProps extends RouteComponentProps<{}> {
}

const CodeEditor: React.FC<CodeEditorProps> = props => {
const { actions, state } = useContext(StoreContext);
const { actions, state, resolvers } = useContext(StoreContext);
const { lgFiles } = state;
const { lgImportresolver } = resolvers;
const { fileId } = props;
const file = lgFiles?.find(({ id }) => id === 'common');
const file = lgFiles?.find(({ id }) => id === fileId);
const [diagnostics, setDiagnostics] = useState(get(file, 'diagnostics', []));
const [errorMsg, setErrorMsg] = useState('');
const [lgEditor, setLgEditor] = useState<editor.IStandaloneCodeEditor | null>(null);
Expand Down Expand Up @@ -125,25 +126,24 @@ const CodeEditor: React.FC<CodeEditorProps> = props => {
parameters,
body: value,
});
setDiagnostics(check(newContent, id));
setDiagnostics(check(newContent, id, lgImportresolver));
updateLgTemplate(value);
} catch (error) {
setErrorMsg(error.message);
}
} else {
setDiagnostics(check(value, id));
const diags = check(value, id, lgImportresolver);
setDiagnostics(diags);
updateLgFile(value);
}
},
[file, template]
);

const lgOption = template
? {
fileId: 'common',
templateId: template?.name || '',
}
: undefined;
const lgOption = {
fileId,
templateId: template?.name,
};

return (
<LgEditor
Expand Down
24 changes: 10 additions & 14 deletions Composer/packages/client/src/pages/language-generation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,11 @@ interface LGPageProps extends RouteComponentProps<{}> {

const LGPage: React.FC<LGPageProps> = props => {
const { state } = useContext(StoreContext);
const { lgFiles, dialogs } = state;
const { dialogs } = state;

const path = props.location?.pathname ?? '';
const { fileId = 'common' } = props;
const edit = /\/edit(\/)?$/.test(path);
const file = lgFiles.find(({ id }) => id === 'common');
const navLinks = useMemo(() => {
const newDialogLinks = dialogs.map(dialog => {
return { id: dialog.id, url: dialog.id, key: dialog.id, name: dialog.displayName };
Expand All @@ -60,8 +59,7 @@ const LGPage: React.FC<LGPageProps> = props => {

const onSelect = useCallback(
id => {
let url = `/language-generation/${id}`;
if (edit) url += `/edit`;
const url = `/language-generation/${id}`;
navigateTo(url);
},
[edit]
Expand Down Expand Up @@ -105,16 +103,14 @@ const LGPage: React.FC<LGPageProps> = props => {
<div css={projectContainer}>
<NavLinks navLinks={navLinks} onSelect={onSelect} fileId={fileId} />
</div>
{file && (
<div css={contentEditor}>
<Suspense fallback={<LoadingSpinner />}>
<Router primary={false} component={Fragment}>
<CodeEditor path="/edit" fileId={fileId} />
<TableView path="/" fileId={fileId} />
</Router>
</Suspense>
</div>
)}
<div css={contentEditor}>
<Suspense fallback={<LoadingSpinner />}>
<Router primary={false} component={Fragment}>
<CodeEditor path="/edit" fileId={fileId} />
<TableView path="/" fileId={fileId} />
</Router>
</Suspense>
</div>
</div>
</Fragment>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const TableView: React.FC<TableViewProps> = props => {
const { state, actions } = useContext(StoreContext);
const { dialogs, lgFiles } = state;
const { fileId } = props;
const file = lgFiles.find(({ id }) => id === 'common');
const file = lgFiles.find(({ id }) => id === fileId);
const createLgTemplate = useRef(debounce(actions.createLgTemplate, 500)).current;
const copyLgTemplate = useRef(debounce(actions.copyLgTemplate, 500)).current;
const removeLgTemplate = useRef(debounce(actions.removeLgTemplate, 500)).current;
Expand All @@ -42,20 +42,8 @@ const TableView: React.FC<TableViewProps> = props => {

useEffect(() => {
if (!file || isEmpty(file)) return;
const allTemplates = file.templates;

if (!activeDialog) {
setTemplates(allTemplates);
} else {
const dialogsTemplates: LgTemplate[] = [];
activeDialog.lgTemplates.forEach(item => {
const template = allTemplates.find(t => t.name === item.name);
if (template) {
dialogsTemplates.push(template);
}
});
setTemplates(dialogsTemplates);
}
setTemplates(file.templates);
}, [file, activeDialog]);

const onClickEdit = useCallback(
Expand Down Expand Up @@ -130,11 +118,6 @@ const TableView: React.FC<TableViewProps> = props => {
},
];

// do not allow delete/copy template in particular dialog
if (activeDialog) {
buttons.splice(1, 2);
}

return buttons;
},
[activeDialog, templates]
Expand Down Expand Up @@ -166,7 +149,6 @@ const TableView: React.FC<TableViewProps> = props => {
return <div css={formCell}>{item.body}</div>;
},
},

{
key: 'buttons',
name: '',
Expand Down Expand Up @@ -221,6 +203,25 @@ const TableView: React.FC<TableViewProps> = props => {
};

tableColums.splice(2, 0, usedInColumn);
} else {
const beenUsedColumn = {
key: 'beenUsed',
name: formatMessage('Been used'),
fieldName: 'beenUsed',
minWidth: 100,
maxWidth: 100,
isResizable: true,
isCollapsable: true,
data: 'string',
onRender: item => {
return activeDialog?.lgTemplates.includes(item.name) ? (
<IconButton iconProps={{ iconName: 'Accept' }} />
) : (
<div />
);
},
};
tableColums.splice(2, 0, beenUsedColumn);
}

return tableColums;
Expand All @@ -240,8 +241,7 @@ const TableView: React.FC<TableViewProps> = props => {
}, []);

const onRenderDetailsFooter = useCallback(() => {
// do not allow add template in particular dialog
// cause new tempalte is not used by this dialog yet.
// do not allow add template in particular dialog lg, it suppose to be auto generated in form.
if (activeDialog) return <div />;

return (
Expand Down
13 changes: 13 additions & 0 deletions Composer/packages/client/src/store/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

import React, { useReducer, useRef } from 'react';
import once from 'lodash/once';
import { ImportResolverDelegate, ImportResolver } from 'botbuilder-lg';

import { prepareAxios } from '../utils/auth';
import { getFileName, getBaseName } from '../utils/fileUtil';

import { reducer } from './reducer';
import bindActions from './action/bindActions';
Expand Down Expand Up @@ -64,12 +66,14 @@ interface StoreContextValue {
state: State;
dispatch: React.Dispatch<ActionType>;
actions: BoundActionHandlers;
resolvers: { lgImportresolver: ImportResolverDelegate };
}

export const StoreContext = React.createContext<StoreContextValue>({
state: initialState,
dispatch: () => {},
actions: {} as ActionHandlers,
resolvers: { lgImportresolver: ImportResolver.fileResolver },
});

interface StoreProviderProps {
Expand Down Expand Up @@ -99,6 +103,15 @@ export const StoreProvider: React.FC<StoreProviderProps> = props => {
state: getState(),
actions: boundActions,
dispatch: interceptDispatch,
resolvers: {
lgImportresolver: function(_source: string, id: string) {
const targetFileName = getFileName(id);
const targetFileId = getBaseName(targetFileName);
const targetFile = getState().lgFiles.find(({ id }) => id === targetFileId);
if (!targetFile) throw new Error(`${id} lg file not found`);
return { id, content: targetFile.content };
} as ImportResolverDelegate,
},
};

prepareAxiosWithStore({ dispatch, getState });
Expand Down
38 changes: 27 additions & 11 deletions Composer/packages/client/src/store/reducer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import set from 'lodash/set';
import { dialogIndexer } from '@bfc/indexers';
import { SensitiveProperties } from '@bfc/shared';
import { Diagnostic, DiagnosticSeverity, LgTemplate, lgIndexer } from '@bfc/indexers';
import { ImportResolverDelegate } from 'botbuilder-lg';

import { ActionTypes, FileTypes } from '../../constants';
import { DialogSetting, ReducerFunc } from '../types';
import { UserTokenPayload } from '../action/types';
import { getExtension } from '../../utils';
import { getExtension, getFileName, getBaseName } from '../../utils';
import settingStorage from '../../utils/dialogSettingStorage';

import createReducer from './createReducer';
Expand Down Expand Up @@ -83,6 +84,7 @@ const updateDialog: ReducerFunc = (state, { id, content }) => {
const removeDialog: ReducerFunc = (state, { response }) => {
state.dialogs = response.data.dialogs;
state.luFiles = response.data.luFiles;
state.lgFiles = response.data.lgFiles;
return state;
};

Expand All @@ -101,26 +103,40 @@ const createDialogCancel: ReducerFunc = state => {
const createDialogSuccess: ReducerFunc = (state, { response }) => {
state.dialogs = response.data.dialogs;
state.luFiles = response.data.luFiles;
state.lgFiles = response.data.lgFiles;
state.showCreateDialogModal = false;
delete state.onCreateDialogComplete;
return state;
};

const updateLgTemplate: ReducerFunc = (state, { id, content }) => {
state.lgFiles = state.lgFiles.map(lgFile => {
const lgFiles = state.lgFiles.map(lgFile => {
if (lgFile.id === id) {
const { check, parse } = lgIndexer;
const diagnostics = check(content, id);
let templates: LgTemplate[] = [];
try {
templates = parse(content, id);
} catch (err) {
diagnostics.push(new Diagnostic(err.message, id, DiagnosticSeverity.Error));
}
return { ...lgFile, templates, diagnostics, content };
lgFile.content = content;
return lgFile;
}
return lgFile;
});
const lgImportresolver: ImportResolverDelegate = function(_source: string, id: string) {
const targetFileName = getFileName(id);
const targetFileId = getBaseName(targetFileName);
const targetFile = lgFiles.find(({ id }) => id === targetFileId);
if (!targetFile) throw new Error(`file not found`);
return { id, content: targetFile.content };
};

state.lgFiles = lgFiles.map(lgFile => {
const { check, parse } = lgIndexer;
const { id, content } = lgFile;
const diagnostics = check(content, id, lgImportresolver);
let templates: LgTemplate[] = [];
try {
templates = parse(content, id);
} catch (err) {
diagnostics.push(new Diagnostic(err.message, id, DiagnosticSeverity.Error));
}
return { ...lgFile, templates, diagnostics, content };
});
return state;
};

Expand Down
4 changes: 4 additions & 0 deletions Composer/packages/client/src/utils/fileUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export function resolveToBasePath(base: string, relPath: string) {
return base === '/' ? leaf : `${base}${leaf}`;
}

export function getFileName(path: string): string {
return path.split('/').pop() || path;
}

// todo: icon file is fixed for now, need to be updated when get it from
// designer.
export function getFileIconName(file: File) {
Expand Down
13 changes: 2 additions & 11 deletions Composer/packages/client/src/utils/lgUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,15 @@
*/

import { LGParser } from 'botbuilder-lg';
import { lgIndexer, combineMessage, isValid, LgTemplate } from '@bfc/indexers';
import { lgIndexer, LgTemplate } from '@bfc/indexers';

const { check, parse } = lgIndexer;
const { parse } = lgIndexer;
export interface Template {
name: string;
parameters?: string[];
body: string;
}

export function checkLgContent(content: string, id: string) {
// check lg content, make up error message
const diagnostics = check(content, id);
if (!isValid(diagnostics)) {
const errorMsg = combineMessage(diagnostics);
throw new Error(errorMsg);
}
}

export function increaseNameUtilNotExist(templates: LgTemplate[], name: string): string {
// if duplicate, increse name with Copy1 Copy2 ...

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ export const ObiEditor: FC<ObiEditorProps> = ({
return lgTemplateRef ? lgTemplateRef.name : '';
})
.filter(x => !!x);
return removeLgTemplates('common', normalizedLgTemplates);
const lgFileId = path;
return removeLgTemplates(lgFileId, normalizedLgTemplates);
};

const dispatchEvent = (eventName: NodeEventTypes, eventData: any): any => {
Expand Down Expand Up @@ -98,7 +99,8 @@ export const ObiEditor: FC<ObiEditorProps> = ({
const newLgName = inputLgMetaData.toString();
const newLgTemplateRefString = new LgTemplateRef(newLgName).toString();

await copyLgTemplate('common', inputLgRef.name, newLgName);
const lgFileId = path;
await copyLgTemplate(lgFileId, inputLgRef.name, newLgName);
return newLgTemplateRefString;
};
pasteNodes(data, e.id, e.position, clipboardActions, copyLgTemplateToNewNode).then(dialog => {
Expand Down
2 changes: 1 addition & 1 deletion Composer/packages/lib/code-editor/src/LgEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const placeholder = `> To learn more about the LG file format, read the document

export interface LGOption {
fileId: string;
templateId: string;
templateId?: string;
}

export interface LGLSPEditorProps extends RichEditorProps {
Expand Down
Loading