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
Show all changes
22 commits
Select commit Hold shift + click to select a range
8fe86c3
Adding modal for node detection on app start
pavolumMsft Mar 26, 2021
4773888
- Adding server side checking of node
pavolumMsft Mar 26, 2021
8baf60d
Merge branch 'main' into pavolum/addNodeModal
benbrown Mar 26, 2021
4c80ac2
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 1, 2021
c1b70ed
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 5, 2021
8a8f3af
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 7, 2021
b8bfb57
Moving node check to creation flow as opposed to app start
pavolumMsft Apr 7, 2021
91f4b1e
Merge branch 'pavolum/addNodeModal' of https://github.com/microsoft/B…
pavolumMsft Apr 7, 2021
bf9dd12
Add isElectron check for node modal
pavolumMsft Apr 7, 2021
33746be
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 7, 2021
bf92a53
Merge branch 'main' into pavolum/addNodeModal
hatpick Apr 7, 2021
40d83d3
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 7, 2021
cf25d17
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 7, 2021
cbe6c30
Merge branch 'main' into pavolum/addNodeModal
cwhitten Apr 8, 2021
8a236d3
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 8, 2021
f109491
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 8, 2021
c782b58
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 8, 2021
6a14762
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 8, 2021
7dd1163
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 8, 2021
7ebbd46
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 8, 2021
d3cd444
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 8, 2021
56028de
Merge branch 'main' into pavolum/addNodeModal
pavolum Apr 8, 2021
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
4 changes: 3 additions & 1 deletion Composer/packages/client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ const Logger = () => {

export const App: React.FC = () => {
const { appLocale } = useRecoilValue(userSettingsState);
const { fetchExtensions, fetchFeatureFlags } = useRecoilValue(dispatcherState);

const { fetchExtensions, fetchFeatureFlags, checkNodeVersion } = useRecoilValue(dispatcherState);

useEffect(() => {
loadLocale(appLocale);
}, [appLocale]);

useEffect(() => {
checkNodeVersion();
fetchExtensions();
fetchFeatureFlags();
}, []);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ import { DialogWrapper, DialogTypes } from '@bfc/ui-shared';
import { navigate, RouteComponentProps } from '@reach/router';
import querystring from 'query-string';
import axios from 'axios';
import { useRecoilValue } from 'recoil';

import { DialogCreationCopy } from '../../../constants';
import { getAliasFromPayload } from '../../../utils/electronUtil';
import { getAliasFromPayload, isElectron } from '../../../utils/electronUtil';
import { userHasNodeInstalledState } from '../../../recoilModel';

import { CreateBotV2 } from './CreateBot';
import { NodeModal } from './NodeModal';

// -------------------- CreateOptions -------------------- //
type CreateOptionsProps = {
Expand All @@ -36,6 +39,8 @@ export function CreateOptionsV2(props: CreateOptionsProps) {
const [option, setOption] = useState('Create');
const [isOpenCreateModal, setIsOpenCreateModal] = useState(false);
const { templates, onDismiss, onNext, onJumpToOpenModal, fetchTemplates, fetchReadMe } = props;
const [showNodeModal, setShowNodeModal] = useState(false);
const userHasNode = useRecoilValue(userHasNodeInstalledState);

useEffect(() => {
// open bot directly if alias exist.
Expand Down Expand Up @@ -100,6 +105,12 @@ export function CreateOptionsV2(props: CreateOptionsProps) {
}
};

useEffect(() => {
if (!userHasNode) {
setShowNodeModal(true);
}
}, [userHasNode]);

return (
<Fragment>
<DialogWrapper
Expand All @@ -124,6 +135,7 @@ export function CreateOptionsV2(props: CreateOptionsProps) {
onDismiss={onDismiss}
onNext={onNext}
/>
{isElectron() && showNodeModal && <NodeModal isOpen={showNodeModal} setIsOpen={setShowNodeModal} />}
</Fragment>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* eslint-disable react/no-danger */
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

/** @jsx jsx */
import { DialogTypes, DialogWrapper } from '@bfc/ui-shared/lib/components/DialogWrapper';
import { jsx } from '@emotion/core';
import formatMessage from 'format-message';
import { DefaultButton, PrimaryButton } from 'office-ui-fabric-react/lib/components/Button';
import { DialogFooter } from 'office-ui-fabric-react/lib/components/Dialog';
import React from 'react';
import { Text } from 'office-ui-fabric-react/lib/Text';
import { mergeStyles } from 'office-ui-fabric-react/lib/Styling';

const dialogFooterClass = mergeStyles({
marginTop: '25px',
});

type NodeModalProps = {
setIsOpen: Function;
isOpen: boolean;
};

export const NodeModal: React.FC<NodeModalProps> = (props) => {
return (
<DialogWrapper
dialogType={DialogTypes.DesignFlow}
isOpen={props.isOpen}
title="Node.js required"
onDismiss={() => {
props.setIsOpen(false);
}}
>
<Text>
{formatMessage(
'Bot Framework Composer requires Node.js in order to create and run a new bot. Click “Install Node.js” to install the latest version'
)}
</Text>
<DialogFooter className={dialogFooterClass}>
<PrimaryButton
data-testid="InstallNode"
href="https://nodejs.org/en/download/"
target="_blank"
text={formatMessage('Install Node.js')}
/>
<DefaultButton
text={formatMessage('Cancel')}
onClick={() => {
props.setIsOpen(false);
}}
/>
</DialogFooter>
</DialogWrapper>
);
};
5 changes: 5 additions & 0 deletions Composer/packages/client/src/recoilModel/atoms/appState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,8 @@ export const isWebChatPanelVisibleState = atom<boolean>({
key: getFullyQualifiedKey('isWebChatPanelVisible'),
default: false,
});

export const userHasNodeInstalledState = atom<boolean>({
key: getFullyQualifiedKey('userHasNodeInstalled'),
default: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import { CallbackInterface, useRecoilCallback } from 'recoil';
import debounce from 'lodash/debounce';
import formatMessage from 'format-message';

import {
appUpdateState,
Expand All @@ -15,11 +16,14 @@ import {
pageElementState,
debugPanelExpansionState,
debugPanelActiveTabState,
userHasNodeInstalledState,
applicationErrorState,
} from '../atoms/appState';
import { AppUpdaterStatus, CreationFlowStatus, CreationFlowType } from '../../constants';
import OnboardingState from '../../utils/onboardingStorage';
import { StateError, AppUpdateState } from '../../recoilModel/types';
import { DebugDrawerKeys } from '../../pages/design/DebugPanel/TabExtensions/types';
import httpClient from '../../utils/httpUtil';

import { setError } from './shared';

Expand Down Expand Up @@ -130,7 +134,21 @@ export const applicationDispatcher = () => {
}
);

const checkNodeVersion = useRecoilCallback(({ set }: CallbackInterface) => async () => {
try {
const response = await httpClient.get(`/utilities/checkNode`);
const userHasNode = response.data?.userHasNode;
set(userHasNodeInstalledState, userHasNode);
} catch (err) {
set(applicationErrorState, {
message: formatMessage('Error checking node version'),
summary: err.message,
});
}
});

return {
checkNodeVersion,
setAppUpdateStatus,
setAppUpdateShowing,
setAppUpdateError,
Expand Down
24 changes: 24 additions & 0 deletions Composer/packages/server/src/controllers/utilities.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { exec } from 'child_process';
import { promisify } from 'util';

import { Request, Response } from 'express';

import { parseQnAContent } from '../models/utilities/parser';
const execAsync = promisify(exec);

async function getQnaContent(req: Request, res: Response) {
try {
Expand All @@ -16,6 +20,26 @@ async function getQnaContent(req: Request, res: Response) {
}
}

async function checkNodeVersion(req: Request, res: Response) {
try {
const command = 'node -v';
const { stderr: checkNodeError, stdout: nodeVersion } = await execAsync(command);
if (checkNodeError) {
throw new Error();
} else {
res.status(200).json({
userHasNode: true,
nodeVersion: nodeVersion,
});
}
} catch (e) {
res.status(200).json({
userHasNode: false,
});
}
}

export const UtilitiesController = {
getQnaContent,
checkNodeVersion,
};
15 changes: 15 additions & 0 deletions Composer/packages/server/src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,15 @@
"bot_framework_composer_is_a_visual_authoring_canva_c3947d91": {
"message": "Bot Framework Composer is a visual authoring canvas for building bots and other types of conversational application with the Microsoft Bot Framework technology stack. With Composer you will find everything you need to build a modern, state-of-the-art conversational experience."
},
"bot_framework_composer_is_an_open_source_visual_au_2be2e02b": {
"message": "Bot Framework Composer is an open-source visual authoring canvas for developers and multi-disciplinary teams to build bots. Composer integrates LUIS and QnA Maker, and allows sophisticated composition of bot replies using language generation."
},
"bot_framework_composer_requires_node_js_in_order_t_a1d3dfb": {
"message": "Bot Framework Composer requires Node.js in order to run. Click “Install Node.js” to install the latest version"
},
"bot_framework_provides_the_most_comprehensive_expe_e34a7f5d": {
"message": "Bot Framework provides the most comprehensive experience for building conversational applications."
},
"bot_framework_emulator_fefd4a59": {
"message": "Bot Framework Emulator"
},
Expand Down Expand Up @@ -1448,6 +1457,9 @@
"error_afac7133": {
"message": "Error:"
},
"error_checking_node_version_98bfbf4c": {
"message": "Error checking node version"
},
"error_encountered_when_getting_template_readme_17dcbc61": {
"message": "### Error encountered when getting template readMe"
},
Expand Down Expand Up @@ -1880,6 +1892,9 @@
"install_more_adapters_in_a_the_package_manager_a_156fb028": {
"message": "Install more adapters in <a>the package manager</a>."
},
"install_node_js_1857298c": {
"message": "Install Node.js"
},
"install_pre_release_versions_of_composer_daily_to__ceb41b54": {
"message": "Install pre-release versions of Composer, daily, to access and test the latest features. <a>Learn more</a>."
},
Expand Down
2 changes: 2 additions & 0 deletions Composer/packages/server/src/router/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ router.use('/assets/locales/', express.static(path.join(__dirname, '..', '..', '

//help api
router.get('/utilities/qna/parse', UtilitiesController.getQnaContent);
router.get('/utilities/checkNode', UtilitiesController.checkNodeVersion);

// extensions
router.get('/extensions', ExtensionsController.listExtensions);
router.post('/extensions', ExtensionsController.addExtension);
Expand Down