From c0efe66ec4b03e5c8c8f7938ef5d698dabe1b721 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Mon, 8 Jun 2020 18:55:08 -0500 Subject: [PATCH] IPFS connected status. --- .../console-app/src/components/BooleanIcon.js | 4 +- .../src/containers/VersionCheck.js | 9 +- .../src/containers/panels/apps/AppRecords.js | 15 +- .../src/containers/panels/ipfs/IPFS.js | 134 +++++++++++++++++- .../console-app/src/gql/wns_action.graphql | 10 -- .../console-app/src/gql/wns_records.graphql | 8 +- packages/console-app/src/resolvers.js | 7 +- packages/console-app/src/version.json | 2 +- 8 files changed, 156 insertions(+), 33 deletions(-) delete mode 100644 packages/console-app/src/gql/wns_action.graphql diff --git a/packages/console-app/src/components/BooleanIcon.js b/packages/console-app/src/components/BooleanIcon.js index b5ffbf3..4506a89 100644 --- a/packages/console-app/src/components/BooleanIcon.js +++ b/packages/console-app/src/components/BooleanIcon.js @@ -24,7 +24,7 @@ const useStyles = makeStyles(theme => ({ export const BooleanIcon = ({ yes = false, error = false }) => { const classes = useStyles(); return (yes - ? - : + ? + : ); }; diff --git a/packages/console-app/src/containers/VersionCheck.js b/packages/console-app/src/containers/VersionCheck.js index 87e62be..943bb02 100644 --- a/packages/console-app/src/containers/VersionCheck.js +++ b/packages/console-app/src/containers/VersionCheck.js @@ -27,17 +27,18 @@ const VersionCheck = () => { const classes = useStyles(); const [{ current, latest }, setUpgrade] = useState({}); const status = useQueryStatusReducer(useQuery(SYSTEM_STATUS)); - const data = useQueryStatusReducer(useQuery(WNS_RECORDS, { + const wnsResponse = useQueryStatusReducer(useQuery(WNS_RECORDS, { pollInterval: CHECK_INTERVAL, variables: { type: 'wrn:resource' } })); // Check version. useEffect(() => { - if (status && data) { + if (status && wnsResponse) { const { dxos: { image: current } } = status.system_status; let latest = current; - data.wns_records.json.forEach(({ attributes: { name, version } }) => { + const data = JSON.parse(wnsResponse.wns_records.json); + data.forEach(({ attributes: { name, version } }) => { // TODO(burdon): Filter by type (WRN?) if (name.startsWith('dxos/xbox:')) { if (compareVersions(version, latest) > 0) { @@ -48,7 +49,7 @@ const VersionCheck = () => { setUpgrade({ current, latest: latest !== current ? latest : undefined }); } - }, [status, data]); + }, [status, wnsResponse]); // TODO(burdon): Link to Github page with upgrade info. return ( diff --git a/packages/console-app/src/containers/panels/apps/AppRecords.js b/packages/console-app/src/containers/panels/apps/AppRecords.js index 834b5c8..35d6665 100644 --- a/packages/console-app/src/containers/panels/apps/AppRecords.js +++ b/packages/console-app/src/containers/panels/apps/AppRecords.js @@ -3,29 +3,30 @@ // import React, { useContext } from 'react'; +import moment from 'moment'; import { useQuery } from '@apollo/react-hooks'; import Link from '@material-ui/core/Link'; import TableHead from '@material-ui/core/TableHead'; import TableRow from '@material-ui/core/TableRow'; import TableBody from '@material-ui/core/TableBody'; -import moment from 'moment'; + +import IPFS_STATUS from '../../../gql/ipfs_status.graphql'; +import WNS_RECORDS from '../../../gql/wns_records.graphql'; + +import { ConsoleContext, useQueryStatusReducer, useSorter } from '../../../hooks'; import { BooleanIcon } from '../../../components/BooleanIcon'; import Table from '../../../components/Table'; import TableCell from '../../../components/TableCell'; -import { ConsoleContext, useQueryStatusReducer, useSorter } from '../../../hooks'; import { getServiceUrl } from '../../../util/config'; -import IPFS_STATUS from '../../../gql/ipfs_status.graphql'; -import WNS_RECORDS from '../../../gql/wns_records.graphql'; - const AppRecords = () => { const { config } = useContext(ConsoleContext); const [sorter, sortBy] = useSorter('createTime', false); const appResponse = useQueryStatusReducer(useQuery(WNS_RECORDS, { pollInterval: config.api.intervalQuery, - variables: { type: 'wrn:app' } + variables: { attributes: { type: 'wrn:app' } } })); // TODO(telackey): Does this also need an interval? @@ -35,7 +36,7 @@ const AppRecords = () => { return null; } - const appData = appResponse.wns_records.json; + const appData = JSON.parse(appResponse.wns_records.json); const ipfsData = JSON.parse(ipfsResponse.ipfs_status.json); const localRefs = new Set(ipfsData.refs.local); diff --git a/packages/console-app/src/containers/panels/ipfs/IPFS.js b/packages/console-app/src/containers/panels/ipfs/IPFS.js index e99019c..be7e74f 100644 --- a/packages/console-app/src/containers/panels/ipfs/IPFS.js +++ b/packages/console-app/src/containers/panels/ipfs/IPFS.js @@ -3,29 +3,157 @@ // import React from 'react'; +import get from 'lodash.get'; + import { useQuery } from '@apollo/react-hooks'; +import { makeStyles } from '@material-ui/core'; +import TableBody from '@material-ui/core/TableBody'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; import IPFS_STATUS from '../../../gql/ipfs_status.graphql'; +import WNS_RECORDS from '../../../gql/wns_records.graphql'; import { useQueryStatusReducer } from '../../../hooks'; import Json from '../../../components/Json'; import Panel from '../../../components/Panel'; +import Table from '../../../components/Table'; +import TableCell from '../../../components/TableCell'; import Toolbar from '../../../components/Toolbar'; +import { BooleanIcon } from '../../../components/BooleanIcon'; + +const RECORD_TYPE = 'wrn:service'; +const SERVICE_TYPE = 'ipfs'; + +const useStyles = makeStyles((theme) => ({ + tableContainer: { + flex: 1, + overflowY: 'scroll' + }, + + table: { + tableLayout: 'fixed', + + '& th': { + fontVariant: 'all-small-caps', + fontSize: 18, + cursor: 'ns-resize' + } + }, + + connected: { + fontWeight: 'bold' + }, + + disconnected: { + fontStyle: 'italic' + }, + + colShort: { + width: '30%' + }, + + colWide: {}, + + colBoolean: { + width: '10%' + }, + + caption: { + backgroundColor: theme.palette.primary[500], + color: theme.palette.primary.contrastText, + paddingLeft: '1em', + margin: 0 + } +})); const IPFS = () => { - const data = useQueryStatusReducer(useQuery(IPFS_STATUS)); - if (!data) { + const classes = useStyles(); + const ipfsResponse = useQueryStatusReducer(useQuery(IPFS_STATUS)); + const wnsResponse = useQueryStatusReducer(useQuery(WNS_RECORDS, { + variables: { attributes: { type: RECORD_TYPE, service: SERVICE_TYPE } } + })); + + if (!wnsResponse || !ipfsResponse) { return null; } + const ipfsData = JSON.parse(ipfsResponse.ipfs_status.json); + const registeredServers = JSON.parse(wnsResponse.wns_records.json); + + const displayServers = registeredServers.map((service) => { + console.error(service); + const addresses = get(service, 'attributes.ipfs.addresses'); + let connected = false; + for (const address of addresses) { + const parts = address.split('/'); + const nodeId = parts[parts.length - 1]; + connected = !!ipfsData.swarm.peers.find(({ peer }) => peer === nodeId); + if (connected) { + break; + } + } + + return { + name: get(service, 'name'), + version: get(service, 'version'), + description: get(service, 'attributes.description'), + ipfs: get(service, 'attributes.ipfs'), + connected + }; + }); + + displayServers.sort((a, b) => { + return a.connected && !b.connected ? -1 : b.connected && !a.connected ? 1 : b.name < a.name ? 1 : -1; + }); + + if (displayServers.length === 0) { + displayServers.push({ name: 'None' }); + } + return ( } > - +

WNS-registered IPFS Servers

+ + + + Identifier + Description + Connected + Address + + + + {displayServers.map(({ name, description, ipfs, connected }) => ( + + {name} + {description} + + + + + {ipfs.addresses} + + + ))} + +
+ +

Local IPFS Server

+
); }; diff --git a/packages/console-app/src/gql/wns_action.graphql b/packages/console-app/src/gql/wns_action.graphql deleted file mode 100644 index 8c349b9..0000000 --- a/packages/console-app/src/gql/wns_action.graphql +++ /dev/null @@ -1,10 +0,0 @@ -# -# Copyright 2020 DxOS.org -# - -mutation ($command: String!) { - wns_action(command: $command) { - timestamp - code - } -} diff --git a/packages/console-app/src/gql/wns_records.graphql b/packages/console-app/src/gql/wns_records.graphql index e04dd0a..43c6685 100644 --- a/packages/console-app/src/gql/wns_records.graphql +++ b/packages/console-app/src/gql/wns_records.graphql @@ -1,9 +1,11 @@ # -# Copyright 2020 DxOS.org +# Copyright 2019 Wireline, Inc. # -query ($type: String) { - wns_records (type: $type) @client { +# TODO(telackey): Object would probably not be legal in general, but does work for a '@client' resolved query. +# When we do strong typing across the board we should replace it with something like KeyValueInput. +query ($attributes: Object) { + wns_records (attributes: $attributes) @client { timestamp json } diff --git a/packages/console-app/src/resolvers.js b/packages/console-app/src/resolvers.js index a93ccb6..1a646a2 100644 --- a/packages/console-app/src/resolvers.js +++ b/packages/console-app/src/resolvers.js @@ -43,16 +43,17 @@ export const createResolvers = config => { }; }, - wns_records: async (_, { type }) => { + wns_records: async (_, { attributes }) => { + console.error(attributes); log('WNS records...'); - const data = await registry.queryRecords({ type }); + const data = await registry.queryRecords(attributes); return { __typename: 'JSONResult', timestamp: timestamp(), // NOTE: Hack since this should be a string according to the schema. - json: data + json: JSON.stringify(data) }; }, diff --git a/packages/console-app/src/version.json b/packages/console-app/src/version.json index 7f83619..ade0cf2 100644 --- a/packages/console-app/src/version.json +++ b/packages/console-app/src/version.json @@ -1,7 +1,7 @@ { "build": { "name": "@dxos/console-app", - "buildDate": "2020-06-08T18:45:46.717Z", + "buildDate": "2020-06-08T23:14:00.921Z", "version": "1.0.0-beta.0" } }