From 63a819957bdf66fdacd027d3f680185ab1b54b33 Mon Sep 17 00:00:00 2001 From: German Ferrero Date: Wed, 21 Apr 2021 17:33:08 -0300 Subject: [PATCH 1/5] feat(network nodes): add Network Nodes Plugin --- plugins/lime-plugin-network-nodes/index.js | 9 ++++ .../networkNodes.spec.js | 54 +++++++++++++++++++ .../src/components/expandableNode/index.js | 25 +++++++++ .../src/components/expandableNode/stories.js | 20 +++++++ .../src/components/expandableNode/style.less | 14 +++++ .../src/networkNodesApi.js | 7 +++ .../src/networkNodesApi.spec.js | 34 ++++++++++++ .../src/networkNodesMenu.js | 8 +++ .../src/networkNodesPage.js | 49 +++++++++++++++++ .../src/networkNodesPage.stories.js | 51 ++++++++++++++++++ .../src/networkNodesQueries.js | 5 ++ .../src/networkNodesStyle.less | 5 ++ src/config.js | 2 + 13 files changed, 283 insertions(+) create mode 100644 plugins/lime-plugin-network-nodes/index.js create mode 100644 plugins/lime-plugin-network-nodes/networkNodes.spec.js create mode 100644 plugins/lime-plugin-network-nodes/src/components/expandableNode/index.js create mode 100644 plugins/lime-plugin-network-nodes/src/components/expandableNode/stories.js create mode 100644 plugins/lime-plugin-network-nodes/src/components/expandableNode/style.less create mode 100644 plugins/lime-plugin-network-nodes/src/networkNodesApi.js create mode 100644 plugins/lime-plugin-network-nodes/src/networkNodesApi.spec.js create mode 100644 plugins/lime-plugin-network-nodes/src/networkNodesMenu.js create mode 100644 plugins/lime-plugin-network-nodes/src/networkNodesPage.js create mode 100644 plugins/lime-plugin-network-nodes/src/networkNodesPage.stories.js create mode 100644 plugins/lime-plugin-network-nodes/src/networkNodesQueries.js create mode 100644 plugins/lime-plugin-network-nodes/src/networkNodesStyle.less diff --git a/plugins/lime-plugin-network-nodes/index.js b/plugins/lime-plugin-network-nodes/index.js new file mode 100644 index 00000000..602ea57b --- /dev/null +++ b/plugins/lime-plugin-network-nodes/index.js @@ -0,0 +1,9 @@ +import Page from './src/networkNodesPage'; +import Menu from './src/networkNodesMenu'; + +export default { + name: 'networkNodes', + page: Page, + menu: Menu, + menuView: 'community' +}; diff --git a/plugins/lime-plugin-network-nodes/networkNodes.spec.js b/plugins/lime-plugin-network-nodes/networkNodes.spec.js new file mode 100644 index 00000000..261b81f5 --- /dev/null +++ b/plugins/lime-plugin-network-nodes/networkNodes.spec.js @@ -0,0 +1,54 @@ +// Here you define tests that closely resemble how your component is used +// Using the testing-library: https://testing-library.com + +import { h } from 'preact'; +import { fireEvent, screen, cleanup, act } from '@testing-library/preact'; +import '@testing-library/jest-dom'; +import { render } from 'utils/test_utils'; +import queryCache from 'utils/queryCache'; + +import NetworkNodes from './src/networkNodesPage'; +import { getNodes } from './src/networkNodesApi'; + +jest.mock('./src/networkNodesApi'); + +describe('networkNodes', () => { + beforeEach(() => { + getNodes.mockImplementation(async () => ({ + "ql-berta": { + ipv4: '10.5.0.16', + ipv6: 'fd0d:fe46:8ce8::8bbf:7500', + board: 'LibreRouter v1', + fw_version: 'LibreRouterOS 1.4' + }, + "ql-nelson": { + ipv4: '10.5.0.17', + ipv6: 'fd0d:fe46:8ce8::8bbf:75bf', + board: 'LibreRouter v1', + fw_version: 'LibreRouterOS 1.4' + } + })); + }); + + afterEach(() => { + cleanup(); + act(() => queryCache.clear()); + }); + + it('test that nodes are shown', async () => { + render(); + expect(await screen.findByText('ql-nelson')).toBeInTheDocument(); + expect(await screen.findByText('ql-berta')).toBeInTheDocument(); + }); + + it('test that details are shown on click', async () => { + render(); + const element = await screen.findByText('ql-nelson'); + fireEvent.click(element); + expect(await screen.findByRole('link', { name: '10.5.0.17'})).toBeInTheDocument(); + expect(await screen.findByText('IPv6: fd0d:fe46:8ce8::8bbf:75bf')).toBeInTheDocument(); + expect(await screen.findByText('Device: LibreRouter v1')).toBeInTheDocument(); + expect(await screen.findByText('Firmware: LibreRouterOS 1.4')).toBeInTheDocument(); + }) + +}); diff --git a/plugins/lime-plugin-network-nodes/src/components/expandableNode/index.js b/plugins/lime-plugin-network-nodes/src/components/expandableNode/index.js new file mode 100644 index 00000000..2bc67362 --- /dev/null +++ b/plugins/lime-plugin-network-nodes/src/components/expandableNode/index.js @@ -0,0 +1,25 @@ +import { h } from 'preact'; +import I18n from 'i18n-js'; +import { ListItem } from 'components/list'; +import style from './style.less'; + +export const ExpandableNode = ({ node, showMore, onClick }) => { + const { hostname, ipv4, ipv6, board, fw_version } = node; + return ( + +
+
+
{hostname}
+
+ {showMore && +
+ {ipv4 &&
IPv4: {ipv4}
} + {ipv6 &&
IPv6: {ipv6}
} + {board &&
{I18n.t('Device')}: {board}
} + {fw_version &&
{I18n.t('Firmware')}: {fw_version}
} +
+ } +
+
+ ) +} \ No newline at end of file diff --git a/plugins/lime-plugin-network-nodes/src/components/expandableNode/stories.js b/plugins/lime-plugin-network-nodes/src/components/expandableNode/stories.js new file mode 100644 index 00000000..2abf1859 --- /dev/null +++ b/plugins/lime-plugin-network-nodes/src/components/expandableNode/stories.js @@ -0,0 +1,20 @@ +import { ExpandableNode } from './index'; + +export default { + title: 'Containers/NetworkNodes/Components/ExpandableNode', + component: ExpandableNode +}; + +const node = { + hostname: 'ql-flor', + ipv4:'10.5.0.16', + ipv6: 'fd0d:fe46:8ce8::8bbf:7500', + board: 'LibreRouter v1', + fw_version: 'LibreRouterOS 1.4' +}; + +export const folded = () => + + +export const unfolded = () => + \ No newline at end of file diff --git a/plugins/lime-plugin-network-nodes/src/components/expandableNode/style.less b/plugins/lime-plugin-network-nodes/src/components/expandableNode/style.less new file mode 100644 index 00000000..5dc98d63 --- /dev/null +++ b/plugins/lime-plugin-network-nodes/src/components/expandableNode/style.less @@ -0,0 +1,14 @@ +.moreData { + padding-left: 2em; + cursor: text; +} + +.hostname { + font-size: 2em; +} + +.threeDots { + font-size: 1.5em; + font-weight: bold; + cursor: pointer; +} \ No newline at end of file diff --git a/plugins/lime-plugin-network-nodes/src/networkNodesApi.js b/plugins/lime-plugin-network-nodes/src/networkNodesApi.js new file mode 100644 index 00000000..a2d02a0b --- /dev/null +++ b/plugins/lime-plugin-network-nodes/src/networkNodesApi.js @@ -0,0 +1,7 @@ +import api from 'utils/uhttpd.service'; + +export const getNodes = () => + api.call('network-nodes', 'get_nodes', {}).toPromise() + .then(res => res.nodes); + +export const markNodesAsGone = () => api.call('network-nodes', 'mark_nodes_as_gone', {}).toPromise(); diff --git a/plugins/lime-plugin-network-nodes/src/networkNodesApi.spec.js b/plugins/lime-plugin-network-nodes/src/networkNodesApi.spec.js new file mode 100644 index 00000000..69fef588 --- /dev/null +++ b/plugins/lime-plugin-network-nodes/src/networkNodesApi.spec.js @@ -0,0 +1,34 @@ +import { getNodes, markNodesAsGone } from './networkNodesApi' +import api from 'utils/uhttpd.service'; +import { of } from 'rxjs'; +jest.mock('utils/uhttpd.service') + +beforeEach(() => { + api.call.mockImplementation(() => of({ status: 'ok' })) +}) + +describe('getNodes', () => { + it('hits the expected endpoint', async () => { + getNodes(); + expect(api.call).toBeCalledWith('network-nodes', 'get_nodes', {}); + }); + + it('test resolves to nodes data', async () => { + const nodes = { + 'host1': { + ipv4: '10.5.0.16', + ipv6: 'fd0d:fe46:8ce8::8bbf:7500', + board: 'LibreRouter v1', + fw_version: 'LibreRouterOS 1.4' + }, + 'host2': { + ipv4: '10.5.0.17', + ipv6: 'fd0d:fe46:8ce8::8bbf:75bf', + board: 'TL-WDR3500', + fw_version: 'LibreRouterOS 1.4' + } + }; + api.call.mockImplementation(() => of({ status: 'ok', nodes })); + expect(await getNodes()).toEqual(nodes); + }); +}); diff --git a/plugins/lime-plugin-network-nodes/src/networkNodesMenu.js b/plugins/lime-plugin-network-nodes/src/networkNodesMenu.js new file mode 100644 index 00000000..0303aa3c --- /dev/null +++ b/plugins/lime-plugin-network-nodes/src/networkNodesMenu.js @@ -0,0 +1,8 @@ +import { h } from 'preact'; +import I18n from 'i18n-js'; + +const Menu = () => ( + {I18n.t('Network Nodes')} +); + +export default Menu; diff --git a/plugins/lime-plugin-network-nodes/src/networkNodesPage.js b/plugins/lime-plugin-network-nodes/src/networkNodesPage.js new file mode 100644 index 00000000..9d20e0d4 --- /dev/null +++ b/plugins/lime-plugin-network-nodes/src/networkNodesPage.js @@ -0,0 +1,49 @@ +// NetworkNodes will be rendered when navigating to this plugin +import { h } from 'preact'; +import { useNetworkNodes } from './networkNodesQueries'; +import { List } from 'components/list'; +import { Loading } from 'components/loading'; +import { ExpandableNode } from './components/expandableNode'; +import style from './networkNodesStyle.less'; +import { useState } from 'preact/hooks'; +import I18n from 'i18n-js'; + +export const _NetworkNodes = ({ nodes, isLoading, unfoldedNode, onUnfold }) => { + if (isLoading) { + return
+ } + return ( +
+
{I18n.t("Network Nodes")}
+ + {nodes.map((node) => + onUnfold(node.hostname)} /> + )} + +
+ ) +}; + +const NetworkNodes = () => { + const { data: networkNodes, isLoading } = useNetworkNodes(); + const [ unfoldedNode, setunfoldedNode ] = useState(null); + const sortedNodes = (networkNodes && + Object.entries(networkNodes) + .map(([k, v]) => ({ ...v, hostname: k })) + .sort((a, b) => a.hostname > b.hostname ? -1 : 1)); + + function changeUnfolded(hostname) { + if (unfoldedNode == hostname) { + setunfoldedNode(null); + return; + } + setunfoldedNode(hostname); + } + + return <_NetworkNodes nodes={sortedNodes} isLoading={isLoading} + unfoldedNode={unfoldedNode} onUnfold={changeUnfolded}/>; +} + +export default NetworkNodes; diff --git a/plugins/lime-plugin-network-nodes/src/networkNodesPage.stories.js b/plugins/lime-plugin-network-nodes/src/networkNodesPage.stories.js new file mode 100644 index 00000000..45f7c376 --- /dev/null +++ b/plugins/lime-plugin-network-nodes/src/networkNodesPage.stories.js @@ -0,0 +1,51 @@ +import NetworkNodes, {_NetworkNodes} from './networkNodesPage'; + +export default { + title: 'Containers/networkNodes' +} + +const nodes = [ + { + hostname: 'ql-berta', + ipv4:'10.5.0.16', + ipv6: 'fd0d:fe46:8ce8::8bbf:7500', + board: 'LibreRouter v1', + fw_version: 'LibreRouterOS 1.4' + }, + { + hostname: 'ql-nelson', + ipv4:'10.5.0.17', + ipv6: 'fd0d:fe46:8ce8::8bbf:75bf', + board: 'LibreRouter v1', + fw_version: 'LibreRouterOS 1.4' + } +]; + +export const networkNodesNonUnfolded = () => + <_NetworkNodes nodes={nodes} /> + +export const networkNodesOneUnfolded = () => + <_NetworkNodes nodes={nodes} unfoldedNode={'ql-berta'} /> + +export const networkNodesLoading = () => + <_NetworkNodes isLoading={true} /> + +const manyNodes = []; +for (let i = 0; i < 15; i++) { + const hostname = `host${i}`; + const node = {...nodes[0]}; + node.hostname = hostname; + manyNodes.push(node); +} + +export const networkNodesManyNodes = () => + <_NetworkNodes nodes={manyNodes} /> + +export const networkNodesInteractive = () => + +networkNodesInteractive.args = { + queries: [ + [['network-nodes', 'get_nodes'], + Object.fromEntries(nodes.map(n => [n.hostname, n]))] + ] +} \ No newline at end of file diff --git a/plugins/lime-plugin-network-nodes/src/networkNodesQueries.js b/plugins/lime-plugin-network-nodes/src/networkNodesQueries.js new file mode 100644 index 00000000..720909a9 --- /dev/null +++ b/plugins/lime-plugin-network-nodes/src/networkNodesQueries.js @@ -0,0 +1,5 @@ +import { useQuery } from 'react-query'; +import { getNodes } from './networkNodesApi'; + +export const useNetworkNodes = () => + useQuery(['network-nodes', 'get_nodes'], getNodes); diff --git a/plugins/lime-plugin-network-nodes/src/networkNodesStyle.less b/plugins/lime-plugin-network-nodes/src/networkNodesStyle.less new file mode 100644 index 00000000..4342d5e2 --- /dev/null +++ b/plugins/lime-plugin-network-nodes/src/networkNodesStyle.less @@ -0,0 +1,5 @@ +.title { + font-size: 2em; + padding-top: 1rem; + padding-left: 1rem; +} \ No newline at end of file diff --git a/src/config.js b/src/config.js index 270a4db2..005cab7b 100644 --- a/src/config.js +++ b/src/config.js @@ -9,6 +9,7 @@ import Fbw from '../plugins/lime-plugin-fbw'; import NetworkAdmin from '../plugins/lime-plugin-network-admin'; import Firmware from '../plugins/lime-plugin-firmware'; import RemoteSupport from '../plugins/lime-plugin-remotesupport'; +import NetworkNodes from '../plugins/lime-plugin-network-nodes'; // REGISTER PLUGINS export const plugins = [ @@ -22,5 +23,6 @@ export const plugins = [ Firmware, ChangeNode, RemoteSupport, + NetworkNodes, Fbw // fbw does not have menu item ]; From f7afcb80079e1beb202a7913cff2293127c5fae1 Mon Sep 17 00:00:00 2001 From: German Ferrero Date: Wed, 21 Apr 2021 17:40:44 -0300 Subject: [PATCH 2/5] chore(translations): spanish for network nodes --- i18n/generic.json | 1 + i18n/translations/es.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/i18n/generic.json b/i18n/generic.json index 5872d1f0..7fddcae1 100644 --- a/i18n/generic.json +++ b/i18n/generic.json @@ -95,6 +95,7 @@ "most_active_2d5a3cae": "Most Active", "must_select_a_network_and_a_valid_hostname_ea82e72c": "Must select a network and a valid hostname", "network_configuration_ea7f4215": "Network Configuration", + "network_nodes_4368eb67": "Network Nodes", "no_network_found_try_realigning_your_node_and_resc_176a9b3e": "No network found, try realigning your node and rescanning.", "node_configuration_7342e6f5": "Node Configuration", "notes_c42e0fd5": "Notes", diff --git a/i18n/translations/es.json b/i18n/translations/es.json index 47267d89..9012acac 100644 --- a/i18n/translations/es.json +++ b/i18n/translations/es.json @@ -193,6 +193,7 @@ "select_new_node_5b2e9165": "Selecciona el nodo", "visit_864b4060": "Visitar", "go_to_community_view_d12b8d67": "Ir a Vista de Comunidad", - "go_to_node_view_26ba929d": "Ir a Vista de Nodo" + "go_to_node_view_26ba929d": "Ir a Vista de Nodo", + "network_nodes_4368eb67": "Nodos de la Red" } From c4eb44d9fad78f28e0244db9eb3be2e932b1bd2d Mon Sep 17 00:00:00 2001 From: German Ferrero Date: Wed, 21 Apr 2021 18:31:57 -0300 Subject: [PATCH 3/5] improvement(network-node): center title --- plugins/lime-plugin-network-nodes/src/networkNodesStyle.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lime-plugin-network-nodes/src/networkNodesStyle.less b/plugins/lime-plugin-network-nodes/src/networkNodesStyle.less index 4342d5e2..81dacaf6 100644 --- a/plugins/lime-plugin-network-nodes/src/networkNodesStyle.less +++ b/plugins/lime-plugin-network-nodes/src/networkNodesStyle.less @@ -1,5 +1,5 @@ .title { + text-align: center; font-size: 2em; padding-top: 1rem; - padding-left: 1rem; } \ No newline at end of file From 7f4d00e447b6e5331d3eb5b4a62fd6f80f42b5a6 Mon Sep 17 00:00:00 2001 From: German Ferrero Date: Wed, 21 Apr 2021 18:32:52 -0300 Subject: [PATCH 4/5] improvement(network-nodes): filter gones and sort --- bkp/src/networkFirmwaresStyle.less | 1 + .../networkNodes.spec.js | 21 ++++++++++++++++--- .../src/networkNodesPage.js | 3 ++- 3 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 bkp/src/networkFirmwaresStyle.less diff --git a/bkp/src/networkFirmwaresStyle.less b/bkp/src/networkFirmwaresStyle.less new file mode 100644 index 00000000..53a06c08 --- /dev/null +++ b/bkp/src/networkFirmwaresStyle.less @@ -0,0 +1 @@ +// Here you define the css for this plugin \ No newline at end of file diff --git a/plugins/lime-plugin-network-nodes/networkNodes.spec.js b/plugins/lime-plugin-network-nodes/networkNodes.spec.js index 261b81f5..8cfbf48c 100644 --- a/plugins/lime-plugin-network-nodes/networkNodes.spec.js +++ b/plugins/lime-plugin-network-nodes/networkNodes.spec.js @@ -19,13 +19,22 @@ describe('networkNodes', () => { ipv4: '10.5.0.16', ipv6: 'fd0d:fe46:8ce8::8bbf:7500', board: 'LibreRouter v1', - fw_version: 'LibreRouterOS 1.4' + fw_version: 'LibreRouterOS 1.4', + status: 'recently_connected' }, "ql-nelson": { ipv4: '10.5.0.17', ipv6: 'fd0d:fe46:8ce8::8bbf:75bf', board: 'LibreRouter v1', - fw_version: 'LibreRouterOS 1.4' + fw_version: 'LibreRouterOS 1.4', + status: 'disconnected' + }, + "ql-gone-node": { + ipv4: '10.5.0.18', + ipv6: 'fd0d:fe46:8ce8::8bbf:75be', + board: 'LibreRouter v1', + fw_version: 'LibreRouterOS 1.4', + status: 'gone' } })); }); @@ -35,7 +44,7 @@ describe('networkNodes', () => { act(() => queryCache.clear()); }); - it('test that nodes are shown', async () => { + it('test that nodes recently_connected and connected nodes are shown', async () => { render(); expect(await screen.findByText('ql-nelson')).toBeInTheDocument(); expect(await screen.findByText('ql-berta')).toBeInTheDocument(); @@ -49,6 +58,12 @@ describe('networkNodes', () => { expect(await screen.findByText('IPv6: fd0d:fe46:8ce8::8bbf:75bf')).toBeInTheDocument(); expect(await screen.findByText('Device: LibreRouter v1')).toBeInTheDocument(); expect(await screen.findByText('Firmware: LibreRouterOS 1.4')).toBeInTheDocument(); + }); + + it('test that gone nodes are not shown', async () => { + render(); + await screen.findByText('ql-nelson'); + expect(screen.queryByText('ql-gone-node')).toBeNull(); }) }); diff --git a/plugins/lime-plugin-network-nodes/src/networkNodesPage.js b/plugins/lime-plugin-network-nodes/src/networkNodesPage.js index 9d20e0d4..5a72fcdf 100644 --- a/plugins/lime-plugin-network-nodes/src/networkNodesPage.js +++ b/plugins/lime-plugin-network-nodes/src/networkNodesPage.js @@ -32,7 +32,8 @@ const NetworkNodes = () => { const sortedNodes = (networkNodes && Object.entries(networkNodes) .map(([k, v]) => ({ ...v, hostname: k })) - .sort((a, b) => a.hostname > b.hostname ? -1 : 1)); + .filter(n => n.status !== 'gone') + .sort((a, b) => a.hostname > b.hostname)); function changeUnfolded(hostname) { if (unfoldedNode == hostname) { From 6dc79d8437a6b4f98868c20bebf8ba75e188cf8a Mon Sep 17 00:00:00 2001 From: German Ferrero Date: Wed, 21 Apr 2021 18:42:30 -0300 Subject: [PATCH 5/5] improvement(expandable-node): let user copypaste details --- .../src/components/expandableNode/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lime-plugin-network-nodes/src/components/expandableNode/index.js b/plugins/lime-plugin-network-nodes/src/components/expandableNode/index.js index 2bc67362..8624eb5d 100644 --- a/plugins/lime-plugin-network-nodes/src/components/expandableNode/index.js +++ b/plugins/lime-plugin-network-nodes/src/components/expandableNode/index.js @@ -12,7 +12,7 @@ export const ExpandableNode = ({ node, showMore, onClick }) => {
{hostname}
{showMore && -
+
e.stopPropagation()}> {ipv4 &&
IPv4: {ipv4}
} {ipv6 &&
IPv6: {ipv6}
} {board &&
{I18n.t('Device')}: {board}
}