diff --git a/Dockerfile b/Dockerfile index a851a020..f627b73c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,4 +25,4 @@ RUN npm install COPY . . RUN npm run build EXPOSE $PORT -ENTRYPOINT node server.js --path ./dist --port $PORT --env "{\"EDDI_API_URL\":\"$EDDI_API_URL\", \"AUTH_METHOD\":\"$AUTH_METHOD\", \"AUTH_URL\":\"$AUTH_URL\", \"AUTH_REALM\":\"$AUTH_REALM\", \"AUTH_CLIENT_ID\":\"$AUTH_CLIENT_ID\", \"READ_ONLY_DOMAIN\":\"$READ_ONLY\"}" +ENTRYPOINT node server.js --path ./dist --port $PORT --env "{\"EDDI_API_URL\":\"$EDDI_API_URL\", \"AUTH_METHOD\":\"$AUTH_METHOD\", \"AUTH_URL\":\"$AUTH_URL\", \"AUTH_REALM\":\"$AUTH_REALM\", \"AUTH_CLIENT_ID\":\"$AUTH_CLIENT_ID\", \"READ_ONLY_DOMAIN\":\"$READ_ONLY_DOMAIN\"}" diff --git a/src/scripts/actions/EddiApiActionDispatchers.ts b/src/scripts/actions/EddiApiActionDispatchers.ts index b5d6c974..eb93880a 100644 --- a/src/scripts/actions/EddiApiActionDispatchers.ts +++ b/src/scripts/actions/EddiApiActionDispatchers.ts @@ -114,6 +114,12 @@ import { endConversationAction, endConversationSuccessAction, endConversationFailedAction, + IDeployExampleBotsAction, + IDeployExampleBotsSuccessAction, + IDeployExampleBotsFailedAction, + deployExampleBotsAction, + deployExampleBotsFailedAction, + deployExampleBotsSuccessAction, } from './EddiApiActions'; export interface IEddiApiActionDispatchers extends ActionCreatorsMapObject { @@ -230,6 +236,9 @@ export interface IEddiApiActionDispatchers extends ActionCreatorsMapObject { conversationId, ) => IEndConversationSuccessAction; endConversationFailedAction: (conversationId) => IEndConversationFailedAction; + deployExampleBotsAction: () => IDeployExampleBotsAction; + deployExampleBotsSuccessAction: (bots) => IDeployExampleBotsSuccessAction; + deployExampleBotsFailedAction: (error) => IDeployExampleBotsFailedAction; } const actions: IEddiApiActionDispatchers = { @@ -294,6 +303,9 @@ const actions: IEddiApiActionDispatchers = { endConversationAction, endConversationSuccessAction, endConversationFailedAction, + deployExampleBotsAction, + deployExampleBotsFailedAction, + deployExampleBotsSuccessAction, }; const eddiApiActionDispatchers: IEddiApiActionDispatchers = bindActionCreators< diff --git a/src/scripts/actions/EddiApiActionTypes.ts b/src/scripts/actions/EddiApiActionTypes.ts index 5e2b6108..690e5cbe 100644 --- a/src/scripts/actions/EddiApiActionTypes.ts +++ b/src/scripts/actions/EddiApiActionTypes.ts @@ -149,3 +149,8 @@ export const FETCH_CONVERSATION_FAILED: string = 'FETCH_CONVERSATION_FAILED'; export const END_CONVERSATION: string = 'END_CONVERSATION'; export const END_CONVERSATION_SUCCESS: string = 'END_CONVERSATION_SUCCESS'; export const END_CONVERSATION_FAILED: string = 'END_CONVERSATION_FAILED'; + +export const DEPLOY_EXAMPLE_BOTS: string = 'DEPLOY_EXAMPLE_BOTS'; +export const DEPLOY_EXAMPLE_BOTS_SUCCESS: string = + 'DEPLOY_EXAMPLE_BOTS_SUCCESS'; +export const DEPLOY_EXAMPLE_BOTS_FAILED: string = 'DEPLOY_EXAMPLE_BOTS_FAILED'; diff --git a/src/scripts/actions/EddiApiActions.ts b/src/scripts/actions/EddiApiActions.ts index a08700ce..13f0d0ad 100644 --- a/src/scripts/actions/EddiApiActions.ts +++ b/src/scripts/actions/EddiApiActions.ts @@ -95,6 +95,9 @@ import { END_CONVERSATION, END_CONVERSATION_SUCCESS, END_CONVERSATION_FAILED, + DEPLOY_EXAMPLE_BOTS, + DEPLOY_EXAMPLE_BOTS_SUCCESS, + DEPLOY_EXAMPLE_BOTS_FAILED, } from './EddiApiActionTypes'; import { IBot, @@ -1444,3 +1447,37 @@ export function endConversationFailedAction( type: END_CONVERSATION_FAILED, }; } + +export interface IDeployExampleBotsAction extends Action {} + +export function deployExampleBotsAction(): IDeployExampleBotsAction { + return { + type: DEPLOY_EXAMPLE_BOTS, + }; +} + +export interface IDeployExampleBotsSuccessAction extends Action { + bots: IBot[]; +} + +export function deployExampleBotsSuccessAction( + bots: IBot[], +): IDeployExampleBotsSuccessAction { + return { + bots, + type: DEPLOY_EXAMPLE_BOTS_SUCCESS, + }; +} + +export interface IDeployExampleBotsFailedAction extends Action { + error: Error; +} + +export function deployExampleBotsFailedAction( + error, +): IDeployExampleBotsFailedAction { + return { + error, + type: DEPLOY_EXAMPLE_BOTS_FAILED, + }; +} diff --git a/src/scripts/components/Bots/Botlist.styles.ts b/src/scripts/components/Bots/Botlist.styles.ts index 2d350113..edf7d539 100644 --- a/src/scripts/components/Bots/Botlist.styles.ts +++ b/src/scripts/components/Bots/Botlist.styles.ts @@ -8,5 +8,9 @@ const styles: CSSProperties = { height: '500px', justifyContent: 'center', }, + deployExampleBotsButton: { + marginTop: '5px', + width: '160px', + }, }; export default styles; diff --git a/src/scripts/components/Bots/Botlist.tsx b/src/scripts/components/Bots/Botlist.tsx index f5af0c15..b79a02bf 100644 --- a/src/scripts/components/Bots/Botlist.tsx +++ b/src/scripts/components/Bots/Botlist.tsx @@ -5,7 +5,7 @@ import * as _ from 'lodash'; import * as Radium from 'radium'; import { Component, compose, pure, setDisplayName } from 'recompose'; import eddiApiActionDispatchers from '../../actions/EddiApiActionDispatchers'; -import { IBot } from '../utils/AxiosFunctions'; +import { deployExampleBots, IBot } from '../utils/AxiosFunctions'; import { connect } from 'react-redux'; import { botsSelector } from '../../selectors/BotSelectors'; import styles from './Botlist.styles'; @@ -13,6 +13,7 @@ import { ClimbingBoxLoader } from 'react-spinners'; import { DEFAULT_LIMIT, getAPIUrl } from '../utils/ApiFunctions'; import * as InfiniteScrollTypes from 'react-infinite-scroller'; const InfiniteScroll = require('react-infinite-scroller') as InfiniteScrollTypes; +import BlueButton from '../Assets/Buttons/BlueButton'; interface IPublicProps { filterText: string; @@ -41,7 +42,9 @@ class BotList extends React.Component { } async componentDidMount() { - this.loadMore(); + if (!this.props.isLoading) { + this.loadMore(); + } this.setState({ apiUrl: await getAPIUrl() }); } @@ -99,7 +102,18 @@ class BotList extends React.Component { !this.props.error && !this.props.isLoading && _.isEmpty(this.props.bots), - )(() =>

{`There are no bots yet`}

)} + )(() => ( +
+
{`There are no bots yet..`}
+ + eddiApiActionDispatchers.deployExampleBotsAction() + } + text={'Deploy Example Bots'} + /> +
+ ))} {renderIf(!this.props.error && !_.isEmpty(this.props.bots))(() => (
{renderIf(_.isEmpty(botList))(() => ( diff --git a/src/scripts/components/utils/AxiosFunctions.tsx b/src/scripts/components/utils/AxiosFunctions.tsx index f2fcb0a7..eed8bd4b 100644 --- a/src/scripts/components/utils/AxiosFunctions.tsx +++ b/src/scripts/components/utils/AxiosFunctions.tsx @@ -1138,3 +1138,32 @@ export async function endConversation(conversationId: string) { console.error(`Failed to end conversation. Error: ${err.message}`); } } + +interface IExampleBotsResponseData { + environment: string; + botId: string; + botVersion: number; + status: string; + descriptor: IDescriptor; +} + +export async function deployExampleBots(): Promise { + try { + const response = await axios.post( + `${await getAPIUrl()}/backup/import/examples`, + ); + const exampleBots: IExampleBotsResponseData[] = response.data; + return exampleBots.map(bot => { + return { + ...bot.descriptor, + environment: bot.environment, + id: bot.botId, + version: bot.botVersion, + currentVersion: bot.botVersion, + deploymentStatus: bot.status, + }; + }); + } catch (err) { + console.error(`Failed to deploy example bots. Error: ${err.message}`); + } +} diff --git a/src/scripts/reducers/BotReducer.ts b/src/scripts/reducers/BotReducer.ts index 4ed40aae..f8e85a53 100644 --- a/src/scripts/reducers/BotReducer.ts +++ b/src/scripts/reducers/BotReducer.ts @@ -21,6 +21,9 @@ import { FETCH_BOT_JSON_SCHEMA_SUCCESS, DUPLICATE_SUCCESS, FETCH_CONVERSATIONS_SUCCESS, + DEPLOY_EXAMPLE_BOTS, + DEPLOY_EXAMPLE_BOTS_FAILED, + DEPLOY_EXAMPLE_BOTS_SUCCESS, } from '../actions/EddiApiActionTypes'; import * as update from 'immutability-helper'; import { @@ -41,6 +44,8 @@ import { IFetchJsonSchemaSuccessAction, IDuplicateSuccessAction, IFetchConversationsSuccessAction, + IDeployExampleBotsFailedAction, + IDeployExampleBotsSuccessAction, } from '../actions/EddiApiActions'; import * as _ from 'lodash'; import { JSONSchema4 } from 'json-schema'; @@ -432,6 +437,39 @@ const BotReducer: IBotReducer = ( }); } + case DEPLOY_EXAMPLE_BOTS: { + return update(state, { + isLoadingAllBots: { + $set: true, + }, + }); + } + + case DEPLOY_EXAMPLE_BOTS_FAILED: { + return update(state, { + isLoadingAllBots: { + $set: false, + }, + error: { + $set: (action as IDeployExampleBotsFailedAction).error, + }, + }); + } + + case DEPLOY_EXAMPLE_BOTS_SUCCESS: { + return update(state, { + isLoadingAllBots: { + $set: false, + }, + bots: { + $set: (action as IDeployExampleBotsSuccessAction).bots, + }, + botsLoaded: { + $set: (action as IDeployExampleBotsSuccessAction).bots.length, + }, + }); + } + default: return state; } diff --git a/src/scripts/sagas/EddiApiSaga.ts b/src/scripts/sagas/EddiApiSaga.ts index 351674cc..87406cd7 100644 --- a/src/scripts/sagas/EddiApiSaga.ts +++ b/src/scripts/sagas/EddiApiSaga.ts @@ -32,6 +32,7 @@ import { FETCH_CONVERSATIONS, FETCH_CONVERSATION, END_CONVERSATION, + DEPLOY_EXAMPLE_BOTS, } from '../actions/EddiApiActionTypes'; import { getPackage, @@ -73,6 +74,7 @@ import { IConversationData, getConversation, endConversation as axiosEndConversation, + deployExampleBots as axiosDeployExampleBots, } from '../components/utils/AxiosFunctions'; import { fetchBotFailedAction, @@ -166,6 +168,9 @@ import { IEndConversationAction, endConversationSuccessAction, endConversationFailedAction, + IDeployExampleBotsAction, + deployExampleBotsFailedAction, + deployExampleBotsSuccessAction, } from '../actions/EddiApiActions'; import * as Edditypes from '../components/utils/EddiTypes'; import Parser from '../components/utils/Parser'; @@ -794,3 +799,18 @@ export function* endConversation(action: IEndConversationAction): Iterator<{}> { export function* watchEndConversation(): Iterator<{}> { yield takeEvery(END_CONVERSATION, endConversation); } + +export function* deployExampleBots( + action: IDeployExampleBotsAction, +): Iterator<{}> { + try { + const bots: IBot[] = yield call(axiosDeployExampleBots); + yield put(deployExampleBotsSuccessAction(bots)); + } catch (err) { + yield put(deployExampleBotsFailedAction(err)); + } +} + +export function* watchDeployExampleBots(): Iterator<{}> { + yield takeEvery(DEPLOY_EXAMPLE_BOTS, deployExampleBots); +} diff --git a/src/scripts/sagas/index.ts b/src/scripts/sagas/index.ts index 1a245d1a..f806a3ee 100644 --- a/src/scripts/sagas/index.ts +++ b/src/scripts/sagas/index.ts @@ -34,6 +34,7 @@ import { watchFetchConversations, watchFetchConversation, watchEndConversation, + watchDeployExampleBots, } from './EddiApiSaga'; import { watchBasicAuthSignIn, @@ -81,6 +82,7 @@ function* root() { yield fork(watchKeycloakRefreshToken); yield fork(watchCheckAuthentication); yield fork(watchSignOut); + yield fork(watchDeployExampleBots); } export const run: () => Task = () => sagaMiddleware.run(root);