fix: Add running bots tab. (#56)
* fix: Add bots resolver. * minot fix. * test commit. * add bot kill * Integration. * add refetch. * Lint. * Formatting fix. * Better error handling.
This commit is contained in:
parent
801d54f0c5
commit
3c731b3b2b
@ -30,9 +30,11 @@
|
||||
"lerna": "^3.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dxos/cli": "^2.0.8",
|
||||
"@dxos/cli-app": "^2.0.8",
|
||||
"@dxos/cli-wns": "^2.0.8",
|
||||
"@dxos/cli": "2.0.20-alpha.0",
|
||||
"@dxos/cli-app": "2.0.20-alpha.0",
|
||||
"@dxos/cli-bot": "2.0.20-alpha.0",
|
||||
"@dxos/cli-data": "2.0.20-alpha.0",
|
||||
"@dxos/cli-wns": "2.0.20-alpha.0",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-config-semistandard": "^15.0.0",
|
||||
|
@ -11,7 +11,7 @@ app:
|
||||
publicUrl: '/console'
|
||||
|
||||
api:
|
||||
server: 'https://alpha.kube.moon.dxos.network'
|
||||
server: 'https://apollo1.kube.moon.dxos.network'
|
||||
path: '/api'
|
||||
intervalLog: 5000
|
||||
pollInterval: 10000
|
||||
@ -22,19 +22,19 @@ system:
|
||||
services:
|
||||
app:
|
||||
prefix: '/app'
|
||||
server: 'https://alpha.kube.moon.dxos.network'
|
||||
server: 'https://apollo1.kube.moon.dxos.network'
|
||||
|
||||
wns:
|
||||
server: 'https://alpha.kube.moon.dxos.network/dxos/wns/api'
|
||||
webui: 'https://alpha.kube.moon.dxos.network/dxos/wns/console'
|
||||
server: 'https://apollo1.kube.moon.dxos.network/dxos/wns/api'
|
||||
webui: 'https://apollo1.kube.moon.dxos.network/dxos/wns/console'
|
||||
|
||||
signal:
|
||||
server: 'wss://alpha.kube.moon.dxos.network/dxos/signal'
|
||||
api: 'https://alpha.kube.moon.dxos.network/dxos/signal/api'
|
||||
server: 'wss://apollo1.kube.moon.dxos.network/dxos/signal'
|
||||
api: 'https://apollo1.kube.moon.dxos.network/dxos/signal/api'
|
||||
|
||||
ipfs:
|
||||
server: 'https://alpha.kube.moon.dxos.network/dxos/ipfs/api'
|
||||
gateway: 'https://alpha.kube.moon.dxos.network/dxos/ipfs/gateway'
|
||||
server: 'https://apollo1.kube.moon.dxos.network/dxos/ipfs/api'
|
||||
gateway: 'https://apollo1.kube.moon.dxos.network/dxos/ipfs/gateway'
|
||||
|
||||
wellknown:
|
||||
endpoint: 'https://alpha.kube.moon.dxos.network/.well-known/dxos'
|
||||
endpoint: 'https://apollo1.kube.moon.dxos.network/.well-known/dxos'
|
||||
|
40
packages/console-app/src/components/BotControls.js
Normal file
40
packages/console-app/src/components/BotControls.js
Normal file
@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright 2020 DXOS.org
|
||||
//
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import StopIcon from '@material-ui/icons/HighlightOff';
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
stop: {
|
||||
color: theme.palette.error.main
|
||||
}
|
||||
}));
|
||||
|
||||
const BotControls = ({ onStop }) => {
|
||||
const [stopPressed, setStopPressed] = useState(false);
|
||||
const classes = useStyles();
|
||||
|
||||
const stopBot = () => {
|
||||
if (!stopPressed) {
|
||||
setStopPressed(true);
|
||||
onStop();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{onStop && (
|
||||
<IconButton onClick={stopBot} title='Stop'>
|
||||
{!stopPressed && (<StopIcon className={classes.stop} />)}
|
||||
{stopPressed && (<CircularProgress className={classes.stop} size={24} />)}
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BotControls;
|
@ -26,7 +26,7 @@ const getLogBuffer = (name) => {
|
||||
const LogPoller = ({ service }) => {
|
||||
const { config } = useContext(ConsoleContext);
|
||||
const logBuffer = getLogBuffer(service);
|
||||
const data = useQueryStatusReducer(useQuery(LOGS, {
|
||||
const { data } = useQueryStatusReducer(useQuery(LOGS, {
|
||||
pollInterval: config.api.intervalLog,
|
||||
variables: { service, incremental: logBuffer.length !== 0 }
|
||||
}));
|
||||
|
@ -30,8 +30,8 @@ const useStyles = makeStyles(theme => ({
|
||||
const VersionCheck = () => {
|
||||
const classes = useStyles();
|
||||
const [{ current, latest }, setUpgrade] = useState({});
|
||||
const statusResponse = useQueryStatusReducer(useQuery(SYSTEM_STATUS));
|
||||
const wnsResponse = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
const { data: statusResponse } = useQueryStatusReducer(useQuery(SYSTEM_STATUS));
|
||||
const { data: wnsResponse } = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
pollInterval: CHECK_INTERVAL,
|
||||
variables: { attributes: { type: 'wrn:resource' } }
|
||||
}));
|
||||
|
@ -24,13 +24,13 @@ import AppLink from '../../../components/AppLink';
|
||||
const AppRecords = () => {
|
||||
const { config } = useContext(ConsoleContext);
|
||||
const [sorter, sortBy] = useSorter('createTime', false);
|
||||
const appResponse = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
const { data: appResponse } = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
pollInterval: config.api.intervalQuery,
|
||||
variables: { attributes: { type: 'wrn:app' } }
|
||||
}));
|
||||
|
||||
// TODO(telackey): Does this also need an interval?
|
||||
const ipfsResponse = useQueryStatusReducer(useQuery(IPFS_STATUS));
|
||||
const { data: ipfsResponse } = useQueryStatusReducer(useQuery(IPFS_STATUS));
|
||||
if (!appResponse || !ipfsResponse) {
|
||||
return null;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import moment from 'moment';
|
||||
const BotRecords = () => {
|
||||
const { config } = useContext(ConsoleContext);
|
||||
const [sorter, sortBy] = useSorter('createTime', false);
|
||||
const data = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
const { data } = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
pollInterval: config.api.intervalQuery,
|
||||
variables: { attributes: { type: 'wrn:bot' } }
|
||||
}));
|
||||
|
@ -12,9 +12,11 @@ import Toolbar from '../../../components/Toolbar';
|
||||
|
||||
import BotRecords from './BotRecords';
|
||||
import LogPoller from '../../../components/LogPoller';
|
||||
import RunningBots from './RunningBots';
|
||||
|
||||
const TAB_RECORDS = 'records';
|
||||
const TAB_LOG = 'log';
|
||||
const TAB_DATA = 'running bots';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {}
|
||||
@ -32,6 +34,7 @@ const Bots = () => {
|
||||
<Tabs value={tab} onChange={(_, value) => setTab(value)}>
|
||||
<Tab value={TAB_RECORDS} label='Records' />
|
||||
<Tab value={TAB_LOG} label='Log' />
|
||||
<Tab value={TAB_DATA} label='Running Bots' />
|
||||
</Tabs>
|
||||
</Toolbar>
|
||||
}
|
||||
@ -43,6 +46,9 @@ const Bots = () => {
|
||||
{tab === TAB_LOG && (
|
||||
<LogPoller service='bot-factory' />
|
||||
)}
|
||||
{tab === TAB_DATA && (
|
||||
<RunningBots />
|
||||
)}
|
||||
</Panel>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,84 @@
|
||||
//
|
||||
// Copyright 2020 DXOS.org
|
||||
//
|
||||
|
||||
import moment from 'moment';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useQuery, useMutation } from '@apollo/react-hooks';
|
||||
import TableHead from '@material-ui/core/TableHead';
|
||||
import TableRow from '@material-ui/core/TableRow';
|
||||
import TableBody from '@material-ui/core/TableBody';
|
||||
|
||||
import BOT_LIST from '../../../gql/bot_list.graphql';
|
||||
import BOT_KILL from '../../../gql/bot_kill.graphql';
|
||||
|
||||
import { useQueryStatusReducer, useStatusReducer, useSorter } from '../../../hooks';
|
||||
|
||||
import BotControls from '../../../components/BotControls';
|
||||
import Table from '../../../components/Table';
|
||||
import TableCell from '../../../components/TableCell';
|
||||
|
||||
const RunningBots = () => {
|
||||
const [sorter, sortBy] = useSorter('started', false);
|
||||
const [botList, setBotList] = useState([]);
|
||||
const [, setStatus] = useStatusReducer();
|
||||
|
||||
const { data: botListResponse, refetch } = useQueryStatusReducer(useQuery(BOT_LIST));
|
||||
|
||||
useEffect(() => {
|
||||
if (botListResponse) {
|
||||
const { error, bots = [] } = JSON.parse(botListResponse.bot_list.json);
|
||||
if (error) {
|
||||
setStatus({ error });
|
||||
}
|
||||
setBotList(bots);
|
||||
}
|
||||
}, [botListResponse]);
|
||||
|
||||
const [killBot] = useMutation(BOT_KILL);
|
||||
|
||||
const onKillBot = async (botId) => {
|
||||
const botKillResponse = await killBot({ variables: { botId } });
|
||||
if (botKillResponse && botKillResponse.data) {
|
||||
const { error } = JSON.parse(botKillResponse.data.bot_kill.json);
|
||||
if (error) {
|
||||
setStatus({ error });
|
||||
} else {
|
||||
refetch();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell onClick={sortBy('id')}>Identifier</TableCell>
|
||||
<TableCell onClick={sortBy('botId')} size='small'>Bot Id</TableCell>
|
||||
<TableCell onClick={sortBy('started')}>Started</TableCell>
|
||||
<TableCell onClick={sortBy('stopped')}>Stopped</TableCell>
|
||||
<TableCell onClick={sortBy('parties')} size='small'>Parties</TableCell>
|
||||
<TableCell size='icon' />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{botList.sort(sorter).map(({ id, botId, started, stopped, parties }) => {
|
||||
return (
|
||||
<TableRow key={botId} size='small'>
|
||||
<TableCell monospace>{id}</TableCell>
|
||||
<TableCell monospace>{botId}</TableCell>
|
||||
<TableCell>{moment.utc(started).fromNow()}</TableCell>
|
||||
<TableCell monospace>{String(stopped)}</TableCell>
|
||||
<TableCell monospace>{parties && parties.map(partyId => <div key={partyId}>{partyId}</div>)}</TableCell>
|
||||
<TableCell monospace>
|
||||
<BotControls onStop={() => onKillBot(botId)} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
export default RunningBots;
|
@ -68,8 +68,8 @@ const useStyles = makeStyles((theme) => ({
|
||||
const IPFSStatus = () => {
|
||||
const classes = useStyles();
|
||||
|
||||
const ipfsResponse = useQueryStatusReducer(useQuery(IPFS_STATUS));
|
||||
const wnsResponse = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
const { data: ipfsResponse } = useQueryStatusReducer(useQuery(IPFS_STATUS));
|
||||
const { data: wnsResponse } = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
variables: { attributes: { type: RECORD_TYPE, service: SERVICE_TYPE } }
|
||||
}));
|
||||
|
||||
|
@ -17,8 +17,8 @@ const RECORD_TYPE = 'wrn:service';
|
||||
const SERVICE_TYPE = 'ipfs';
|
||||
|
||||
const IPFSStatus = () => {
|
||||
const ipfsResponse = useQueryStatusReducer(useQuery(IPFS_STATUS));
|
||||
const wnsResponse = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
const { data: ipfsResponse } = useQueryStatusReducer(useQuery(IPFS_STATUS));
|
||||
const { data: wnsResponse } = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
variables: { attributes: { type: RECORD_TYPE, service: SERVICE_TYPE } }
|
||||
}));
|
||||
|
||||
|
@ -21,7 +21,7 @@ import AppLink from '../../../components/AppLink';
|
||||
const KubeRecords = () => {
|
||||
const { config } = useContext(ConsoleContext);
|
||||
const [sorter, sortBy] = useSorter('names[0]');
|
||||
const appResponse = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
const { data: appResponse } = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
pollInterval: config.api.intervalQuery,
|
||||
variables: { attributes: { type: 'wrn:kube' } }
|
||||
}));
|
||||
|
@ -74,7 +74,7 @@ const RegistryLookup = ({ scope }) => {
|
||||
const [result, setResult] = useState({});
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
|
||||
const data = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
const { data } = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
pollInterval: config.api.intervalQuery
|
||||
}));
|
||||
|
||||
|
@ -67,7 +67,7 @@ export const RecordType = ({ type = types[0].key, onChange }) => {
|
||||
const RegistryRecords = ({ type }) => {
|
||||
const { config } = useContext(ConsoleContext);
|
||||
const [sorter, sortBy] = useSorter('createTime', false);
|
||||
const data = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
const { data } = useQueryStatusReducer(useQuery(WNS_RECORDS, {
|
||||
pollInterval: config.api.intervalQuery,
|
||||
variables: { attributes: { type } }
|
||||
}));
|
||||
|
@ -13,7 +13,7 @@ import Json from '../../../components/Json';
|
||||
|
||||
const RegistryStatus = () => {
|
||||
const { config } = useContext(ConsoleContext);
|
||||
const data = useQueryStatusReducer(useQuery(WNS_STATUS, { pollInterval: config.api.intervalQuery }));
|
||||
const { data } = useQueryStatusReducer(useQuery(WNS_STATUS, { pollInterval: config.api.intervalQuery }));
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import { ConsoleContext, useQueryStatusReducer } from '../../../hooks';
|
||||
|
||||
const SignalChannels = () => {
|
||||
const { config } = useContext(ConsoleContext);
|
||||
const data = useQueryStatusReducer(useQuery(SIGNAL_STATUS, { fetchPolicy: 'no-cache', pollInterval: config.api.pollInterval, context: { api: 'signal' } }));
|
||||
const { data } = useQueryStatusReducer(useQuery(SIGNAL_STATUS, { fetchPolicy: 'no-cache', pollInterval: config.api.pollInterval, context: { api: 'signal' } }));
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ function Row (props) {
|
||||
|
||||
function SignalServers () {
|
||||
const { config } = useContext(ConsoleContext);
|
||||
const response = useQueryStatusReducer(useQuery(SIGNAL_STATUS, { fetchPolicy: 'no-cache', pollInterval: config.api.pollInterval, context: { api: 'signal' } }));
|
||||
const { data: response } = useQueryStatusReducer(useQuery(SIGNAL_STATUS, { fetchPolicy: 'no-cache', pollInterval: config.api.pollInterval, context: { api: 'signal' } }));
|
||||
|
||||
const data = useDataGraph(response);
|
||||
|
||||
|
@ -13,7 +13,7 @@ import { ConsoleContext, useQueryStatusReducer } from '../../../hooks';
|
||||
|
||||
const Info = () => {
|
||||
const { config } = useContext(ConsoleContext);
|
||||
const systemResponse = useQueryStatusReducer(useQuery(SYSTEM_STATUS, { pollInterval: config.api.intervalQuery }));
|
||||
const { data: systemResponse } = useQueryStatusReducer(useQuery(SYSTEM_STATUS, { pollInterval: config.api.intervalQuery }));
|
||||
if (!systemResponse) {
|
||||
return null;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ const format = (value, unit, symbol = '') => Math.floor(value / unit).toLocaleSt
|
||||
const SignalServers = () => {
|
||||
const { config } = useContext(ConsoleContext);
|
||||
const [sorter] = useSorter('name');
|
||||
const serviceResponse = useQueryStatusReducer(useQuery(SERVICE_STATUS, { pollInterval: config.api.intervalQuery }));
|
||||
const { data: serviceResponse } = useQueryStatusReducer(useQuery(SERVICE_STATUS, { pollInterval: config.api.intervalQuery }));
|
||||
if (!serviceResponse) {
|
||||
return null;
|
||||
}
|
||||
|
10
packages/console-app/src/gql/bot_kill.graphql
Normal file
10
packages/console-app/src/gql/bot_kill.graphql
Normal file
@ -0,0 +1,10 @@
|
||||
#
|
||||
# Copyright 2020 DXOS.org
|
||||
#
|
||||
|
||||
mutation ($botId: String!) {
|
||||
bot_kill(botId: $botId) {
|
||||
timestamp
|
||||
json
|
||||
}
|
||||
}
|
10
packages/console-app/src/gql/bot_list.graphql
Normal file
10
packages/console-app/src/gql/bot_list.graphql
Normal file
@ -0,0 +1,10 @@
|
||||
#
|
||||
# Copyright 2020 DXOS.org
|
||||
#
|
||||
|
||||
query {
|
||||
bot_list {
|
||||
timestamp
|
||||
json
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ export const useStatusReducer = () => {
|
||||
/**
|
||||
* Handle Apollo queries.
|
||||
*/
|
||||
export const useQueryStatusReducer = ({ loading, error, data }) => {
|
||||
export const useQueryStatusReducer = ({ loading, error, data, refetch }) => {
|
||||
const [, setStatus] = useStatusReducer();
|
||||
|
||||
useEffect(() => {
|
||||
@ -37,7 +37,7 @@ export const useQueryStatusReducer = ({ loading, error, data }) => {
|
||||
}
|
||||
}, [loading, error]);
|
||||
|
||||
return data;
|
||||
return { data, refetch };
|
||||
};
|
||||
|
||||
export const statusReducer = (state, action) => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"build": {
|
||||
"name": "@dxos/console-app",
|
||||
"buildDate": "2020-11-19T22:11:06.119Z",
|
||||
"version": "1.2.1-alpha.1"
|
||||
"buildDate": "2020-12-02T09:05:39.888Z",
|
||||
"version": "1.2.2-alpha.4"
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,7 @@
|
||||
"react-dom": "^16.13.1",
|
||||
"source-map-support": "^0.5.12",
|
||||
"systeminformation": "^4.26.5",
|
||||
"tree-kill": "^1.2.2",
|
||||
"yargs": "^15.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -21,8 +21,14 @@ type Query {
|
||||
signal_status: JSONResult!
|
||||
system_status: JSONResult!
|
||||
wns_status: JSONResult!
|
||||
bot_list: JSONResult!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
bot_kill(botId: String!): JSONResult!
|
||||
}
|
||||
|
||||
schema {
|
||||
query: Query
|
||||
mutation: Mutation
|
||||
}
|
||||
|
104
packages/console-server/src/resolvers/bots.js
Normal file
104
packages/console-server/src/resolvers/bots.js
Normal file
@ -0,0 +1,104 @@
|
||||
//
|
||||
// Copyright 2020 DXOS.org
|
||||
//
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import debug from 'debug';
|
||||
import fs from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import kill from 'tree-kill';
|
||||
|
||||
const DEFAULT_BOT_FACTORY_CWD = '.wire/bots';
|
||||
const SERVICE_CONFIG_FILENAME = 'service.yml';
|
||||
|
||||
const log = debug('dxos:console:server:resolvers');
|
||||
|
||||
const getBotFactoryTopic = (botFactoryCwd) => {
|
||||
// TODO(egorgripasov): Get topic from config or registry.
|
||||
const serviceFilePath = path.join(os.homedir(), botFactoryCwd || DEFAULT_BOT_FACTORY_CWD, SERVICE_CONFIG_FILENAME);
|
||||
if (fs.existsSync(serviceFilePath)) {
|
||||
const { topic } = yaml.safeLoad(fs.readFileSync(serviceFilePath));
|
||||
return topic;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const topic = getBotFactoryTopic();
|
||||
|
||||
const executeCommand = async (command, args, timeout = 10000) => {
|
||||
return new Promise((resolve) => {
|
||||
const child = spawn(command, args, { encoding: 'utf8' });
|
||||
|
||||
const stdout = [];
|
||||
const stderr = [];
|
||||
const timer = setTimeout(() => {
|
||||
try {
|
||||
kill(child.pid, 'SIGKILL');
|
||||
} catch (err) {
|
||||
log(`Can not kill ${command} process: ${err}`);
|
||||
}
|
||||
stderr.push('Timeout.');
|
||||
}, timeout);
|
||||
|
||||
child.stdout.on('data', (data) => stdout.push(data));
|
||||
|
||||
child.stderr.on('data', (data) => stderr.push(data));
|
||||
|
||||
child.on('exit', (code) => {
|
||||
clearTimeout(timer);
|
||||
resolve({
|
||||
code: code === null ? 1 : code,
|
||||
stdout: stdout.join('').trim(),
|
||||
stderr: stderr.join('').trim()
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const getRunningBots = async () => {
|
||||
const command = 'wire';
|
||||
const args = ['bot', 'factory', 'status', '--topic', topic];
|
||||
|
||||
const { code, stdout, stderr } = await executeCommand(command, args);
|
||||
return {
|
||||
success: !code,
|
||||
bots: code ? [] : JSON.parse(stdout).bots || [],
|
||||
error: (stderr || code) ? stderr || stdout : undefined
|
||||
};
|
||||
};
|
||||
|
||||
const sendBotCommand = async (botId, botCommand) => {
|
||||
const command = 'wire';
|
||||
const args = ['bot', botCommand, '--topic', topic, '--bot-id', botId];
|
||||
|
||||
const { code, stdout, stderr } = await executeCommand(command, args);
|
||||
|
||||
return {
|
||||
success: !code,
|
||||
botId: code ? undefined : botId,
|
||||
error: (stderr || code) ? stderr || stdout : undefined
|
||||
};
|
||||
};
|
||||
|
||||
export const botsResolvers = {
|
||||
Query: {
|
||||
bot_list: async () => {
|
||||
const result = await getRunningBots();
|
||||
return {
|
||||
timestamp: new Date().toUTCString(),
|
||||
json: JSON.stringify(result)
|
||||
};
|
||||
}
|
||||
},
|
||||
Mutation: {
|
||||
bot_kill: async (_, { botId }) => {
|
||||
const result = await sendBotCommand(botId, 'kill');
|
||||
return {
|
||||
timestamp: new Date().toUTCString(),
|
||||
json: JSON.stringify(result)
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
@ -8,6 +8,7 @@ import defaultsDeep from 'lodash.defaultsdeep';
|
||||
import { ipfsResolvers } from './ipfs';
|
||||
import { systemResolvers } from './system';
|
||||
import { logResolvers } from './log';
|
||||
import { botsResolvers } from './bots';
|
||||
|
||||
// eslint-disable-next-line
|
||||
const log = debug('dxos:console:server:resolvers');
|
||||
@ -21,4 +22,4 @@ export const resolvers = defaultsDeep({
|
||||
// TODO(burdon): Auth.
|
||||
// https://www.apollographql.com/docs/apollo-server/data/errors/#codes
|
||||
|
||||
}, ipfsResolvers, systemResolvers, logResolvers);
|
||||
}, ipfsResolvers, systemResolvers, logResolvers, botsResolvers);
|
||||
|
Loading…
Reference in New Issue
Block a user