From c9b139bb9b8ae27c8f5aad1b1beb72ad8bd44289 Mon Sep 17 00:00:00 2001 From: richburdon Date: Mon, 25 May 2020 22:16:25 -0400 Subject: [PATCH] Version check and sorting. --- packages/console-client/README.md | 12 +++- .../console-client/gql/system_status.graphql | 2 +- packages/console-client/gql/wns_log.graphql | 1 + .../console-client/gql/wns_records.graphql | 1 + .../console-client/gql/wns_status.graphql | 1 + packages/console-client/package.json | 1 + .../src/{components => containers}/Layout.js | 7 +- .../console-client/src/containers/Main.js | 10 ++- .../{components => containers}/StatusBar.js | 26 +++++-- .../src/containers/VersionCheck.js | 67 +++++++++++++++++++ .../src/containers/panels/Status.js | 2 +- .../src/containers/panels/apps/AppRecords.js | 12 ++-- .../src/containers/panels/bots/BotRecords.js | 13 ++-- .../src/containers/panels/wns/WNSRecords.js | 17 ++--- packages/console-client/src/hooks/index.js | 1 + packages/console-client/src/hooks/sorter.js | 25 +++++++ packages/console-client/src/resolvers.js | 9 +++ packages/console-client/version.json | 2 +- packages/console-server/src/gql/api.graphql | 26 ++++--- packages/console-server/src/resolvers.js | 8 ++- yarn.lock | 5 ++ 21 files changed, 192 insertions(+), 56 deletions(-) rename packages/console-client/src/{components => containers}/Layout.js (92%) rename packages/console-client/src/{components => containers}/StatusBar.js (82%) create mode 100644 packages/console-client/src/containers/VersionCheck.js create mode 100644 packages/console-client/src/hooks/sorter.js diff --git a/packages/console-client/README.md b/packages/console-client/README.md index bbaa713..2a89b06 100644 --- a/packages/console-client/README.md +++ b/packages/console-client/README.md @@ -4,13 +4,21 @@ Apollo GraphQL client. ## Usage +First start the server: + ```bash - yarn + cd packages/consoe-server yarn start ``` -http://localhost:8080 +Then start the Webpack devserver. +```bash + cd packages/consoe-client + yarn start +``` + +Then load the app: http://localhost:8080. ## Deploy diff --git a/packages/console-client/gql/system_status.graphql b/packages/console-client/gql/system_status.graphql index 6f8c171..7037633 100644 --- a/packages/console-client/gql/system_status.graphql +++ b/packages/console-client/gql/system_status.graphql @@ -5,6 +5,6 @@ { system_status { timestamp - version + json } } diff --git a/packages/console-client/gql/wns_log.graphql b/packages/console-client/gql/wns_log.graphql index ad3e2ea..a477f8f 100644 --- a/packages/console-client/gql/wns_log.graphql +++ b/packages/console-client/gql/wns_log.graphql @@ -4,6 +4,7 @@ { wns_log @client { + timestamp log } } diff --git a/packages/console-client/gql/wns_records.graphql b/packages/console-client/gql/wns_records.graphql index 2d88d69..e04dd0a 100644 --- a/packages/console-client/gql/wns_records.graphql +++ b/packages/console-client/gql/wns_records.graphql @@ -4,6 +4,7 @@ query ($type: String) { wns_records (type: $type) @client { + timestamp json } } diff --git a/packages/console-client/gql/wns_status.graphql b/packages/console-client/gql/wns_status.graphql index 098eaf9..b3d4ad5 100644 --- a/packages/console-client/gql/wns_status.graphql +++ b/packages/console-client/gql/wns_status.graphql @@ -4,6 +4,7 @@ { wns_status @client { + timestamp json } } diff --git a/packages/console-client/package.json b/packages/console-client/package.json index 01a741b..842f547 100644 --- a/packages/console-client/package.json +++ b/packages/console-client/package.json @@ -41,6 +41,7 @@ "apollo-link-http": "^1.5.17", "build-url": "^2.0.0", "clsx": "^1.1.0", + "compare-versions": "^3.6.0", "debug": "^4.1.1", "graphql-tag": "^2.10.3", "lodash.defaultsdeep": "^4.6.1", diff --git a/packages/console-client/src/components/Layout.js b/packages/console-client/src/containers/Layout.js similarity index 92% rename from packages/console-client/src/components/Layout.js rename to packages/console-client/src/containers/Layout.js index c8fd7c5..80806e5 100644 --- a/packages/console-client/src/components/Layout.js +++ b/packages/console-client/src/containers/Layout.js @@ -9,8 +9,8 @@ import { FullScreen } from '@dxos/gem-core'; import { ConsoleContext } from '../hooks'; -import AppBar from './AppBar'; -import Sidebar from './Sidebar'; +import AppBar from '../components/AppBar'; +import Sidebar from '../components/Sidebar'; import StatusBar from './StatusBar'; const useStyles = makeStyles((theme) => ({ @@ -44,6 +44,9 @@ const useStyles = makeStyles((theme) => ({ } })); +/** + * Main layout for app. + */ const Layout = ({ children }) => { const classes = useStyles(); const { config, modules } = useContext(ConsoleContext); diff --git a/packages/console-client/src/containers/Main.js b/packages/console-client/src/containers/Main.js index 1f1a398..9ce9d12 100644 --- a/packages/console-client/src/containers/Main.js +++ b/packages/console-client/src/containers/Main.js @@ -10,11 +10,13 @@ import { ThemeProvider } from '@material-ui/core/styles'; import CssBaseline from '@material-ui/core/CssBaseline'; import config from '../../config.yml'; +import { build } from '../../version.json'; + import { createTheme } from '../theme'; import { clientFactory } from '../client'; import modules from '../modules'; -import Layout from '../components/Layout'; +import Layout from './Layout'; import ConsoleContextProvider from './ConsoleContextProvider'; import AppRecords from './panels/apps/Apps'; @@ -26,8 +28,14 @@ import Signaling from './panels/Signaling'; import Status from './panels/Status'; import WNS from './panels/wns/WNS'; +// TODO(burdon): Config object. +Object.assign(config, { build }); + debug.enable(config.system.debug); +/** + * Root application. + */ const Main = () => { return ( diff --git a/packages/console-client/src/components/StatusBar.js b/packages/console-client/src/containers/StatusBar.js similarity index 82% rename from packages/console-client/src/components/StatusBar.js rename to packages/console-client/src/containers/StatusBar.js index c2dc1be..3bd0e13 100644 --- a/packages/console-client/src/components/StatusBar.js +++ b/packages/console-client/src/containers/StatusBar.js @@ -3,6 +3,7 @@ // import clsx from 'clsx'; +import moment from 'moment'; import React, { useContext, useEffect, useState } from 'react'; import { makeStyles } from '@material-ui/core'; import Link from '@material-ui/core/Link'; @@ -15,15 +16,16 @@ import grey from '@material-ui/core/colors/grey'; import green from '@material-ui/core/colors/green'; import red from '@material-ui/core/colors/red'; -import { version } from '../../package.json'; import { ConsoleContext, useStatusReducer } from '../hooks'; +import VersionCheck from './VersionCheck'; + const useStyles = makeStyles((theme) => ({ root: { display: 'flex', flexDirection: 'row', flex: 1, - justifyContent: 'space-around', + justifyContent: 'space-between', backgroundColor: grey[900], color: grey[400] }, @@ -50,16 +52,26 @@ const useStyles = makeStyles((theme) => ({ running: { color: green[500] }, - loading:{ + loading: { color: theme.palette.primary.dark + }, + info: { + display: 'flex', + '& div': { + margin: 4 + } } })); +/** + * Displays status indicators at the bottom of the page. + */ const StatusBar = () => { const classes = useStyles(); const [{ loading, error }] = useStatusReducer(); const [isLoading, setLoading] = useState(loading); const { config } = useContext(ConsoleContext); + const { build: { name, buildDate, version } } = config; useEffect(() => { let t; @@ -92,7 +104,13 @@ const StatusBar = () => { -
(c) {config.app.org} {version}
+ +
+
{name} ({version})
+
{moment(buildDate).format('L')}
+ +
+
diff --git a/packages/console-client/src/containers/VersionCheck.js b/packages/console-client/src/containers/VersionCheck.js new file mode 100644 index 0000000..1d12aeb --- /dev/null +++ b/packages/console-client/src/containers/VersionCheck.js @@ -0,0 +1,67 @@ +// +// Copyright 2020 DxOS.org +// + +import compareVersions from 'compare-versions'; +import React, { useEffect, useState } from 'react'; +import { useQuery } from '@apollo/react-hooks'; +import { makeStyles } from '@material-ui/core'; + +import SYSTEM_STATUS from '../../gql/system_status.graphql'; +import WNS_RECORDS from '../../gql/wns_records.graphql'; + +import { useQueryStatusReducer } from '../hooks'; + +const CHECK_INTERVAL = 5 * 60 * 1000; + +const useStyles = makeStyles(theme => ({ + update: { + color: theme.palette.error.light + } +})); + +/** + * Checks for a system upgrade. + */ +const VersionCheck = () => { + const classes = useStyles(); + const [{ current, latest }, setUpgrade] = useState({}); + const status = useQueryStatusReducer(useQuery(SYSTEM_STATUS)); + const data = useQueryStatusReducer(useQuery(WNS_RECORDS, { + pollInterval: CHECK_INTERVAL, + variables: { type: 'wrn:resource' } + })); + + // Check version. + useEffect(() => { + if (status && data) { + const { dxos: { image: current } } = JSON.parse(status.system_status.json); + let latest = current; + data.wns_records.json.forEach(({ attributes: { name, version } }) => { + if (name.startsWith('dxos/xbox:')) { + if (compareVersions(version, latest) > 0) { + latest = version; + } + } + }); + + if (latest !== current) { + setUpgrade({ current, latest }); + } + } + }, [status, data]); + + // TODO(burdon): Link to Github page with upgrade info. + return ( + <> + {current && ( +
SYS: {current}
+ )} + {latest && ( +
LATEST: {latest}
+ )} + + ); +}; + +export default VersionCheck; diff --git a/packages/console-client/src/containers/panels/Status.js b/packages/console-client/src/containers/panels/Status.js index f3dc2f9..89aca56 100644 --- a/packages/console-client/src/containers/panels/Status.js +++ b/packages/console-client/src/containers/panels/Status.js @@ -27,7 +27,7 @@ const Status = () => { } > - + ); }; diff --git a/packages/console-client/src/containers/panels/apps/AppRecords.js b/packages/console-client/src/containers/panels/apps/AppRecords.js index 7decfbc..9b7b6f4 100644 --- a/packages/console-client/src/containers/panels/apps/AppRecords.js +++ b/packages/console-client/src/containers/panels/apps/AppRecords.js @@ -7,7 +7,7 @@ import { useQuery } from '@apollo/react-hooks'; import WNS_RECORDS from '../../../../gql/wns_records.graphql'; -import { ConsoleContext, useQueryStatusReducer } from '../../../hooks'; +import { ConsoleContext, useQueryStatusReducer, useSorter } from '../../../hooks'; import Link from '@material-ui/core/Link'; import TableHead from '@material-ui/core/TableHead'; @@ -21,6 +21,7 @@ import TableCell from '../../../components/TableCell'; const AppRecords = () => { const { config } = useContext(ConsoleContext); + const [sorter, sortBy] = useSorter('id'); const data = useQueryStatusReducer(useQuery(WNS_RECORDS, { pollInterval: config.api.pollInterval, variables: { type: 'wrn:app' } @@ -32,9 +33,6 @@ const AppRecords = () => { const records = data.wns_records.json; - // TODO(burdon): Factor out. - const sorter = (a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0); - // TODO(burdon): Test if app is deployed. const getAppUrl = ({ name, version }) => { const base = getServiceUrl(config, 'app.server'); @@ -56,9 +54,9 @@ const AppRecords = () => { - Name - Version - Description + ID + Version + Name Link diff --git a/packages/console-client/src/containers/panels/bots/BotRecords.js b/packages/console-client/src/containers/panels/bots/BotRecords.js index 65f19ad..0609b7c 100644 --- a/packages/console-client/src/containers/panels/bots/BotRecords.js +++ b/packages/console-client/src/containers/panels/bots/BotRecords.js @@ -7,7 +7,7 @@ import { useQuery } from '@apollo/react-hooks'; import WNS_RECORDS from '../../../../gql/wns_records.graphql'; -import { ConsoleContext, useQueryStatusReducer } from '../../../hooks'; +import { ConsoleContext, useQueryStatusReducer, useSorter } from '../../../hooks'; import TableHead from '@material-ui/core/TableHead'; import TableRow from '@material-ui/core/TableRow'; @@ -18,6 +18,7 @@ import TableCell from '../../../components/TableCell'; const AppRecords = () => { const { config } = useContext(ConsoleContext); + const [sorter, sortBy] = useSorter('id'); const data = useQueryStatusReducer(useQuery(WNS_RECORDS, { pollInterval: config.api.pollInterval, variables: { type: 'wrn:bot' } @@ -29,16 +30,14 @@ const AppRecords = () => { const records = data.wns_records.json; - // TODO(burdon): Factor out. - const sorter = (a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0); - return (
- Name - Version - Description + ID + Version + Name + diff --git a/packages/console-client/src/containers/panels/wns/WNSRecords.js b/packages/console-client/src/containers/panels/wns/WNSRecords.js index 75354b9..e982411 100644 --- a/packages/console-client/src/containers/panels/wns/WNSRecords.js +++ b/packages/console-client/src/containers/panels/wns/WNSRecords.js @@ -15,7 +15,7 @@ import TableBody from '@material-ui/core/TableBody'; import WNS_RECORDS from '../../../../gql/wns_records.graphql'; -import { ConsoleContext, useQueryStatusReducer } from '../../../hooks'; +import { ConsoleContext, useQueryStatusReducer, useSorter } from '../../../hooks'; import Table from '../../../components/Table'; import TableCell from '../../../components/TableCell'; @@ -64,7 +64,7 @@ export const WNSRecordType = ({ type = types[0].key, onChanged }) => { const WNSRecords = ({ type }) => { const { config } = useContext(ConsoleContext); - const [{ sort, ascend }, setSort] = useState({ sort: 'type', ascend: true }); + const [sorter, sortBy] = useSorter('id'); const data = useQueryStatusReducer(useQuery(WNS_RECORDS, { pollInterval: config.api.pollInterval, variables: { type } @@ -76,23 +76,14 @@ const WNSRecords = ({ type }) => { const records = data.wns_records.json; - // TODO(burdon): Factor out. - const sortBy = field => () => setSort({ sort: field, ascend: (field === sort ? !ascend : true) }); - const sorter = (item1, item2) => { - const a = get(item1, sort); - const b = get(item2, sort); - const dir = ascend ? 1 : -1; - return (a < b) ? -1 * dir : (a > b) ? dir : 0; - }; - return (
Type - Name + ID Version - Description + Name Package Hash Created diff --git a/packages/console-client/src/hooks/index.js b/packages/console-client/src/hooks/index.js index 14b5c2f..156e815 100644 --- a/packages/console-client/src/hooks/index.js +++ b/packages/console-client/src/hooks/index.js @@ -4,4 +4,5 @@ export * from './context'; export * from './registry'; +export * from './sorter'; export * from './status'; diff --git a/packages/console-client/src/hooks/sorter.js b/packages/console-client/src/hooks/sorter.js new file mode 100644 index 0000000..6e7e03c --- /dev/null +++ b/packages/console-client/src/hooks/sorter.js @@ -0,0 +1,25 @@ +// +// Copyright 2020 DxOS.org +// + +import get from 'lodash.get'; +import { useState } from 'react'; + +// TODO(burdon): Enable multiple sort order (e.g., id, version). +export const useSorter = (initial) => { + const [{ sort, ascend }, setSort] = useState({ sort: initial, ascend: true }); + + const sorter = (item1, item2) => { + const a = get(item1, sort); + const b = get(item2, sort); + const dir = ascend ? 1 : -1; + return (a < b) ? -1 * dir : (a > b) ? dir : 0; + }; + + const sortBy = field => () => setSort({ sort: field, ascend: (field === sort ? !ascend : true) }); + + return [ + sorter, + sortBy + ]; +}; diff --git a/packages/console-client/src/resolvers.js b/packages/console-client/src/resolvers.js index aadb113..841a9ff 100644 --- a/packages/console-client/src/resolvers.js +++ b/packages/console-client/src/resolvers.js @@ -10,6 +10,8 @@ import { getServiceUrl } from './util/config'; const log = debug('dxos:console:client:resolvers'); +const timestamp = () => new Date().toUTCString(); + /** * Resolvers * https://www.apollographql.com/docs/tutorial/local-state/#local-resolvers @@ -27,6 +29,9 @@ export const createResolvers = config => { return { __typename: 'JSONResult', + timestamp: timestamp(), + + // NOTE: Hack since this should be a string according to the schema. json: data }; }, @@ -37,6 +42,9 @@ export const createResolvers = config => { return { __typename: 'JSONResult', + timestamp: timestamp(), + + // NOTE: Hack since this should be a string according to the schema. json: data }; }, @@ -47,6 +55,7 @@ export const createResolvers = config => { // TODO(burdon): Use Registry API rather than from CLI? return { __typename: 'JSONLog', + timestamp: timestamp(), log: [] }; } diff --git a/packages/console-client/version.json b/packages/console-client/version.json index 93867b5..8f6e049 100644 --- a/packages/console-client/version.json +++ b/packages/console-client/version.json @@ -1,7 +1,7 @@ { "build": { "name": "@dxos/console-client", - "buildDate": "2020-05-25T22:04:20.163Z", + "buildDate": "2020-05-26T01:16:30.514Z", "version": "1.0.0-beta.0" } } diff --git a/packages/console-server/src/gql/api.graphql b/packages/console-server/src/gql/api.graphql index ed8d0ee..1b1db8a 100644 --- a/packages/console-server/src/gql/api.graphql +++ b/packages/console-server/src/gql/api.graphql @@ -2,30 +2,28 @@ # Copyright 2020 DxOS.org # -type JSONResult { - json: String! -} - -type Log { - log: [String]! -} - -type Status { - timestamp: String! - version: String! -} - type Result { timestamp: String! code: Int! } +type Log { + timestamp: String! + log: [String]! +} + +# TODO(burdon): Generic result. +type JSONResult { + timestamp: String! + json: String! +} + # # Schema # type Query { - system_status: Status! + system_status: JSONResult! ipfs_status: JSONResult! wns_status: JSONResult! # TODO(burdon): Import WNS schema! diff --git a/packages/console-server/src/resolvers.js b/packages/console-server/src/resolvers.js index c6e0cff..ace506a 100644 --- a/packages/console-server/src/resolvers.js +++ b/packages/console-server/src/resolvers.js @@ -5,8 +5,6 @@ import debug from 'debug'; import IpfsHttpClient from 'ipfs-http-client'; -import { version } from '../package.json'; - const log = debug('dxos:console:server:resolvers'); const timestamp = () => new Date().toUTCString(); @@ -39,7 +37,11 @@ export const createResolvers = config => ({ system_status: () => ({ timestamp: timestamp(), - version + json: JSON.stringify({ + dxos: { + image: '0.0.1' + } + }) }), // diff --git a/yarn.lock b/yarn.lock index 1ab7b67..4c25b90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5260,6 +5260,11 @@ compare-func@^1.3.1: array-ify "^1.0.0" dot-prop "^3.0.0" +compare-versions@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"