Skip to content

Commit

Permalink
feat: Plugin loader and multi-user capabilities (#2023)
Browse files Browse the repository at this point in the history
* first commit of working prototype for mongoDB storage

* change mechanism for creating zip file for syncing to runtime
now builds all the files loaded into project (from arbitrary source) rahter than zipping a folder on disk

* clean up console output

* plugin loader looks for plugins, finds them

* dynamically load in the storage provider via plugin loader
use custom storage if one is specified

* remove some debug traces

* clean up some minor code formatting things

* disable linter rule to allow for requiring in plugin modules

* slight refactor of plugin loader to encapsulate functionality
add sample web route plugin
first tiny stab at publish -- TIME TO BRANCH

* functioning auth implementattion using passportjs

* sample github auth

* first round of changes for multi-user support
Related to #1918 and #1917

* add in the bot project id into the route

* fix new bot

* fix save as

* rationalize all the URLS used in context of a project

* plumb user identity down through storage

* disable plugins

* add readme

* plugin readme

* plugin readme

* plugin readme

* plugin readme

* import publisher controller from other branch

* update to readme

* import prototype of publishing page

* updates

* delete environment and connector

* add error checking for missing project by id

* Better error handling for attempts to write to a missing or moved project
Should fix last remaining item in #1918

* add publish method with  localpublish plugin

* keep bot dir clean

* basic publish of localpublish plugin

* remove hostBot

* add gitignore file

* change unzip template into copy template folder

* Fix build script

* Fix default projectMap not exists

* change luis operation to match local plugin

* polish default setting

* use projectId

* clean up

* remove lib dir

* use projectId

* use limit 'currentBotProjects' cache

* remove unused

* Update copy

* return error response when publish failed

* update name initialization process

* fix some test,hide publish tab and change deploy script

* fix UT integrity check

* fix bf-cli version error

* update the version

* remove @microsoft/bf-cli-command@1.0.0

* try to fix todo e2e test

* fix lint

* fix lint

* remove some unused test and skip some failed unit tests

* feat: Prevent old content from overwriting newer changes (#2156)

* updates to write operations to prevent overwrite
not 100% working on LU files yet

* debounce lu

* remove comment

* remove some console logs

* address feedback from chris

* remove commented code

* change hint word deploy to publish

* fix lint

* fix some test syntax

* update the ui for notification page

* fix luis publish path not found

* fix some e2e tests

* fix onboarding when using projectId

* fix notification page bug

* Back out simple overwrite protection, will replace with less error prone mechanism

* add plugins volumn in docker

* fix docker up

* fix unit test

* fix the filesetting path

* change docker to map multi ports. in order to support multi runtime

* add projectId when create and delete dialog

* fix botstatus

* fix botstatus

* remove console log

* fix settings files path

* update the moke data

Co-authored-by: Wenyi Luo <wenyluo@microsoft.com>
Co-authored-by: Dong Lei <donglei@microsoft.com>
Co-authored-by: zhixzhan <zhixzhan@microsoft.com>
Co-authored-by: liweitian <liweitian93@outlook.com>
Co-authored-by: leilzh <leilzh@microsoft.com>
Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>
  • Loading branch information
7 people authored Mar 19, 2020
1 parent 4338c1f commit cbb5b4a
Show file tree
Hide file tree
Showing 152 changed files with 7,243 additions and 1,706 deletions.
1 change: 1 addition & 0 deletions Composer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ COPY --from=build /src/Composer/package.json .
COPY --from=build /src/Composer/packages/server ./packages/server
COPY --from=build /src/Composer/packages/lib ./packages/lib
COPY --from=build /src/Composer/packages/tools ./packages/tools
COPY --from=build /src/Composer/plugins ./plugins

ENV NODE_ENV "production"
RUN yarn --production --frozen-lockfile --force && yarn cache clean
Expand Down
6 changes: 3 additions & 3 deletions Composer/cypress/integration/LUPage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
// Licensed under the MIT License.

context('LU Page', () => {
before(() => {
beforeEach(() => {
cy.visit(Cypress.env('COMPOSER_URL'));
cy.createBot('ToDoBotWithLuisSample');
});

it('can open language understanding page', () => {
cy.findByTestId('LeftNav-CommandBarButtonUser Input').click();

cy.wait(1000);
// left nav tree
cy.contains('ToDoBotWithLuisSample.Main');
cy.contains('__TestToDoBotWithLuisSample.Main');
cy.contains('All');

cy.get('.toggleEditMode button').should('not.exist');
Expand Down
11 changes: 5 additions & 6 deletions Composer/cypress/integration/LuisDeploy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@
context('Luis Deploy', () => {
beforeEach(() => {
cy.server();
cy.route('GET', '/api/launcher/connect?botEnvironment=production', 'OK');
cy.route('POST', '/api/launcher/sync', 'OK');
cy.route('POST', 'api/projects/opened/settings', 'OK');
cy.route('POST', '/api/publish/*/publish/default', 'OK');
cy.route('POST', '/api/projects/*/settings', 'OK');
cy.visit(Cypress.env('COMPOSER_URL'));
cy.createBot('ToDoBotWithLuisSample');
});

it('can deploy luis success', () => {
cy.findByTestId('LeftNav-CommandBarButtonUser Input').click();

cy.wait(1000);
cy.route({
method: 'POST',
url: '/api/projects/opened/luFiles/publish',
url: 'api/projects/*/luFiles/publish',
status: 200,
response: 'fixture:luPublish/success',
});
Expand All @@ -39,7 +38,7 @@ context('Luis Deploy', () => {

cy.route({
method: 'POST',
url: '/api/projects/opened/luFiles/publish',
url: 'api/projects/*/luFiles/publish',
status: 400,
response: 'fixture:luPublish/error',
});
Expand Down
10 changes: 5 additions & 5 deletions Composer/cypress/integration/NotificationPage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ context('Notification Page', () => {

cy.get('.toggleEditMode button').as('switchButton');
cy.get('@switchButton').click();
cy.get('textarea').type('test lg syntax error#');
cy.get('textarea').type('#');

cy.visitPage('Notifications');
cy.get('[data-testid="notifications-info-button"]').click();

cy.get('[data-testid="notifications-table-view"]').within(() => {
cy.findAllByText('common.lg')
Expand All @@ -35,9 +35,9 @@ context('Notification Page', () => {
});

cy.get('.toggleEditMode button').click();
cy.get('textarea').type('test lu syntax error');
cy.get('textarea').type('t');

cy.visitPage('Notifications');
cy.get('[data-testid="notifications-info-button"]').click();

cy.get('[data-testid="notifications-table-view"]').within(() => {
cy.findAllByText('Main.lu')
Expand Down Expand Up @@ -65,7 +65,7 @@ context('Notification Page', () => {
cy.get('.ObjectItem input').type('()');
});

cy.visitPage('Notifications');
cy.get('[data-testid="notifications-info-button"]').click();

cy.get('[data-testid="notifications-table-view"]').within(() => {
cy.findAllByText('Main.dialog')
Expand Down
3 changes: 2 additions & 1 deletion Composer/cypress/integration/ToDoBot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
// Licensed under the MIT License.

context('ToDo Bot', () => {
beforeEach(() => {
before(() => {
cy.visit(Cypress.env('COMPOSER_URL'));
cy.createBot('TodoSample');
cy.wait(5000);
});

it('can open the main dialog', () => {
Expand Down
5 changes: 3 additions & 2 deletions Composer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@
"packages/tools/language-servers/*"
],
"scripts": {
"build": "node scripts/update.js && node scripts/begin.js && yarn build:prod",
"build": "node scripts/update.js && node scripts/begin.js && yarn build:prod && yarn build:plugins",
"build:prod": "yarn build:dev && yarn build:server && yarn build:client",
"build:dev": "yarn build:lib && yarn build:tools && yarn build:extensions",
"build:dev": "yarn build:lib && yarn build:tools && yarn build:extensions && yarn build:plugins",
"build:lib": "yarn workspace @bfc/libs build:all",
"build:extensions": "yarn workspace @bfc/extensions build:all",
"build:server": "yarn workspace @bfc/server build",
"build:client": "yarn workspace @bfc/client build",
"build:tools": "yarn workspace @bfc/tools build:all",
"build:plugins": "cd plugins/localPublish && yarn install && yarn build",
"start": "cross-env NODE_ENV=production PORT=3000 yarn start:server",
"startall": "node scripts/update.js && yarn start",
"start:dev": "concurrently \"npm:start:client\" \"npm:start:server:dev\"",
Expand Down
2 changes: 2 additions & 0 deletions Composer/packages/client/config/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ function getClientEnvironment(publicUrl) {
.replace('\n', ''),
SDK_PACKAGE_VERSION: '4.7.0-preview-191208-1', // TODO: change this when Composer supports custom schema/custom runtime
COMPOSER_VERSION: 'Preview 2.0',
LOCAL_PUBLISH_PATH:
process.env.LOCAL_PUBLISH_PATH || path.resolve(process.cwd(), '../../plugins/localPublish/hostedBots'),
}
);
// Stringify all values so we can feed into Webpack DefinePlugin
Expand Down
2 changes: 1 addition & 1 deletion Composer/packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,4 @@
"webpack-manifest-plugin": "2.1.0",
"workbox-webpack-plugin": "4.3.1"
}
}
}
17 changes: 9 additions & 8 deletions Composer/packages/client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ const Onboarding = React.lazy(() => import('./Onboarding'));
// eslint-disable-next-line react/display-name
const Content = forwardRef<HTMLDivElement>((props, ref) => <div css={content} {...props} ref={ref} />);

const topLinks = (botLoaded: boolean) => {
const topLinks = (projectId: string) => {
const botLoaded = !!projectId;
let links = [
{
to: '/home',
Expand All @@ -37,7 +38,7 @@ const topLinks = (botLoaded: boolean) => {
disabled: false,
},
{
to: '/dialogs/Main',
to: `/bot/${projectId}/dialogs/Main`,
iconName: 'SplitObject',
labelName: formatMessage('Design Flow'),
exact: false,
Expand All @@ -51,14 +52,14 @@ const topLinks = (botLoaded: boolean) => {
// disabled: true, // will delete
// },
{
to: '/language-generation',
to: `/bot/${projectId}/language-generation`,
iconName: 'Robot',
labelName: formatMessage('Bot Responses'),
exact: false,
disabled: !botLoaded,
},
{
to: '/language-understanding',
to: `/bot/${projectId}/language-understanding`,
iconName: 'People',
labelName: formatMessage('User Input'),
exact: false,
Expand All @@ -72,14 +73,14 @@ const topLinks = (botLoaded: boolean) => {
// disabled: true,
// },
{
to: '/notifications',
to: `/bot/${projectId}/notifications`,
iconName: 'Warning',
labelName: formatMessage('Notifications'),
exact: true,
disabled: !botLoaded,
},
{
to: '/setting/',
to: `/bot/${projectId}/setting/`,
iconName: 'Settings',
labelName: formatMessage('Settings'),
exact: false,
Expand Down Expand Up @@ -114,7 +115,7 @@ const bottomLinks = [
export const App: React.FC = () => {
const { state, actions } = useContext(StoreContext);
const [sideBarExpand, setSideBarExpand] = useState(false);
const { botName, creationFlowStatus } = state;
const { botName, projectId, creationFlowStatus } = state;
const { setCreationFlowStatus } = actions;
const mapNavItemTo = x => resolveToBasePath(BASEPATH, x);

Expand All @@ -136,7 +137,7 @@ export const App: React.FC = () => {
ariaLabel={sideBarExpand ? formatMessage('Collapse Nav') : formatMessage('Expand Nav')}
/>
<div css={dividerTop} />{' '}
{topLinks(!!botName).map((link, index) => {
{topLinks(projectId).map((link, index) => {
return (
<NavItem
key={'NavLeftBar' + index}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ interface FormDataError {
name?: string;
}

interface Bots {
interface Files {
name: '';
path: '';
}
Expand All @@ -40,13 +40,13 @@ interface DefineConversationProps {
onGetErrorMessage?: (text: string) => void;
focusedStorageFolder?: StorageFolder;
currentPath?: string;
bots?: Bots[];
files?: Files[];
}

const initialFormDataError: FormDataError = {};

export const DefineConversation: React.FC<DefineConversationProps> = props => {
const { onSubmit, onDismiss, onCurrentPathUpdate, focusedStorageFolder, currentPath, bots } = props;
const { onSubmit, onDismiss, onCurrentPathUpdate, focusedStorageFolder, currentPath, files } = props;
const { state } = useContext(StoreContext);
const { templateId } = state;

Expand All @@ -58,9 +58,9 @@ export const DefineConversation: React.FC<DefineConversationProps> = props => {
i++;
defaultName = `${bot}-${i}`;
} while (
bots &&
bots.find(bot => {
return bot.name === defaultName;
files &&
files.find(file => {
return file.name.toLowerCase() === defaultName.toLowerCase();
}) &&
i < MAXTRYTIMES
);
Expand Down Expand Up @@ -94,9 +94,9 @@ export const DefineConversation: React.FC<DefineConversationProps> = props => {
: '';
if (
name &&
bots &&
bots.find(bot => {
return bot.path === newBotPath;
files &&
files.find(bot => {
return bot.path.toLowerCase() === newBotPath.toLowerCase();
})
) {
errors.name = formatMessage('Duplication of names');
Expand All @@ -116,7 +116,7 @@ export const DefineConversation: React.FC<DefineConversationProps> = props => {
setDisable(false);
}
setFormDataErrors(errors);
}, [bots, formData.name]);
}, [files, formData.name]);

const handleSubmit = e => {
e.preventDefault();
Expand Down
20 changes: 7 additions & 13 deletions Composer/packages/client/src/CreationFlow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { navigateTo } from './../utils/navigation';

export function CreationFlow(props) {
const { state, actions } = useContext(StoreContext);
const [bots, setBots] = useState([]);
const [files, setFiles] = useState([]);
const [step, setStep] = useState();
// eslint-disable-next-line react/prop-types
const { creationFlowStatus, setCreationFlowStatus } = props;
Expand Down Expand Up @@ -47,13 +47,7 @@ export function CreationFlow(props) {
useEffect(() => {
const allFilesInFolder = get(focusedStorageFolder, 'children', []);

const botsInCurrentFolder = allFilesInFolder.filter(file => {
if (file.type === FileTypes.BOT) {
return file;
}
});

setBots(botsInCurrentFolder);
setFiles(allFilesInFolder);
if (Object.keys(focusedStorageFolder).length) {
setCurrentPath(Path.join(focusedStorageFolder.parent, focusedStorageFolder.name));
}
Expand Down Expand Up @@ -105,7 +99,7 @@ export function CreationFlow(props) {

const openBot = async botFolder => {
await openBotProject(botFolder);
navigateTo('/dialogs/Main');
// navigateTo(`/bot/${state.projectId}/dialogs/Main`);
handleDismiss();
};

Expand All @@ -114,7 +108,7 @@ export function CreationFlow(props) {
};

const handleSaveAs = async formData => {
await saveProjectAs(formData.name, formData.description, formData.location);
await saveProjectAs(state.projectId, formData.name, formData.description, formData.location);
};

const handleSubmit = formData => {
Expand All @@ -123,11 +117,11 @@ export function CreationFlow(props) {
case CreationFlowStatus.NEW_FROM_TEMPLATE:
case CreationFlowStatus.NEW:
handleCreateNew(formData);
navigateTo('/dialogs/Main');
//navigateTo(`/bot/${state.projectId}/dialogs/Main`);
break;
case CreationFlowStatus.SAVEAS:
handleSaveAs(formData);
navigateTo('/dialogs/Main');
navigateTo(`/bot/${state.projectId}/dialogs/Main`);
break;
default:
setStep(Steps.NONE);
Expand Down Expand Up @@ -167,7 +161,7 @@ export function CreationFlow(props) {
onCurrentPathUpdate={updateCurrentPath}
focusedStorageFolder={focusedStorageFolder}
currentPath={currentPath}
bots={bots}
files={files}
/>
),
},
Expand Down
7 changes: 4 additions & 3 deletions Composer/packages/client/src/Onboarding/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ const Onboarding: React.FC = () => {
state: {
dialogs,
onboarding: { complete },
projectId,
},
} = useContext(StoreContext);

const [stepSets, setStepSets] = useState<IStepSet[]>(defaultStepSets());
const [stepSets, setStepSets] = useState<IStepSet[]>(defaultStepSets(projectId));
const [currentSet, setCurrentSet] = useState<number>(getCurrentSet(stepSets));
const [currentStep, setCurrentStep] = useState<number>(0);
const [hideModal, setHideModal] = useState(true);
Expand Down Expand Up @@ -67,7 +68,7 @@ const Onboarding: React.FC = () => {
}, [currentSet, currentStep, setTeachingBubble]);

useEffect(() => {
const sets = defaultStepSets()
const sets = defaultStepSets(projectId)
.map(stepSet => ({
...stepSet,
steps: stepSet.steps.filter(({ targetId }) => {
Expand All @@ -85,7 +86,7 @@ const Onboarding: React.FC = () => {
}, [dialogs]);

useEffect(() => {
setHideModal(pathname !== '/dialogs/Main');
setHideModal(pathname !== `/bot/${projectId}/dialogs/Main`);
if (currentSet === 0) {
setCurrentStep(pathname === '/home' ? 0 : -1);
}
Expand Down
Loading

0 comments on commit cbb5b4a

Please sign in to comment.