From f0daa9371ec0a19c419f09706698146ef6681500 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 3 May 2023 12:42:56 -0400 Subject: [PATCH 01/49] initial layout and design tweaks (#234) --- ui/src/console/Console.js | 26 +++++++++++++++--------- ui/src/console/visualizer/Network.js | 30 ++++------------------------ ui/src/console/visualizer/graph.js | 4 ++-- ui/src/index.css | 11 +++++++++- 4 files changed, 33 insertions(+), 38 deletions(-) diff --git a/ui/src/console/Console.js b/ui/src/console/Console.js index ba8df307..5dae41cd 100644 --- a/ui/src/console/Console.js +++ b/ui/src/console/Console.js @@ -1,4 +1,4 @@ -import {Container, Nav, Navbar, NavDropdown} from "react-bootstrap"; +import {Col, Container, Nav, Navbar, NavDropdown, Row} from "react-bootstrap"; import {useEffect, useState} from "react"; import Visualizer from "./visualizer/Visualizer"; import Enable from "./modals/Enable"; @@ -64,14 +64,22 @@ const Console = (props) => { </Navbar.Collapse> </Container> </Navbar> - <Visualizer - user={props.user} - overview={overview} - defaultSelection={defaultSelection} - selection={selection} - setSelection={setSelection} - /> - <Detail user={props.user} selection={selection} /> + <Container fluid={"xl"}> + <Row id={"controls-row"}> + <Col> + <Visualizer + user={props.user} + overview={overview} + defaultSelection={defaultSelection} + selection={selection} + setSelection={setSelection} + /> + </Col> + <Col> + <Detail user={props.user} selection={selection} /> + </Col> + </Row> + </Container> <Enable show={showEnableModal} onHide={closeEnableModal} token={props.user.token} /> <Version show={showVersionModal} onHide={closeVersionModal} /> </Container> diff --git a/ui/src/console/visualizer/Network.js b/ui/src/console/visualizer/Network.js index 62819e59..7291e95b 100644 --- a/ui/src/console/visualizer/Network.js +++ b/ui/src/console/visualizer/Network.js @@ -16,20 +16,8 @@ const Network = (props) => { }, []); const paintNode = (node, ctx) => { - let nodeColor = "#636363"; - let textColor = "#ccc"; - switch(node.type) { - case "environment": - nodeColor = "#444"; - break; - - case "share": // share - nodeColor = "#291A66"; - break; - - default: - // - } + let nodeColor = node.selected ? "#04adef" : "#9BF316"; + let textColor = node.selected ? "white" : "black"; ctx.textBaseline = "middle"; ctx.textAlign = "center"; @@ -41,16 +29,6 @@ const Network = (props) => { roundRect(ctx, node.x - (nodeWidth / 2), node.y - 7, nodeWidth, 14, 1.25); ctx.fill(); - if(node.selected) { - ctx.strokeStyle = "#c4bdde"; - ctx.stroke(); - } else { - if(node.type === "share") { - ctx.strokeStyle = "#433482"; - ctx.stroke(); - } - } - ctx.fillStyle = textColor; ctx.fillText(node.label, node.x, node.y); } @@ -64,12 +42,12 @@ const Network = (props) => { ref={targetRef} graphData={props.networkGraph} width={props.size.width} - height={500} + height={800} onNodeClick={nodeClicked} linkOpacity={.75} linkWidth={1.5} nodeCanvasObject={paintNode} - backgroundColor={"#3b2693"} + backgroundColor={"linear-gradient(180deg, #0E0238 0%, #231069 100%);"} cooldownTicks={300} /> ) diff --git a/ui/src/console/visualizer/graph.js b/ui/src/console/visualizer/graph.js index 3661c04b..c2565ea8 100644 --- a/ui/src/console/visualizer/graph.js +++ b/ui/src/console/visualizer/graph.js @@ -40,7 +40,7 @@ export const mergeGraph = (oldGraph, user, newOverview) => { newGraph.links.push({ target: accountNode.id, source: envNode.id, - color: "#777" + color: "#9BF316" }); if(env.shares) { env.shares.forEach(shr => { @@ -59,7 +59,7 @@ export const mergeGraph = (oldGraph, user, newOverview) => { newGraph.links.push({ target: envNode.id, source: shrNode.id, - color: "#777" + color: "#9BF316" }); }); } diff --git a/ui/src/index.css b/ui/src/index.css index a7713c43..80c1f048 100644 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -25,7 +25,7 @@ code, pre { .visualizer-container { padding: 10px; - background: #3b2693; + background: linear-gradient(180deg, #0E0238 0%, #231069 100%); border-radius: 15px; margin-top: 15px; } @@ -120,4 +120,13 @@ code, pre { #zrok-tou { margin-top: 15px; +} + +#controls-row { + margin-left: -30px; + margin-right: -30px; +} + +#navbar { + background: linear-gradient(180deg, #0E0238 0%, #231069 100%); } \ No newline at end of file From 0f4a97549591648995bf6fc61d196c47c7e6e479 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 3 May 2023 14:11:09 -0400 Subject: [PATCH 02/49] responsive layout (#234) --- ui/src/console/Console.js | 4 ++-- ui/src/console/detail/environment/SharesTab.js | 11 ----------- ui/src/index.css | 2 +- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/ui/src/console/Console.js b/ui/src/console/Console.js index 5dae41cd..e615daca 100644 --- a/ui/src/console/Console.js +++ b/ui/src/console/Console.js @@ -66,7 +66,7 @@ const Console = (props) => { </Navbar> <Container fluid={"xl"}> <Row id={"controls-row"}> - <Col> + <Col lg={6}> <Visualizer user={props.user} overview={overview} @@ -75,7 +75,7 @@ const Console = (props) => { setSelection={setSelection} /> </Col> - <Col> + <Col lg={6}> <Detail user={props.user} selection={selection} /> </Col> </Row> diff --git a/ui/src/console/detail/environment/SharesTab.js b/ui/src/console/detail/environment/SharesTab.js index 333c8c7c..478d8c37 100644 --- a/ui/src/console/detail/environment/SharesTab.js +++ b/ui/src/console/detail/environment/SharesTab.js @@ -34,22 +34,11 @@ const SharesTab = (props) => { name: "Frontend", selector: row => <a href={row.frontendEndpoint} target={"_"}>{row.frontendEndpoint}</a>, sortable: true, - hide: "md" }, { name: "Backend", selector: row => row.backendProxyEndpoint, sortable: true, - }, - { - name: "Share Mode", - selector: row => row.shareMode, - hide: "md" - }, - { - name: "Token", - selector: row => row.token, - sortable: true, hide: "md" }, { diff --git a/ui/src/index.css b/ui/src/index.css index 80c1f048..7d4e69f7 100644 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -64,7 +64,7 @@ code, pre { } .fullscreen { - background-color: #3b2693; + background: linear-gradient(180deg, #0E0238 0%, #231069 100%); padding: 25px; color: white; display: flex; From 2da4f58f8119cb7e9c1fa41b6b82fd113ece6977 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 4 May 2023 14:44:26 -0400 Subject: [PATCH 03/49] explorer (visualizer) updates to include overlaid icons; updated react-force-graph (#234) --- ui/package-lock.json | 53 ++++++++++--------- ui/package.json | 2 +- .../console/detail/account/AccountDetail.js | 4 +- ui/src/console/visualizer/Network.js | 26 +++++++++ ui/src/console/visualizer/graph.js | 1 + 5 files changed, 58 insertions(+), 28 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 9a7cb891..b09c8141 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -21,7 +21,7 @@ "react-bootstrap": "^2.7.0", "react-data-table-component": "^7.5.2", "react-dom": "^18.2.0", - "react-force-graph": "^1.41.20", + "react-force-graph": "^1.43.0", "react-router-dom": "^6.4.0", "react-sizeme": "^3.0.2", "react-sparklines": "^1.7.0", @@ -16162,16 +16162,19 @@ "dev": true }, "node_modules/react-force-graph": { - "version": "1.41.20", - "resolved": "https://registry.npmjs.org/react-force-graph/-/react-force-graph-1.41.20.tgz", - "integrity": "sha512-PdhbYTdvciKJLv2tePTHY+fTzrLEhfcji6/lijPc9GVQfJOLofssAYz+HgcWCKCRj1CIT8M/J7FICXX3ALXpbw==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/react-force-graph/-/react-force-graph-1.43.0.tgz", + "integrity": "sha512-g59ZWGrR6hkokY8RMO6FQHbltaIZ3+AGf9mrQs+s1+J26Sc2Wc6aro4cLW8PTHMIHgX/zml44yp60gRbzdFSMw==", "dependencies": { - "3d-force-graph": "^1.70", - "3d-force-graph-ar": "^1.7", - "3d-force-graph-vr": "^2.0", - "force-graph": "^1.42", - "prop-types": "^15.8", - "react-kapsule": "^2.2" + "3d-force-graph": "1", + "3d-force-graph-ar": "1", + "3d-force-graph-vr": "2", + "force-graph": "1", + "prop-types": "15", + "react-kapsule": "2" + }, + "engines": { + "node": ">=12" }, "peerDependencies": { "react": "*" @@ -18018,9 +18021,9 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, "node_modules/three": { - "version": "0.149.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.149.0.tgz", - "integrity": "sha512-tohpUxPDht0qExRLDTM8sjRLc5d9STURNrdnK3w9A+V4pxaTBfKWWT/IqtiLfg23Vfc3Z+ImNfvRw1/0CtxrkQ==" + "version": "0.152.2", + "resolved": "https://registry.npmjs.org/three/-/three-0.152.2.tgz", + "integrity": "sha512-Ff9zIpSfkkqcBcpdiFo2f35vA9ZucO+N8TNacJOqaEE6DrB0eufItVMib8bK8Pcju/ZNT6a7blE1GhTpkdsILw==" }, "node_modules/three-bmfont-text": { "version": "2.4.0", @@ -31290,16 +31293,16 @@ "dev": true }, "react-force-graph": { - "version": "1.41.20", - "resolved": "https://registry.npmjs.org/react-force-graph/-/react-force-graph-1.41.20.tgz", - "integrity": "sha512-PdhbYTdvciKJLv2tePTHY+fTzrLEhfcji6/lijPc9GVQfJOLofssAYz+HgcWCKCRj1CIT8M/J7FICXX3ALXpbw==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/react-force-graph/-/react-force-graph-1.43.0.tgz", + "integrity": "sha512-g59ZWGrR6hkokY8RMO6FQHbltaIZ3+AGf9mrQs+s1+J26Sc2Wc6aro4cLW8PTHMIHgX/zml44yp60gRbzdFSMw==", "requires": { - "3d-force-graph": "^1.70", - "3d-force-graph-ar": "^1.7", - "3d-force-graph-vr": "^2.0", - "force-graph": "^1.42", - "prop-types": "^15.8", - "react-kapsule": "^2.2" + "3d-force-graph": "1", + "3d-force-graph-ar": "1", + "3d-force-graph-vr": "2", + "force-graph": "1", + "prop-types": "15", + "react-kapsule": "2" } }, "react-is": { @@ -32658,9 +32661,9 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, "three": { - "version": "0.149.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.149.0.tgz", - "integrity": "sha512-tohpUxPDht0qExRLDTM8sjRLc5d9STURNrdnK3w9A+V4pxaTBfKWWT/IqtiLfg23Vfc3Z+ImNfvRw1/0CtxrkQ==" + "version": "0.152.2", + "resolved": "https://registry.npmjs.org/three/-/three-0.152.2.tgz", + "integrity": "sha512-Ff9zIpSfkkqcBcpdiFo2f35vA9ZucO+N8TNacJOqaEE6DrB0eufItVMib8bK8Pcju/ZNT6a7blE1GhTpkdsILw==" }, "three-bmfont-text": { "version": "git+ssh://git@github.com/dmarcos/three-bmfont-text.git#21d017046216e318362c48abd1a48bddfb6e0733", diff --git a/ui/package.json b/ui/package.json index c662144d..c465eb15 100644 --- a/ui/package.json +++ b/ui/package.json @@ -16,7 +16,7 @@ "react-bootstrap": "^2.7.0", "react-data-table-component": "^7.5.2", "react-dom": "^18.2.0", - "react-force-graph": "^1.41.20", + "react-force-graph": "^1.43.0", "react-router-dom": "^6.4.0", "react-sizeme": "^3.0.2", "react-sparklines": "^1.7.0", diff --git a/ui/src/console/detail/account/AccountDetail.js b/ui/src/console/detail/account/AccountDetail.js index cad7bcef..679b51aa 100644 --- a/ui/src/console/detail/account/AccountDetail.js +++ b/ui/src/console/detail/account/AccountDetail.js @@ -1,4 +1,4 @@ -import {mdiCardAccountDetails} from "@mdi/js"; +import {mdiAccountBox} from "@mdi/js"; import Icon from "@mdi/react"; import PropertyTable from "../../PropertyTable"; import {Tab, Tabs} from "react-bootstrap"; @@ -11,7 +11,7 @@ const AccountDetail = (props) => { return ( <div> - <h2><Icon path={mdiCardAccountDetails} size={2} />{" "}{props.user.email}</h2> + <h2><Icon path={mdiAccountBox} size={2} />{" "}{props.user.email}</h2> <Tabs defaultActiveKey={"detail"}> <Tab eventKey={"detail"} title={"Detail"}> <PropertyTable object={props.user} custom={customProperties}/> diff --git a/ui/src/console/visualizer/Network.js b/ui/src/console/visualizer/Network.js index 7291e95b..3de59eb9 100644 --- a/ui/src/console/visualizer/Network.js +++ b/ui/src/console/visualizer/Network.js @@ -3,6 +3,11 @@ import {useEffect, useRef} from "react"; import {ForceGraph2D} from "react-force-graph"; import * as d3 from "d3-force-3d"; import {roundRect} from "./draw"; +import {mdiShareVariant, mdiConsoleNetwork, mdiAccountBox} from "@mdi/js"; + +const accountIcon = new Path2D(mdiAccountBox); +const environmentIcon = new Path2D(mdiConsoleNetwork); +const shareIcon = new Path2D(mdiShareVariant); const Network = (props) => { const targetRef = useRef(); @@ -29,6 +34,27 @@ const Network = (props) => { roundRect(ctx, node.x - (nodeWidth / 2), node.y - 7, nodeWidth, 14, 1.25); ctx.fill(); + const nodeIcon = new Path2D(); + let xform = new DOMMatrix(); + xform.translateSelf(node.x - (nodeWidth / 2) - 6, node.y - 13); + xform.scaleSelf(0.5, 0.5); + switch(node.type) { + case "share": + nodeIcon.addPath(shareIcon, xform); + break; + case "environment": + nodeIcon.addPath(environmentIcon, xform); + break; + case "account": + nodeIcon.addPath(accountIcon, xform); + break; + } + + ctx.fill(nodeIcon); + ctx.strokeStyle = "black"; + ctx.lineWidth = 0.5; + ctx.stroke(nodeIcon); + ctx.fillStyle = textColor; ctx.fillText(node.label, node.x, node.y); } diff --git a/ui/src/console/visualizer/graph.js b/ui/src/console/visualizer/graph.js index c2565ea8..4e068555 100644 --- a/ui/src/console/visualizer/graph.js +++ b/ui/src/console/visualizer/graph.js @@ -55,6 +55,7 @@ export const mergeGraph = (oldGraph, user, newOverview) => { type: "share", val: 50 }; + console.log('share', shrNode.label); newGraph.nodes.push(shrNode); newGraph.links.push({ target: envNode.id, From 94d02dd64cd833c84fe459d724d63988cb14ce51 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Mon, 8 May 2023 13:49:20 -0400 Subject: [PATCH 04/49] replace 'react-sparklines' with 'recharts' to handle more intensive metrics representations (#234) --- ui/package-lock.json | 409 +++++++++++++++++- ui/package.json | 2 +- .../console/detail/environment/SharesTab.js | 10 +- ui/src/console/detail/share/ShareDetail.js | 13 +- 4 files changed, 407 insertions(+), 27 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index b09c8141..845ba995 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -24,7 +24,7 @@ "react-force-graph": "^1.43.0", "react-router-dom": "^6.4.0", "react-sizeme": "^3.0.2", - "react-sparklines": "^1.7.0", + "recharts": "^2.5.0", "styled-components": "^5.3.5", "svgo": "^3.0.2" }, @@ -4145,6 +4145,60 @@ "@types/node": "*" } }, + "node_modules/@types/d3-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.4.tgz", + "integrity": "sha512-nwvEkG9vYOc0Ic7G7kwgviY4AQlTfYGIZ0fqB7CQHXGyYM6nO7kJh5EguSNA3jfh4rq7Sb7eMVq8isuvg2/miQ==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", + "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", + "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", + "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.1.tgz", + "integrity": "sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", + "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" + }, "node_modules/@types/eslint": { "version": "8.21.0", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.0.tgz", @@ -6879,6 +6933,11 @@ "postcss-value-parser": "^4.0.2" } }, + "node_modules/css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -7153,6 +7212,14 @@ "resolved": "https://registry.npmjs.org/d3-octree/-/d3-octree-0.2.2.tgz", "integrity": "sha512-ysk9uSPAhZVb0Gq4GXzghl/Yqxu80dHrq55I53qaIMdGB65+0UfO84sr4Fci2JHumcgh6H4WE0r8LwxPagkE+g==" }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-quadtree": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", @@ -7196,6 +7263,17 @@ "node": ">=12" } }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-time": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", @@ -7322,6 +7400,11 @@ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "node_modules/decode-uri-component": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", @@ -8726,8 +8809,7 @@ "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, "node_modules/events": { "version": "3.3.0", @@ -8853,6 +8935,11 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-equals": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", + "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==" + }, "node_modules/fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", @@ -16211,6 +16298,18 @@ "node": ">=0.10.0" } }, + "node_modules/react-resize-detector": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.1.0.tgz", + "integrity": "sha512-S7szxlaIuiy5UqLhLL1KY3aoyGHbZzsTpYal9eYMwCyKqoqoVLCmIgAgNyIM1FhnP2KyBygASJxdhejrzjMb+w==", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-router": { "version": "6.8.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.0.tgz", @@ -16358,16 +16457,41 @@ "throttle-debounce": "^3.0.1" } }, - "node_modules/react-sparklines": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/react-sparklines/-/react-sparklines-1.7.0.tgz", - "integrity": "sha512-bJFt9K4c5Z0k44G8KtxIhbG+iyxrKjBZhdW6afP+R7EnIq+iKjbWbEFISrf3WKNFsda+C46XAfnX0StS5fbDcg==", + "node_modules/react-smooth": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.2.tgz", + "integrity": "sha512-pgqSp1q8rAGtF1bXQE0m3CHGLNfZZh5oA5o1tsPLXRHnKtkujMIJ8Ws5nO1mTySZf1c4vgwlEk+pHi3Ln6eYLw==", "dependencies": { - "prop-types": "^15.5.10" + "fast-equals": "^4.0.3", + "react-transition-group": "2.9.0" }, "peerDependencies": { - "react": "*", - "react-dom": "*" + "prop-types": "^15.6.0", + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-smooth/node_modules/dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/react-smooth/node_modules/react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "dependencies": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" } }, "node_modules/react-transition-group": { @@ -16420,6 +16544,43 @@ "node": ">=8.10.0" } }, + "node_modules/recharts": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.5.0.tgz", + "integrity": "sha512-0EQYz3iA18r1Uq8VqGZ4dABW52AKBnio37kJgnztIqprELJXpOEsa0SzkqU1vjAhpCXCv52Dx1hiL9119xsqsQ==", + "dependencies": { + "classnames": "^2.2.5", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "^16.10.2", + "react-resize-detector": "^8.0.4", + "react-smooth": "^2.0.2", + "recharts-scale": "^0.4.4", + "reduce-css-calc": "^2.1.8", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "prop-types": "^15.6.0", + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/recharts/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/recursive-readdir": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", @@ -16432,6 +16593,20 @@ "node": ">=6.0.0" } }, + "node_modules/reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "dependencies": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/reduce-css-calc/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -18569,6 +18744,27 @@ "node": ">= 0.8" } }, + "node_modules/victory-vendor": { + "version": "36.6.10", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.10.tgz", + "integrity": "sha512-7YqYGtsA4mByokBhCjk+ewwPhUfzhR1I3Da6/ZsZUv/31ceT77RKoaqrxRq5Ki+9we4uzf7+A+7aG2sfYhm7nA==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -22458,6 +22654,60 @@ "@types/node": "*" } }, + "@types/d3-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.4.tgz", + "integrity": "sha512-nwvEkG9vYOc0Ic7G7kwgviY4AQlTfYGIZ0fqB7CQHXGyYM6nO7kJh5EguSNA3jfh4rq7Sb7eMVq8isuvg2/miQ==" + }, + "@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + }, + "@types/d3-ease": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", + "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" + }, + "@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", + "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==" + }, + "@types/d3-scale": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", + "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-shape": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.1.tgz", + "integrity": "sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==", + "requires": { + "@types/d3-path": "*" + } + }, + "@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + }, + "@types/d3-timer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", + "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" + }, "@types/eslint": { "version": "8.21.0", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.0.tgz", @@ -24550,6 +24800,11 @@ "postcss-value-parser": "^4.0.2" } }, + "css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, "css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -24755,6 +25010,11 @@ "resolved": "https://registry.npmjs.org/d3-octree/-/d3-octree-0.2.2.tgz", "integrity": "sha512-ysk9uSPAhZVb0Gq4GXzghl/Yqxu80dHrq55I53qaIMdGB65+0UfO84sr4Fci2JHumcgh6H4WE0r8LwxPagkE+g==" }, + "d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==" + }, "d3-quadtree": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", @@ -24786,6 +25046,14 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" }, + "d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "requires": { + "d3-path": "^3.1.0" + } + }, "d3-time": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", @@ -24883,6 +25151,11 @@ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true }, + "decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "decode-uri-component": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", @@ -25927,8 +26200,7 @@ "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, "events": { "version": "3.3.0", @@ -26038,6 +26310,11 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-equals": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", + "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==" + }, "fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", @@ -31330,6 +31607,14 @@ "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", "dev": true }, + "react-resize-detector": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.1.0.tgz", + "integrity": "sha512-S7szxlaIuiy5UqLhLL1KY3aoyGHbZzsTpYal9eYMwCyKqoqoVLCmIgAgNyIM1FhnP2KyBygASJxdhejrzjMb+w==", + "requires": { + "lodash": "^4.17.21" + } + }, "react-router": { "version": "6.8.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.0.tgz", @@ -31440,12 +31725,34 @@ "throttle-debounce": "^3.0.1" } }, - "react-sparklines": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/react-sparklines/-/react-sparklines-1.7.0.tgz", - "integrity": "sha512-bJFt9K4c5Z0k44G8KtxIhbG+iyxrKjBZhdW6afP+R7EnIq+iKjbWbEFISrf3WKNFsda+C46XAfnX0StS5fbDcg==", + "react-smooth": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.2.tgz", + "integrity": "sha512-pgqSp1q8rAGtF1bXQE0m3CHGLNfZZh5oA5o1tsPLXRHnKtkujMIJ8Ws5nO1mTySZf1c4vgwlEk+pHi3Ln6eYLw==", "requires": { - "prop-types": "^15.5.10" + "fast-equals": "^4.0.3", + "react-transition-group": "2.9.0" + }, + "dependencies": { + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + } } }, "react-transition-group": { @@ -31488,6 +31795,37 @@ "picomatch": "^2.2.1" } }, + "recharts": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.5.0.tgz", + "integrity": "sha512-0EQYz3iA18r1Uq8VqGZ4dABW52AKBnio37kJgnztIqprELJXpOEsa0SzkqU1vjAhpCXCv52Dx1hiL9119xsqsQ==", + "requires": { + "classnames": "^2.2.5", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "^16.10.2", + "react-resize-detector": "^8.0.4", + "react-smooth": "^2.0.2", + "recharts-scale": "^0.4.4", + "reduce-css-calc": "^2.1.8", + "victory-vendor": "^36.6.8" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "requires": { + "decimal.js-light": "^2.4.1" + } + }, "recursive-readdir": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", @@ -31497,6 +31835,22 @@ "minimatch": "^3.0.5" } }, + "reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "requires": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -33080,6 +33434,27 @@ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, + "victory-vendor": { + "version": "36.6.10", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.10.tgz", + "integrity": "sha512-7YqYGtsA4mByokBhCjk+ewwPhUfzhR1I3Da6/ZsZUv/31ceT77RKoaqrxRq5Ki+9we4uzf7+A+7aG2sfYhm7nA==", + "requires": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/ui/package.json b/ui/package.json index c465eb15..782354c0 100644 --- a/ui/package.json +++ b/ui/package.json @@ -19,7 +19,7 @@ "react-force-graph": "^1.43.0", "react-router-dom": "^6.4.0", "react-sizeme": "^3.0.2", - "react-sparklines": "^1.7.0", + "recharts": "^2.5.0", "styled-components": "^5.3.5", "svgo": "^3.0.2" }, diff --git a/ui/src/console/detail/environment/SharesTab.js b/ui/src/console/detail/environment/SharesTab.js index 478d8c37..550a1e5c 100644 --- a/ui/src/console/detail/environment/SharesTab.js +++ b/ui/src/console/detail/environment/SharesTab.js @@ -1,7 +1,7 @@ import * as metadata from "../../../api/metadata"; -import {useEffect, useState} from "react"; +import React, {useEffect, useState} from "react"; import DataTable from 'react-data-table-component'; -import {Sparklines, SparklinesLine, SparklinesSpots} from "react-sparklines"; +import {Area, AreaChart, ResponsiveContainer} from "recharts"; const SharesTab = (props) => { const [detail, setDetail] = useState({}); @@ -44,7 +44,11 @@ const SharesTab = (props) => { { name: "Activity", cell: row => { - return <Sparklines data={row.metrics} height={20} limit={60}><SparklinesLine color={"#3b2693"}/><SparklinesSpots/></Sparklines>; + return <ResponsiveContainer width={"100%"} height={"100%"}> + <AreaChart data={row.metrics}> + <Area type="linearClosed" dataKey={(v) => v} stroke={"#231069"} fillOpacity={1} fill={"#655796"} isAnimationActive={false} dot={false} /> + </AreaChart> + </ResponsiveContainer> } } ]; diff --git a/ui/src/console/detail/share/ShareDetail.js b/ui/src/console/detail/share/ShareDetail.js index be78206b..0b5792b3 100644 --- a/ui/src/console/detail/share/ShareDetail.js +++ b/ui/src/console/detail/share/ShareDetail.js @@ -1,12 +1,12 @@ import * as metadata from "../../../api/metadata"; -import {Sparklines, SparklinesLine, SparklinesSpots} from "react-sparklines"; -import {useEffect, useState} from "react"; +import React, {useEffect, useState} from "react"; import {mdiShareVariant} from "@mdi/js"; import Icon from "@mdi/react"; import PropertyTable from "../../PropertyTable"; import {Tab, Tabs} from "react-bootstrap"; import ActionsTab from "./ActionsTab"; import SecretToggle from "../../SecretToggle"; +import {Area, AreaChart, Line, LineChart, ResponsiveContainer, XAxis} from "recharts"; const ShareDetail = (props) => { const [detail, setDetail] = useState({}); @@ -40,10 +40,11 @@ const ShareDetail = (props) => { const customProperties = { metrics: row => ( - <Sparklines data={row.value} limit={60} height={10}> - <SparklinesLine color={"#3b2693"}/> - <SparklinesSpots/> - </Sparklines> + <ResponsiveContainer width={"100%"} height={"100%"}> + <AreaChart data={row.value}> + <Area type="linearClosed" dataKey={(v) => v} stroke={"#231069"} fillOpacity={1} fill={"#04adef"} isAnimationActive={false} dot={false} /> + </AreaChart> + </ResponsiveContainer> ), frontendEndpoint: row => ( <a href={row.value} target="_">{row.value}</a> From 645537934ee283a16fb108ba03d41623c638e488 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Mon, 8 May 2023 13:51:47 -0400 Subject: [PATCH 05/49] colors; amirite? (#234) --- ui/src/console/detail/share/ShareDetail.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/console/detail/share/ShareDetail.js b/ui/src/console/detail/share/ShareDetail.js index 0b5792b3..8b86171c 100644 --- a/ui/src/console/detail/share/ShareDetail.js +++ b/ui/src/console/detail/share/ShareDetail.js @@ -42,7 +42,7 @@ const ShareDetail = (props) => { metrics: row => ( <ResponsiveContainer width={"100%"} height={"100%"}> <AreaChart data={row.value}> - <Area type="linearClosed" dataKey={(v) => v} stroke={"#231069"} fillOpacity={1} fill={"#04adef"} isAnimationActive={false} dot={false} /> + <Area type="linearClosed" dataKey={(v) => v} stroke={"#231069"} fillOpacity={1} fill={"#655796"} isAnimationActive={false} dot={false} /> </AreaChart> </ResponsiveContainer> ), From a4b6313b2d6949b4078e554aaaa90c6d1f1b024d Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Mon, 8 May 2023 13:56:27 -0400 Subject: [PATCH 06/49] tweaks (#234) --- ui/src/console/detail/environment/SharesTab.js | 2 +- ui/src/console/detail/share/ShareDetail.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/console/detail/environment/SharesTab.js b/ui/src/console/detail/environment/SharesTab.js index 550a1e5c..e902529c 100644 --- a/ui/src/console/detail/environment/SharesTab.js +++ b/ui/src/console/detail/environment/SharesTab.js @@ -46,7 +46,7 @@ const SharesTab = (props) => { cell: row => { return <ResponsiveContainer width={"100%"} height={"100%"}> <AreaChart data={row.metrics}> - <Area type="linearClosed" dataKey={(v) => v} stroke={"#231069"} fillOpacity={1} fill={"#655796"} isAnimationActive={false} dot={false} /> + <Area type="basis" dataKey={(v) => v} stroke={"#231069"} fillOpacity={1} fill={"#655796"} isAnimationActive={false} dot={false} /> </AreaChart> </ResponsiveContainer> } diff --git a/ui/src/console/detail/share/ShareDetail.js b/ui/src/console/detail/share/ShareDetail.js index 8b86171c..78371e1f 100644 --- a/ui/src/console/detail/share/ShareDetail.js +++ b/ui/src/console/detail/share/ShareDetail.js @@ -42,7 +42,7 @@ const ShareDetail = (props) => { metrics: row => ( <ResponsiveContainer width={"100%"} height={"100%"}> <AreaChart data={row.value}> - <Area type="linearClosed" dataKey={(v) => v} stroke={"#231069"} fillOpacity={1} fill={"#655796"} isAnimationActive={false} dot={false} /> + <Area type="basis" dataKey={(v) => v} stroke={"#231069"} fillOpacity={1} fill={"#655796"} isAnimationActive={false} dot={false} /> </AreaChart> </ResponsiveContainer> ), From 58a225284d6742a1b71ec51dc8e31f4d1efb8884 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Mon, 8 May 2023 14:33:15 -0400 Subject: [PATCH 07/49] new definitions for metrics api endpoints; strawman (#319) --- .../metrics/get_account_metrics_parameters.go | 128 ++++++++++ .../metrics/get_account_metrics_responses.go | 98 ++++++++ .../get_environment_metrics_parameters.go | 148 ++++++++++++ .../get_environment_metrics_responses.go | 155 ++++++++++++ .../metrics/get_share_metrics_parameters.go | 148 ++++++++++++ .../metrics/get_share_metrics_responses.go | 155 ++++++++++++ rest_client_zrok/metrics/metrics_client.go | 162 +++++++++++++ rest_client_zrok/zrok_client.go | 5 + rest_model_zrok/metrics.go | 62 +++++ rest_server_zrok/embedded_spec.go | 222 ++++++++++++++++++ .../operations/metrics/get_account_metrics.go | 71 ++++++ .../metrics/get_account_metrics_parameters.go | 46 ++++ .../metrics/get_account_metrics_responses.go | 59 +++++ .../metrics/get_account_metrics_urlbuilder.go | 87 +++++++ .../metrics/get_environment_metrics.go | 71 ++++++ .../get_environment_metrics_parameters.go | 71 ++++++ .../get_environment_metrics_responses.go | 84 +++++++ .../get_environment_metrics_urlbuilder.go | 99 ++++++++ .../operations/metrics/get_share_metrics.go | 71 ++++++ .../metrics/get_share_metrics_parameters.go | 71 ++++++ .../metrics/get_share_metrics_responses.go | 84 +++++++ .../metrics/get_share_metrics_urlbuilder.go | 99 ++++++++ rest_server_zrok/operations/zrok_api.go | 37 +++ specs/zrok.yml | 74 ++++++ ui/src/api/metrics.js | 65 +++++ ui/src/api/types.js | 11 + 26 files changed, 2383 insertions(+) create mode 100644 rest_client_zrok/metrics/get_account_metrics_parameters.go create mode 100644 rest_client_zrok/metrics/get_account_metrics_responses.go create mode 100644 rest_client_zrok/metrics/get_environment_metrics_parameters.go create mode 100644 rest_client_zrok/metrics/get_environment_metrics_responses.go create mode 100644 rest_client_zrok/metrics/get_share_metrics_parameters.go create mode 100644 rest_client_zrok/metrics/get_share_metrics_responses.go create mode 100644 rest_client_zrok/metrics/metrics_client.go create mode 100644 rest_model_zrok/metrics.go create mode 100644 rest_server_zrok/operations/metrics/get_account_metrics.go create mode 100644 rest_server_zrok/operations/metrics/get_account_metrics_parameters.go create mode 100644 rest_server_zrok/operations/metrics/get_account_metrics_responses.go create mode 100644 rest_server_zrok/operations/metrics/get_account_metrics_urlbuilder.go create mode 100644 rest_server_zrok/operations/metrics/get_environment_metrics.go create mode 100644 rest_server_zrok/operations/metrics/get_environment_metrics_parameters.go create mode 100644 rest_server_zrok/operations/metrics/get_environment_metrics_responses.go create mode 100644 rest_server_zrok/operations/metrics/get_environment_metrics_urlbuilder.go create mode 100644 rest_server_zrok/operations/metrics/get_share_metrics.go create mode 100644 rest_server_zrok/operations/metrics/get_share_metrics_parameters.go create mode 100644 rest_server_zrok/operations/metrics/get_share_metrics_responses.go create mode 100644 rest_server_zrok/operations/metrics/get_share_metrics_urlbuilder.go create mode 100644 ui/src/api/metrics.js diff --git a/rest_client_zrok/metrics/get_account_metrics_parameters.go b/rest_client_zrok/metrics/get_account_metrics_parameters.go new file mode 100644 index 00000000..5af21fb7 --- /dev/null +++ b/rest_client_zrok/metrics/get_account_metrics_parameters.go @@ -0,0 +1,128 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewGetAccountMetricsParams creates a new GetAccountMetricsParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetAccountMetricsParams() *GetAccountMetricsParams { + return &GetAccountMetricsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetAccountMetricsParamsWithTimeout creates a new GetAccountMetricsParams object +// with the ability to set a timeout on a request. +func NewGetAccountMetricsParamsWithTimeout(timeout time.Duration) *GetAccountMetricsParams { + return &GetAccountMetricsParams{ + timeout: timeout, + } +} + +// NewGetAccountMetricsParamsWithContext creates a new GetAccountMetricsParams object +// with the ability to set a context for a request. +func NewGetAccountMetricsParamsWithContext(ctx context.Context) *GetAccountMetricsParams { + return &GetAccountMetricsParams{ + Context: ctx, + } +} + +// NewGetAccountMetricsParamsWithHTTPClient creates a new GetAccountMetricsParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetAccountMetricsParamsWithHTTPClient(client *http.Client) *GetAccountMetricsParams { + return &GetAccountMetricsParams{ + HTTPClient: client, + } +} + +/* +GetAccountMetricsParams contains all the parameters to send to the API endpoint + + for the get account metrics operation. + + Typically these are written to a http.Request. +*/ +type GetAccountMetricsParams struct { + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get account metrics params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetAccountMetricsParams) WithDefaults() *GetAccountMetricsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get account metrics params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetAccountMetricsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get account metrics params +func (o *GetAccountMetricsParams) WithTimeout(timeout time.Duration) *GetAccountMetricsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get account metrics params +func (o *GetAccountMetricsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get account metrics params +func (o *GetAccountMetricsParams) WithContext(ctx context.Context) *GetAccountMetricsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get account metrics params +func (o *GetAccountMetricsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get account metrics params +func (o *GetAccountMetricsParams) WithHTTPClient(client *http.Client) *GetAccountMetricsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get account metrics params +func (o *GetAccountMetricsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WriteToRequest writes these params to a swagger request +func (o *GetAccountMetricsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_client_zrok/metrics/get_account_metrics_responses.go b/rest_client_zrok/metrics/get_account_metrics_responses.go new file mode 100644 index 00000000..4768551f --- /dev/null +++ b/rest_client_zrok/metrics/get_account_metrics_responses.go @@ -0,0 +1,98 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetAccountMetricsReader is a Reader for the GetAccountMetrics structure. +type GetAccountMetricsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetAccountMetricsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetAccountMetricsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewGetAccountMetricsOK creates a GetAccountMetricsOK with default headers values +func NewGetAccountMetricsOK() *GetAccountMetricsOK { + return &GetAccountMetricsOK{} +} + +/* +GetAccountMetricsOK describes a response with status code 200, with default header values. + +account metrics +*/ +type GetAccountMetricsOK struct { + Payload *rest_model_zrok.Metrics +} + +// IsSuccess returns true when this get account metrics o k response has a 2xx status code +func (o *GetAccountMetricsOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get account metrics o k response has a 3xx status code +func (o *GetAccountMetricsOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get account metrics o k response has a 4xx status code +func (o *GetAccountMetricsOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get account metrics o k response has a 5xx status code +func (o *GetAccountMetricsOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get account metrics o k response a status code equal to that given +func (o *GetAccountMetricsOK) IsCode(code int) bool { + return code == 200 +} + +func (o *GetAccountMetricsOK) Error() string { + return fmt.Sprintf("[GET /metrics/account][%d] getAccountMetricsOK %+v", 200, o.Payload) +} + +func (o *GetAccountMetricsOK) String() string { + return fmt.Sprintf("[GET /metrics/account][%d] getAccountMetricsOK %+v", 200, o.Payload) +} + +func (o *GetAccountMetricsOK) GetPayload() *rest_model_zrok.Metrics { + return o.Payload +} + +func (o *GetAccountMetricsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(rest_model_zrok.Metrics) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/rest_client_zrok/metrics/get_environment_metrics_parameters.go b/rest_client_zrok/metrics/get_environment_metrics_parameters.go new file mode 100644 index 00000000..538d0074 --- /dev/null +++ b/rest_client_zrok/metrics/get_environment_metrics_parameters.go @@ -0,0 +1,148 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewGetEnvironmentMetricsParams creates a new GetEnvironmentMetricsParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetEnvironmentMetricsParams() *GetEnvironmentMetricsParams { + return &GetEnvironmentMetricsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetEnvironmentMetricsParamsWithTimeout creates a new GetEnvironmentMetricsParams object +// with the ability to set a timeout on a request. +func NewGetEnvironmentMetricsParamsWithTimeout(timeout time.Duration) *GetEnvironmentMetricsParams { + return &GetEnvironmentMetricsParams{ + timeout: timeout, + } +} + +// NewGetEnvironmentMetricsParamsWithContext creates a new GetEnvironmentMetricsParams object +// with the ability to set a context for a request. +func NewGetEnvironmentMetricsParamsWithContext(ctx context.Context) *GetEnvironmentMetricsParams { + return &GetEnvironmentMetricsParams{ + Context: ctx, + } +} + +// NewGetEnvironmentMetricsParamsWithHTTPClient creates a new GetEnvironmentMetricsParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetEnvironmentMetricsParamsWithHTTPClient(client *http.Client) *GetEnvironmentMetricsParams { + return &GetEnvironmentMetricsParams{ + HTTPClient: client, + } +} + +/* +GetEnvironmentMetricsParams contains all the parameters to send to the API endpoint + + for the get environment metrics operation. + + Typically these are written to a http.Request. +*/ +type GetEnvironmentMetricsParams struct { + + // EnvID. + EnvID string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get environment metrics params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetEnvironmentMetricsParams) WithDefaults() *GetEnvironmentMetricsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get environment metrics params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetEnvironmentMetricsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get environment metrics params +func (o *GetEnvironmentMetricsParams) WithTimeout(timeout time.Duration) *GetEnvironmentMetricsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get environment metrics params +func (o *GetEnvironmentMetricsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get environment metrics params +func (o *GetEnvironmentMetricsParams) WithContext(ctx context.Context) *GetEnvironmentMetricsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get environment metrics params +func (o *GetEnvironmentMetricsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get environment metrics params +func (o *GetEnvironmentMetricsParams) WithHTTPClient(client *http.Client) *GetEnvironmentMetricsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get environment metrics params +func (o *GetEnvironmentMetricsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithEnvID adds the envID to the get environment metrics params +func (o *GetEnvironmentMetricsParams) WithEnvID(envID string) *GetEnvironmentMetricsParams { + o.SetEnvID(envID) + return o +} + +// SetEnvID adds the envId to the get environment metrics params +func (o *GetEnvironmentMetricsParams) SetEnvID(envID string) { + o.EnvID = envID +} + +// WriteToRequest writes these params to a swagger request +func (o *GetEnvironmentMetricsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param envId + if err := r.SetPathParam("envId", o.EnvID); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_client_zrok/metrics/get_environment_metrics_responses.go b/rest_client_zrok/metrics/get_environment_metrics_responses.go new file mode 100644 index 00000000..9f555e8b --- /dev/null +++ b/rest_client_zrok/metrics/get_environment_metrics_responses.go @@ -0,0 +1,155 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetEnvironmentMetricsReader is a Reader for the GetEnvironmentMetrics structure. +type GetEnvironmentMetricsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetEnvironmentMetricsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetEnvironmentMetricsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 401: + result := NewGetEnvironmentMetricsUnauthorized() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewGetEnvironmentMetricsOK creates a GetEnvironmentMetricsOK with default headers values +func NewGetEnvironmentMetricsOK() *GetEnvironmentMetricsOK { + return &GetEnvironmentMetricsOK{} +} + +/* +GetEnvironmentMetricsOK describes a response with status code 200, with default header values. + +environment metrics +*/ +type GetEnvironmentMetricsOK struct { + Payload *rest_model_zrok.Metrics +} + +// IsSuccess returns true when this get environment metrics o k response has a 2xx status code +func (o *GetEnvironmentMetricsOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get environment metrics o k response has a 3xx status code +func (o *GetEnvironmentMetricsOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get environment metrics o k response has a 4xx status code +func (o *GetEnvironmentMetricsOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get environment metrics o k response has a 5xx status code +func (o *GetEnvironmentMetricsOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get environment metrics o k response a status code equal to that given +func (o *GetEnvironmentMetricsOK) IsCode(code int) bool { + return code == 200 +} + +func (o *GetEnvironmentMetricsOK) Error() string { + return fmt.Sprintf("[GET /metrics/environment/{envId}][%d] getEnvironmentMetricsOK %+v", 200, o.Payload) +} + +func (o *GetEnvironmentMetricsOK) String() string { + return fmt.Sprintf("[GET /metrics/environment/{envId}][%d] getEnvironmentMetricsOK %+v", 200, o.Payload) +} + +func (o *GetEnvironmentMetricsOK) GetPayload() *rest_model_zrok.Metrics { + return o.Payload +} + +func (o *GetEnvironmentMetricsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(rest_model_zrok.Metrics) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetEnvironmentMetricsUnauthorized creates a GetEnvironmentMetricsUnauthorized with default headers values +func NewGetEnvironmentMetricsUnauthorized() *GetEnvironmentMetricsUnauthorized { + return &GetEnvironmentMetricsUnauthorized{} +} + +/* +GetEnvironmentMetricsUnauthorized describes a response with status code 401, with default header values. + +unauthorized +*/ +type GetEnvironmentMetricsUnauthorized struct { +} + +// IsSuccess returns true when this get environment metrics unauthorized response has a 2xx status code +func (o *GetEnvironmentMetricsUnauthorized) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get environment metrics unauthorized response has a 3xx status code +func (o *GetEnvironmentMetricsUnauthorized) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get environment metrics unauthorized response has a 4xx status code +func (o *GetEnvironmentMetricsUnauthorized) IsClientError() bool { + return true +} + +// IsServerError returns true when this get environment metrics unauthorized response has a 5xx status code +func (o *GetEnvironmentMetricsUnauthorized) IsServerError() bool { + return false +} + +// IsCode returns true when this get environment metrics unauthorized response a status code equal to that given +func (o *GetEnvironmentMetricsUnauthorized) IsCode(code int) bool { + return code == 401 +} + +func (o *GetEnvironmentMetricsUnauthorized) Error() string { + return fmt.Sprintf("[GET /metrics/environment/{envId}][%d] getEnvironmentMetricsUnauthorized ", 401) +} + +func (o *GetEnvironmentMetricsUnauthorized) String() string { + return fmt.Sprintf("[GET /metrics/environment/{envId}][%d] getEnvironmentMetricsUnauthorized ", 401) +} + +func (o *GetEnvironmentMetricsUnauthorized) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} diff --git a/rest_client_zrok/metrics/get_share_metrics_parameters.go b/rest_client_zrok/metrics/get_share_metrics_parameters.go new file mode 100644 index 00000000..690b280f --- /dev/null +++ b/rest_client_zrok/metrics/get_share_metrics_parameters.go @@ -0,0 +1,148 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewGetShareMetricsParams creates a new GetShareMetricsParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetShareMetricsParams() *GetShareMetricsParams { + return &GetShareMetricsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetShareMetricsParamsWithTimeout creates a new GetShareMetricsParams object +// with the ability to set a timeout on a request. +func NewGetShareMetricsParamsWithTimeout(timeout time.Duration) *GetShareMetricsParams { + return &GetShareMetricsParams{ + timeout: timeout, + } +} + +// NewGetShareMetricsParamsWithContext creates a new GetShareMetricsParams object +// with the ability to set a context for a request. +func NewGetShareMetricsParamsWithContext(ctx context.Context) *GetShareMetricsParams { + return &GetShareMetricsParams{ + Context: ctx, + } +} + +// NewGetShareMetricsParamsWithHTTPClient creates a new GetShareMetricsParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetShareMetricsParamsWithHTTPClient(client *http.Client) *GetShareMetricsParams { + return &GetShareMetricsParams{ + HTTPClient: client, + } +} + +/* +GetShareMetricsParams contains all the parameters to send to the API endpoint + + for the get share metrics operation. + + Typically these are written to a http.Request. +*/ +type GetShareMetricsParams struct { + + // ShrToken. + ShrToken string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get share metrics params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetShareMetricsParams) WithDefaults() *GetShareMetricsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get share metrics params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetShareMetricsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get share metrics params +func (o *GetShareMetricsParams) WithTimeout(timeout time.Duration) *GetShareMetricsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get share metrics params +func (o *GetShareMetricsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get share metrics params +func (o *GetShareMetricsParams) WithContext(ctx context.Context) *GetShareMetricsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get share metrics params +func (o *GetShareMetricsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get share metrics params +func (o *GetShareMetricsParams) WithHTTPClient(client *http.Client) *GetShareMetricsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get share metrics params +func (o *GetShareMetricsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithShrToken adds the shrToken to the get share metrics params +func (o *GetShareMetricsParams) WithShrToken(shrToken string) *GetShareMetricsParams { + o.SetShrToken(shrToken) + return o +} + +// SetShrToken adds the shrToken to the get share metrics params +func (o *GetShareMetricsParams) SetShrToken(shrToken string) { + o.ShrToken = shrToken +} + +// WriteToRequest writes these params to a swagger request +func (o *GetShareMetricsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param shrToken + if err := r.SetPathParam("shrToken", o.ShrToken); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_client_zrok/metrics/get_share_metrics_responses.go b/rest_client_zrok/metrics/get_share_metrics_responses.go new file mode 100644 index 00000000..9f99df33 --- /dev/null +++ b/rest_client_zrok/metrics/get_share_metrics_responses.go @@ -0,0 +1,155 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetShareMetricsReader is a Reader for the GetShareMetrics structure. +type GetShareMetricsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetShareMetricsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetShareMetricsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 401: + result := NewGetShareMetricsUnauthorized() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewGetShareMetricsOK creates a GetShareMetricsOK with default headers values +func NewGetShareMetricsOK() *GetShareMetricsOK { + return &GetShareMetricsOK{} +} + +/* +GetShareMetricsOK describes a response with status code 200, with default header values. + +share metrics +*/ +type GetShareMetricsOK struct { + Payload *rest_model_zrok.Metrics +} + +// IsSuccess returns true when this get share metrics o k response has a 2xx status code +func (o *GetShareMetricsOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get share metrics o k response has a 3xx status code +func (o *GetShareMetricsOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get share metrics o k response has a 4xx status code +func (o *GetShareMetricsOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get share metrics o k response has a 5xx status code +func (o *GetShareMetricsOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get share metrics o k response a status code equal to that given +func (o *GetShareMetricsOK) IsCode(code int) bool { + return code == 200 +} + +func (o *GetShareMetricsOK) Error() string { + return fmt.Sprintf("[GET /metrics/share/{shrToken}][%d] getShareMetricsOK %+v", 200, o.Payload) +} + +func (o *GetShareMetricsOK) String() string { + return fmt.Sprintf("[GET /metrics/share/{shrToken}][%d] getShareMetricsOK %+v", 200, o.Payload) +} + +func (o *GetShareMetricsOK) GetPayload() *rest_model_zrok.Metrics { + return o.Payload +} + +func (o *GetShareMetricsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(rest_model_zrok.Metrics) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetShareMetricsUnauthorized creates a GetShareMetricsUnauthorized with default headers values +func NewGetShareMetricsUnauthorized() *GetShareMetricsUnauthorized { + return &GetShareMetricsUnauthorized{} +} + +/* +GetShareMetricsUnauthorized describes a response with status code 401, with default header values. + +unauthorized +*/ +type GetShareMetricsUnauthorized struct { +} + +// IsSuccess returns true when this get share metrics unauthorized response has a 2xx status code +func (o *GetShareMetricsUnauthorized) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get share metrics unauthorized response has a 3xx status code +func (o *GetShareMetricsUnauthorized) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get share metrics unauthorized response has a 4xx status code +func (o *GetShareMetricsUnauthorized) IsClientError() bool { + return true +} + +// IsServerError returns true when this get share metrics unauthorized response has a 5xx status code +func (o *GetShareMetricsUnauthorized) IsServerError() bool { + return false +} + +// IsCode returns true when this get share metrics unauthorized response a status code equal to that given +func (o *GetShareMetricsUnauthorized) IsCode(code int) bool { + return code == 401 +} + +func (o *GetShareMetricsUnauthorized) Error() string { + return fmt.Sprintf("[GET /metrics/share/{shrToken}][%d] getShareMetricsUnauthorized ", 401) +} + +func (o *GetShareMetricsUnauthorized) String() string { + return fmt.Sprintf("[GET /metrics/share/{shrToken}][%d] getShareMetricsUnauthorized ", 401) +} + +func (o *GetShareMetricsUnauthorized) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} diff --git a/rest_client_zrok/metrics/metrics_client.go b/rest_client_zrok/metrics/metrics_client.go new file mode 100644 index 00000000..b1f4ab1f --- /dev/null +++ b/rest_client_zrok/metrics/metrics_client.go @@ -0,0 +1,162 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" +) + +// New creates a new metrics API client. +func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientService { + return &Client{transport: transport, formats: formats} +} + +/* +Client for metrics API +*/ +type Client struct { + transport runtime.ClientTransport + formats strfmt.Registry +} + +// ClientOption is the option for Client methods +type ClientOption func(*runtime.ClientOperation) + +// ClientService is the interface for Client methods +type ClientService interface { + GetAccountMetrics(params *GetAccountMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetAccountMetricsOK, error) + + GetEnvironmentMetrics(params *GetEnvironmentMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetEnvironmentMetricsOK, error) + + GetShareMetrics(params *GetShareMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetShareMetricsOK, error) + + SetTransport(transport runtime.ClientTransport) +} + +/* +GetAccountMetrics get account metrics API +*/ +func (a *Client) GetAccountMetrics(params *GetAccountMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetAccountMetricsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetAccountMetricsParams() + } + op := &runtime.ClientOperation{ + ID: "getAccountMetrics", + Method: "GET", + PathPattern: "/metrics/account", + ProducesMediaTypes: []string{"application/zrok.v1+json"}, + ConsumesMediaTypes: []string{"application/zrok.v1+json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetAccountMetricsReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetAccountMetricsOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for getAccountMetrics: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + +/* +GetEnvironmentMetrics get environment metrics API +*/ +func (a *Client) GetEnvironmentMetrics(params *GetEnvironmentMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetEnvironmentMetricsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetEnvironmentMetricsParams() + } + op := &runtime.ClientOperation{ + ID: "getEnvironmentMetrics", + Method: "GET", + PathPattern: "/metrics/environment/{envId}", + ProducesMediaTypes: []string{"application/zrok.v1+json"}, + ConsumesMediaTypes: []string{"application/zrok.v1+json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetEnvironmentMetricsReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetEnvironmentMetricsOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for getEnvironmentMetrics: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + +/* +GetShareMetrics get share metrics API +*/ +func (a *Client) GetShareMetrics(params *GetShareMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetShareMetricsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetShareMetricsParams() + } + op := &runtime.ClientOperation{ + ID: "getShareMetrics", + Method: "GET", + PathPattern: "/metrics/share/{shrToken}", + ProducesMediaTypes: []string{"application/zrok.v1+json"}, + ConsumesMediaTypes: []string{"application/zrok.v1+json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetShareMetricsReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetShareMetricsOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for getShareMetrics: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + +// SetTransport changes the transport on the client +func (a *Client) SetTransport(transport runtime.ClientTransport) { + a.transport = transport +} diff --git a/rest_client_zrok/zrok_client.go b/rest_client_zrok/zrok_client.go index a4200e67..c5f02b8c 100644 --- a/rest_client_zrok/zrok_client.go +++ b/rest_client_zrok/zrok_client.go @@ -14,6 +14,7 @@ import ( "github.com/openziti/zrok/rest_client_zrok/admin" "github.com/openziti/zrok/rest_client_zrok/environment" "github.com/openziti/zrok/rest_client_zrok/metadata" + "github.com/openziti/zrok/rest_client_zrok/metrics" "github.com/openziti/zrok/rest_client_zrok/share" ) @@ -63,6 +64,7 @@ func New(transport runtime.ClientTransport, formats strfmt.Registry) *Zrok { cli.Admin = admin.New(transport, formats) cli.Environment = environment.New(transport, formats) cli.Metadata = metadata.New(transport, formats) + cli.Metrics = metrics.New(transport, formats) cli.Share = share.New(transport, formats) return cli } @@ -116,6 +118,8 @@ type Zrok struct { Metadata metadata.ClientService + Metrics metrics.ClientService + Share share.ClientService Transport runtime.ClientTransport @@ -128,5 +132,6 @@ func (c *Zrok) SetTransport(transport runtime.ClientTransport) { c.Admin.SetTransport(transport) c.Environment.SetTransport(transport) c.Metadata.SetTransport(transport) + c.Metrics.SetTransport(transport) c.Share.SetTransport(transport) } diff --git a/rest_model_zrok/metrics.go b/rest_model_zrok/metrics.go new file mode 100644 index 00000000..dfb9bf08 --- /dev/null +++ b/rest_model_zrok/metrics.go @@ -0,0 +1,62 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package rest_model_zrok + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// Metrics metrics +// +// swagger:model metrics +type Metrics struct { + + // id + ID string `json:"id,omitempty"` + + // period + Period float64 `json:"period,omitempty"` + + // rx + Rx []float64 `json:"rx"` + + // scope + Scope string `json:"scope,omitempty"` + + // tx + Tx []float64 `json:"tx"` +} + +// Validate validates this metrics +func (m *Metrics) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this metrics based on context it is used +func (m *Metrics) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *Metrics) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Metrics) UnmarshalBinary(b []byte) error { + var res Metrics + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 5560d26d..59341826 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -520,6 +520,91 @@ func init() { } } }, + "/metrics/account": { + "get": { + "security": [ + { + "key": [] + } + ], + "tags": [ + "metrics" + ], + "operationId": "getAccountMetrics", + "responses": { + "200": { + "description": "account metrics", + "schema": { + "$ref": "#/definitions/metrics" + } + } + } + } + }, + "/metrics/environment/{envId}": { + "get": { + "security": [ + { + "key": [] + } + ], + "tags": [ + "metrics" + ], + "operationId": "getEnvironmentMetrics", + "parameters": [ + { + "type": "string", + "name": "envId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "environment metrics", + "schema": { + "$ref": "#/definitions/metrics" + } + }, + "401": { + "description": "unauthorized" + } + } + } + }, + "/metrics/share/{shrToken}": { + "get": { + "security": [ + { + "key": [] + } + ], + "tags": [ + "metrics" + ], + "operationId": "getShareMetrics", + "parameters": [ + { + "type": "string", + "name": "shrToken", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "share metrics", + "schema": { + "$ref": "#/definitions/metrics" + } + }, + "401": { + "description": "unauthorized" + } + } + } + }, "/overview": { "get": { "security": [ @@ -1030,6 +1115,32 @@ func init() { "loginResponse": { "type": "string" }, + "metrics": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "period": { + "type": "number" + }, + "rx": { + "type": "array", + "items": { + "type": "number" + } + }, + "scope": { + "type": "string" + }, + "tx": { + "type": "array", + "items": { + "type": "number" + } + } + } + }, "principal": { "type": "object", "properties": { @@ -1802,6 +1913,91 @@ func init() { } } }, + "/metrics/account": { + "get": { + "security": [ + { + "key": [] + } + ], + "tags": [ + "metrics" + ], + "operationId": "getAccountMetrics", + "responses": { + "200": { + "description": "account metrics", + "schema": { + "$ref": "#/definitions/metrics" + } + } + } + } + }, + "/metrics/environment/{envId}": { + "get": { + "security": [ + { + "key": [] + } + ], + "tags": [ + "metrics" + ], + "operationId": "getEnvironmentMetrics", + "parameters": [ + { + "type": "string", + "name": "envId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "environment metrics", + "schema": { + "$ref": "#/definitions/metrics" + } + }, + "401": { + "description": "unauthorized" + } + } + } + }, + "/metrics/share/{shrToken}": { + "get": { + "security": [ + { + "key": [] + } + ], + "tags": [ + "metrics" + ], + "operationId": "getShareMetrics", + "parameters": [ + { + "type": "string", + "name": "shrToken", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "share metrics", + "schema": { + "$ref": "#/definitions/metrics" + } + }, + "401": { + "description": "unauthorized" + } + } + } + }, "/overview": { "get": { "security": [ @@ -2312,6 +2508,32 @@ func init() { "loginResponse": { "type": "string" }, + "metrics": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "period": { + "type": "number" + }, + "rx": { + "type": "array", + "items": { + "type": "number" + } + }, + "scope": { + "type": "string" + }, + "tx": { + "type": "array", + "items": { + "type": "number" + } + } + } + }, "principal": { "type": "object", "properties": { diff --git a/rest_server_zrok/operations/metrics/get_account_metrics.go b/rest_server_zrok/operations/metrics/get_account_metrics.go new file mode 100644 index 00000000..d3fcab7f --- /dev/null +++ b/rest_server_zrok/operations/metrics/get_account_metrics.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetAccountMetricsHandlerFunc turns a function with the right signature into a get account metrics handler +type GetAccountMetricsHandlerFunc func(GetAccountMetricsParams, *rest_model_zrok.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetAccountMetricsHandlerFunc) Handle(params GetAccountMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + return fn(params, principal) +} + +// GetAccountMetricsHandler interface for that can handle valid get account metrics params +type GetAccountMetricsHandler interface { + Handle(GetAccountMetricsParams, *rest_model_zrok.Principal) middleware.Responder +} + +// NewGetAccountMetrics creates a new http.Handler for the get account metrics operation +func NewGetAccountMetrics(ctx *middleware.Context, handler GetAccountMetricsHandler) *GetAccountMetrics { + return &GetAccountMetrics{Context: ctx, Handler: handler} +} + +/* + GetAccountMetrics swagger:route GET /metrics/account metrics getAccountMetrics + +GetAccountMetrics get account metrics API +*/ +type GetAccountMetrics struct { + Context *middleware.Context + Handler GetAccountMetricsHandler +} + +func (o *GetAccountMetrics) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetAccountMetricsParams() + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + *r = *aCtx + } + var principal *rest_model_zrok.Principal + if uprinc != nil { + principal = uprinc.(*rest_model_zrok.Principal) // this is really a rest_model_zrok.Principal, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/rest_server_zrok/operations/metrics/get_account_metrics_parameters.go b/rest_server_zrok/operations/metrics/get_account_metrics_parameters.go new file mode 100644 index 00000000..98bb9b8c --- /dev/null +++ b/rest_server_zrok/operations/metrics/get_account_metrics_parameters.go @@ -0,0 +1,46 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" +) + +// NewGetAccountMetricsParams creates a new GetAccountMetricsParams object +// +// There are no default values defined in the spec. +func NewGetAccountMetricsParams() GetAccountMetricsParams { + + return GetAccountMetricsParams{} +} + +// GetAccountMetricsParams contains all the bound params for the get account metrics operation +// typically these are obtained from a http.Request +// +// swagger:parameters getAccountMetrics +type GetAccountMetricsParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetAccountMetricsParams() beforehand. +func (o *GetAccountMetricsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_server_zrok/operations/metrics/get_account_metrics_responses.go b/rest_server_zrok/operations/metrics/get_account_metrics_responses.go new file mode 100644 index 00000000..78480b51 --- /dev/null +++ b/rest_server_zrok/operations/metrics/get_account_metrics_responses.go @@ -0,0 +1,59 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetAccountMetricsOKCode is the HTTP code returned for type GetAccountMetricsOK +const GetAccountMetricsOKCode int = 200 + +/* +GetAccountMetricsOK account metrics + +swagger:response getAccountMetricsOK +*/ +type GetAccountMetricsOK struct { + + /* + In: Body + */ + Payload *rest_model_zrok.Metrics `json:"body,omitempty"` +} + +// NewGetAccountMetricsOK creates GetAccountMetricsOK with default headers values +func NewGetAccountMetricsOK() *GetAccountMetricsOK { + + return &GetAccountMetricsOK{} +} + +// WithPayload adds the payload to the get account metrics o k response +func (o *GetAccountMetricsOK) WithPayload(payload *rest_model_zrok.Metrics) *GetAccountMetricsOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get account metrics o k response +func (o *GetAccountMetricsOK) SetPayload(payload *rest_model_zrok.Metrics) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetAccountMetricsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/rest_server_zrok/operations/metrics/get_account_metrics_urlbuilder.go b/rest_server_zrok/operations/metrics/get_account_metrics_urlbuilder.go new file mode 100644 index 00000000..16b79838 --- /dev/null +++ b/rest_server_zrok/operations/metrics/get_account_metrics_urlbuilder.go @@ -0,0 +1,87 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// GetAccountMetricsURL generates an URL for the get account metrics operation +type GetAccountMetricsURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetAccountMetricsURL) WithBasePath(bp string) *GetAccountMetricsURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetAccountMetricsURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetAccountMetricsURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/metrics/account" + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetAccountMetricsURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetAccountMetricsURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetAccountMetricsURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetAccountMetricsURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetAccountMetricsURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetAccountMetricsURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/rest_server_zrok/operations/metrics/get_environment_metrics.go b/rest_server_zrok/operations/metrics/get_environment_metrics.go new file mode 100644 index 00000000..b26b7535 --- /dev/null +++ b/rest_server_zrok/operations/metrics/get_environment_metrics.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetEnvironmentMetricsHandlerFunc turns a function with the right signature into a get environment metrics handler +type GetEnvironmentMetricsHandlerFunc func(GetEnvironmentMetricsParams, *rest_model_zrok.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetEnvironmentMetricsHandlerFunc) Handle(params GetEnvironmentMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + return fn(params, principal) +} + +// GetEnvironmentMetricsHandler interface for that can handle valid get environment metrics params +type GetEnvironmentMetricsHandler interface { + Handle(GetEnvironmentMetricsParams, *rest_model_zrok.Principal) middleware.Responder +} + +// NewGetEnvironmentMetrics creates a new http.Handler for the get environment metrics operation +func NewGetEnvironmentMetrics(ctx *middleware.Context, handler GetEnvironmentMetricsHandler) *GetEnvironmentMetrics { + return &GetEnvironmentMetrics{Context: ctx, Handler: handler} +} + +/* + GetEnvironmentMetrics swagger:route GET /metrics/environment/{envId} metrics getEnvironmentMetrics + +GetEnvironmentMetrics get environment metrics API +*/ +type GetEnvironmentMetrics struct { + Context *middleware.Context + Handler GetEnvironmentMetricsHandler +} + +func (o *GetEnvironmentMetrics) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetEnvironmentMetricsParams() + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + *r = *aCtx + } + var principal *rest_model_zrok.Principal + if uprinc != nil { + principal = uprinc.(*rest_model_zrok.Principal) // this is really a rest_model_zrok.Principal, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/rest_server_zrok/operations/metrics/get_environment_metrics_parameters.go b/rest_server_zrok/operations/metrics/get_environment_metrics_parameters.go new file mode 100644 index 00000000..1f10409f --- /dev/null +++ b/rest_server_zrok/operations/metrics/get_environment_metrics_parameters.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" +) + +// NewGetEnvironmentMetricsParams creates a new GetEnvironmentMetricsParams object +// +// There are no default values defined in the spec. +func NewGetEnvironmentMetricsParams() GetEnvironmentMetricsParams { + + return GetEnvironmentMetricsParams{} +} + +// GetEnvironmentMetricsParams contains all the bound params for the get environment metrics operation +// typically these are obtained from a http.Request +// +// swagger:parameters getEnvironmentMetrics +type GetEnvironmentMetricsParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: path + */ + EnvID string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetEnvironmentMetricsParams() beforehand. +func (o *GetEnvironmentMetricsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + rEnvID, rhkEnvID, _ := route.Params.GetOK("envId") + if err := o.bindEnvID(rEnvID, rhkEnvID, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindEnvID binds and validates parameter EnvID from path. +func (o *GetEnvironmentMetricsParams) bindEnvID(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.EnvID = raw + + return nil +} diff --git a/rest_server_zrok/operations/metrics/get_environment_metrics_responses.go b/rest_server_zrok/operations/metrics/get_environment_metrics_responses.go new file mode 100644 index 00000000..d5e9897c --- /dev/null +++ b/rest_server_zrok/operations/metrics/get_environment_metrics_responses.go @@ -0,0 +1,84 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetEnvironmentMetricsOKCode is the HTTP code returned for type GetEnvironmentMetricsOK +const GetEnvironmentMetricsOKCode int = 200 + +/* +GetEnvironmentMetricsOK environment metrics + +swagger:response getEnvironmentMetricsOK +*/ +type GetEnvironmentMetricsOK struct { + + /* + In: Body + */ + Payload *rest_model_zrok.Metrics `json:"body,omitempty"` +} + +// NewGetEnvironmentMetricsOK creates GetEnvironmentMetricsOK with default headers values +func NewGetEnvironmentMetricsOK() *GetEnvironmentMetricsOK { + + return &GetEnvironmentMetricsOK{} +} + +// WithPayload adds the payload to the get environment metrics o k response +func (o *GetEnvironmentMetricsOK) WithPayload(payload *rest_model_zrok.Metrics) *GetEnvironmentMetricsOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get environment metrics o k response +func (o *GetEnvironmentMetricsOK) SetPayload(payload *rest_model_zrok.Metrics) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetEnvironmentMetricsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetEnvironmentMetricsUnauthorizedCode is the HTTP code returned for type GetEnvironmentMetricsUnauthorized +const GetEnvironmentMetricsUnauthorizedCode int = 401 + +/* +GetEnvironmentMetricsUnauthorized unauthorized + +swagger:response getEnvironmentMetricsUnauthorized +*/ +type GetEnvironmentMetricsUnauthorized struct { +} + +// NewGetEnvironmentMetricsUnauthorized creates GetEnvironmentMetricsUnauthorized with default headers values +func NewGetEnvironmentMetricsUnauthorized() *GetEnvironmentMetricsUnauthorized { + + return &GetEnvironmentMetricsUnauthorized{} +} + +// WriteResponse to the client +func (o *GetEnvironmentMetricsUnauthorized) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(401) +} diff --git a/rest_server_zrok/operations/metrics/get_environment_metrics_urlbuilder.go b/rest_server_zrok/operations/metrics/get_environment_metrics_urlbuilder.go new file mode 100644 index 00000000..a31dfd88 --- /dev/null +++ b/rest_server_zrok/operations/metrics/get_environment_metrics_urlbuilder.go @@ -0,0 +1,99 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// GetEnvironmentMetricsURL generates an URL for the get environment metrics operation +type GetEnvironmentMetricsURL struct { + EnvID string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetEnvironmentMetricsURL) WithBasePath(bp string) *GetEnvironmentMetricsURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetEnvironmentMetricsURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetEnvironmentMetricsURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/metrics/environment/{envId}" + + envID := o.EnvID + if envID != "" { + _path = strings.Replace(_path, "{envId}", envID, -1) + } else { + return nil, errors.New("envId is required on GetEnvironmentMetricsURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetEnvironmentMetricsURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetEnvironmentMetricsURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetEnvironmentMetricsURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetEnvironmentMetricsURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetEnvironmentMetricsURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetEnvironmentMetricsURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/rest_server_zrok/operations/metrics/get_share_metrics.go b/rest_server_zrok/operations/metrics/get_share_metrics.go new file mode 100644 index 00000000..67470dfa --- /dev/null +++ b/rest_server_zrok/operations/metrics/get_share_metrics.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetShareMetricsHandlerFunc turns a function with the right signature into a get share metrics handler +type GetShareMetricsHandlerFunc func(GetShareMetricsParams, *rest_model_zrok.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetShareMetricsHandlerFunc) Handle(params GetShareMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + return fn(params, principal) +} + +// GetShareMetricsHandler interface for that can handle valid get share metrics params +type GetShareMetricsHandler interface { + Handle(GetShareMetricsParams, *rest_model_zrok.Principal) middleware.Responder +} + +// NewGetShareMetrics creates a new http.Handler for the get share metrics operation +func NewGetShareMetrics(ctx *middleware.Context, handler GetShareMetricsHandler) *GetShareMetrics { + return &GetShareMetrics{Context: ctx, Handler: handler} +} + +/* + GetShareMetrics swagger:route GET /metrics/share/{shrToken} metrics getShareMetrics + +GetShareMetrics get share metrics API +*/ +type GetShareMetrics struct { + Context *middleware.Context + Handler GetShareMetricsHandler +} + +func (o *GetShareMetrics) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetShareMetricsParams() + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + *r = *aCtx + } + var principal *rest_model_zrok.Principal + if uprinc != nil { + principal = uprinc.(*rest_model_zrok.Principal) // this is really a rest_model_zrok.Principal, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/rest_server_zrok/operations/metrics/get_share_metrics_parameters.go b/rest_server_zrok/operations/metrics/get_share_metrics_parameters.go new file mode 100644 index 00000000..1013bb7a --- /dev/null +++ b/rest_server_zrok/operations/metrics/get_share_metrics_parameters.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" +) + +// NewGetShareMetricsParams creates a new GetShareMetricsParams object +// +// There are no default values defined in the spec. +func NewGetShareMetricsParams() GetShareMetricsParams { + + return GetShareMetricsParams{} +} + +// GetShareMetricsParams contains all the bound params for the get share metrics operation +// typically these are obtained from a http.Request +// +// swagger:parameters getShareMetrics +type GetShareMetricsParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: path + */ + ShrToken string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetShareMetricsParams() beforehand. +func (o *GetShareMetricsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + rShrToken, rhkShrToken, _ := route.Params.GetOK("shrToken") + if err := o.bindShrToken(rShrToken, rhkShrToken, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindShrToken binds and validates parameter ShrToken from path. +func (o *GetShareMetricsParams) bindShrToken(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.ShrToken = raw + + return nil +} diff --git a/rest_server_zrok/operations/metrics/get_share_metrics_responses.go b/rest_server_zrok/operations/metrics/get_share_metrics_responses.go new file mode 100644 index 00000000..70e0c0e1 --- /dev/null +++ b/rest_server_zrok/operations/metrics/get_share_metrics_responses.go @@ -0,0 +1,84 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetShareMetricsOKCode is the HTTP code returned for type GetShareMetricsOK +const GetShareMetricsOKCode int = 200 + +/* +GetShareMetricsOK share metrics + +swagger:response getShareMetricsOK +*/ +type GetShareMetricsOK struct { + + /* + In: Body + */ + Payload *rest_model_zrok.Metrics `json:"body,omitempty"` +} + +// NewGetShareMetricsOK creates GetShareMetricsOK with default headers values +func NewGetShareMetricsOK() *GetShareMetricsOK { + + return &GetShareMetricsOK{} +} + +// WithPayload adds the payload to the get share metrics o k response +func (o *GetShareMetricsOK) WithPayload(payload *rest_model_zrok.Metrics) *GetShareMetricsOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get share metrics o k response +func (o *GetShareMetricsOK) SetPayload(payload *rest_model_zrok.Metrics) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetShareMetricsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetShareMetricsUnauthorizedCode is the HTTP code returned for type GetShareMetricsUnauthorized +const GetShareMetricsUnauthorizedCode int = 401 + +/* +GetShareMetricsUnauthorized unauthorized + +swagger:response getShareMetricsUnauthorized +*/ +type GetShareMetricsUnauthorized struct { +} + +// NewGetShareMetricsUnauthorized creates GetShareMetricsUnauthorized with default headers values +func NewGetShareMetricsUnauthorized() *GetShareMetricsUnauthorized { + + return &GetShareMetricsUnauthorized{} +} + +// WriteResponse to the client +func (o *GetShareMetricsUnauthorized) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(401) +} diff --git a/rest_server_zrok/operations/metrics/get_share_metrics_urlbuilder.go b/rest_server_zrok/operations/metrics/get_share_metrics_urlbuilder.go new file mode 100644 index 00000000..deeb05e3 --- /dev/null +++ b/rest_server_zrok/operations/metrics/get_share_metrics_urlbuilder.go @@ -0,0 +1,99 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metrics + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// GetShareMetricsURL generates an URL for the get share metrics operation +type GetShareMetricsURL struct { + ShrToken string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetShareMetricsURL) WithBasePath(bp string) *GetShareMetricsURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetShareMetricsURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetShareMetricsURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/metrics/share/{shrToken}" + + shrToken := o.ShrToken + if shrToken != "" { + _path = strings.Replace(_path, "{shrToken}", shrToken, -1) + } else { + return nil, errors.New("shrToken is required on GetShareMetricsURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetShareMetricsURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetShareMetricsURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetShareMetricsURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetShareMetricsURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetShareMetricsURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetShareMetricsURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/rest_server_zrok/operations/zrok_api.go b/rest_server_zrok/operations/zrok_api.go index 3424c976..19a12af8 100644 --- a/rest_server_zrok/operations/zrok_api.go +++ b/rest_server_zrok/operations/zrok_api.go @@ -24,6 +24,7 @@ import ( "github.com/openziti/zrok/rest_server_zrok/operations/admin" "github.com/openziti/zrok/rest_server_zrok/operations/environment" "github.com/openziti/zrok/rest_server_zrok/operations/metadata" + "github.com/openziti/zrok/rest_server_zrok/operations/metrics" "github.com/openziti/zrok/rest_server_zrok/operations/share" ) @@ -70,12 +71,21 @@ func NewZrokAPI(spec *loads.Document) *ZrokAPI { EnvironmentEnableHandler: environment.EnableHandlerFunc(func(params environment.EnableParams, principal *rest_model_zrok.Principal) middleware.Responder { return middleware.NotImplemented("operation environment.Enable has not yet been implemented") }), + MetricsGetAccountMetricsHandler: metrics.GetAccountMetricsHandlerFunc(func(params metrics.GetAccountMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + return middleware.NotImplemented("operation metrics.GetAccountMetrics has not yet been implemented") + }), MetadataGetEnvironmentDetailHandler: metadata.GetEnvironmentDetailHandlerFunc(func(params metadata.GetEnvironmentDetailParams, principal *rest_model_zrok.Principal) middleware.Responder { return middleware.NotImplemented("operation metadata.GetEnvironmentDetail has not yet been implemented") }), + MetricsGetEnvironmentMetricsHandler: metrics.GetEnvironmentMetricsHandlerFunc(func(params metrics.GetEnvironmentMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + return middleware.NotImplemented("operation metrics.GetEnvironmentMetrics has not yet been implemented") + }), MetadataGetShareDetailHandler: metadata.GetShareDetailHandlerFunc(func(params metadata.GetShareDetailParams, principal *rest_model_zrok.Principal) middleware.Responder { return middleware.NotImplemented("operation metadata.GetShareDetail has not yet been implemented") }), + MetricsGetShareMetricsHandler: metrics.GetShareMetricsHandlerFunc(func(params metrics.GetShareMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + return middleware.NotImplemented("operation metrics.GetShareMetrics has not yet been implemented") + }), AccountInviteHandler: account.InviteHandlerFunc(func(params account.InviteParams) middleware.Responder { return middleware.NotImplemented("operation account.Invite has not yet been implemented") }), @@ -185,10 +195,16 @@ type ZrokAPI struct { EnvironmentDisableHandler environment.DisableHandler // EnvironmentEnableHandler sets the operation handler for the enable operation EnvironmentEnableHandler environment.EnableHandler + // MetricsGetAccountMetricsHandler sets the operation handler for the get account metrics operation + MetricsGetAccountMetricsHandler metrics.GetAccountMetricsHandler // MetadataGetEnvironmentDetailHandler sets the operation handler for the get environment detail operation MetadataGetEnvironmentDetailHandler metadata.GetEnvironmentDetailHandler + // MetricsGetEnvironmentMetricsHandler sets the operation handler for the get environment metrics operation + MetricsGetEnvironmentMetricsHandler metrics.GetEnvironmentMetricsHandler // MetadataGetShareDetailHandler sets the operation handler for the get share detail operation MetadataGetShareDetailHandler metadata.GetShareDetailHandler + // MetricsGetShareMetricsHandler sets the operation handler for the get share metrics operation + MetricsGetShareMetricsHandler metrics.GetShareMetricsHandler // AccountInviteHandler sets the operation handler for the invite operation AccountInviteHandler account.InviteHandler // AdminInviteTokenGenerateHandler sets the operation handler for the invite token generate operation @@ -321,12 +337,21 @@ func (o *ZrokAPI) Validate() error { if o.EnvironmentEnableHandler == nil { unregistered = append(unregistered, "environment.EnableHandler") } + if o.MetricsGetAccountMetricsHandler == nil { + unregistered = append(unregistered, "metrics.GetAccountMetricsHandler") + } if o.MetadataGetEnvironmentDetailHandler == nil { unregistered = append(unregistered, "metadata.GetEnvironmentDetailHandler") } + if o.MetricsGetEnvironmentMetricsHandler == nil { + unregistered = append(unregistered, "metrics.GetEnvironmentMetricsHandler") + } if o.MetadataGetShareDetailHandler == nil { unregistered = append(unregistered, "metadata.GetShareDetailHandler") } + if o.MetricsGetShareMetricsHandler == nil { + unregistered = append(unregistered, "metrics.GetShareMetricsHandler") + } if o.AccountInviteHandler == nil { unregistered = append(unregistered, "account.InviteHandler") } @@ -502,11 +527,23 @@ func (o *ZrokAPI) initHandlerCache() { if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } + o.handlers["GET"]["/metrics/account"] = metrics.NewGetAccountMetrics(o.context, o.MetricsGetAccountMetricsHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } o.handlers["GET"]["/detail/environment/{envZId}"] = metadata.NewGetEnvironmentDetail(o.context, o.MetadataGetEnvironmentDetailHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } + o.handlers["GET"]["/metrics/environment/{envId}"] = metrics.NewGetEnvironmentMetrics(o.context, o.MetricsGetEnvironmentMetricsHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } o.handlers["GET"]["/detail/share/{shrToken}"] = metadata.NewGetShareDetail(o.context, o.MetadataGetShareDetailHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } + o.handlers["GET"]["/metrics/share/{shrToken}"] = metrics.NewGetShareMetrics(o.context, o.MetricsGetShareMetricsHandler) if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) } diff --git a/specs/zrok.yml b/specs/zrok.yml index 9064aa36..ffe081b8 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -404,6 +404,62 @@ paths: description: current server version schema: $ref: "#/definitions/version" + + # + # metrics + # + /metrics/account: + get: + tags: + - metrics + security: + - key: [] + operationId: getAccountMetrics + responses: + 200: + description: account metrics + schema: + $ref: "#/definitions/metrics" + + /metrics/environment/{envId}: + get: + tags: + - metrics + security: + - key: [] + operationId: getEnvironmentMetrics + parameters: + - name: envId + in: path + type: string + required: true + responses: + 200: + description: environment metrics + schema: + $ref: "#/definitions/metrics" + 401: + description: unauthorized + + /metrics/share/{shrToken}: + get: + tags: + - metrics + security: + - key: [] + operationId: getShareMetrics + parameters: + - name: shrToken + in: path + type: string + required: true + responses: + 200: + description: share metrics + schema: + $ref: "#/definitions/metrics" + 401: + description: unauthorized # # share # @@ -666,6 +722,24 @@ definitions: loginResponse: type: string + metrics: + type: object + properties: + scope: + type: string + id: + type: string + period: + type: number + rx: + type: array + items: + type: number + tx: + type: array + items: + type: number + principal: type: object properties: diff --git a/ui/src/api/metrics.js b/ui/src/api/metrics.js new file mode 100644 index 00000000..5c1111dd --- /dev/null +++ b/ui/src/api/metrics.js @@ -0,0 +1,65 @@ +/** @module metrics */ +// Auto-generated, edits will be overwritten +import * as gateway from './gateway' + +/** + */ +export function getAccountMetrics() { + return gateway.request(getAccountMetricsOperation) +} + +/** + * @param {string} envId + * @return {Promise<module:types.metrics>} environment metrics + */ +export function getEnvironmentMetrics(envId) { + const parameters = { + path: { + envId + } + } + return gateway.request(getEnvironmentMetricsOperation, parameters) +} + +/** + * @param {string} shrToken + * @return {Promise<module:types.metrics>} share metrics + */ +export function getShareMetrics(shrToken) { + const parameters = { + path: { + shrToken + } + } + return gateway.request(getShareMetricsOperation, parameters) +} + +const getAccountMetricsOperation = { + path: '/metrics/account', + method: 'get', + security: [ + { + id: 'key' + } + ] +} + +const getEnvironmentMetricsOperation = { + path: '/metrics/environment/{envId}', + method: 'get', + security: [ + { + id: 'key' + } + ] +} + +const getShareMetricsOperation = { + path: '/metrics/share/{shrToken}', + method: 'get', + security: [ + { + id: 'key' + } + ] +} diff --git a/ui/src/api/types.js b/ui/src/api/types.js index 6196d926..6f5a6317 100644 --- a/ui/src/api/types.js +++ b/ui/src/api/types.js @@ -123,6 +123,17 @@ * @property {string} password */ +/** + * @typedef metrics + * @memberof module:types + * + * @property {string} scope + * @property {string} id + * @property {number} period + * @property {number[]} rx + * @property {number[]} tx + */ + /** * @typedef principal * @memberof module:types From 21fdaf8e3ca4a1e9dbc0c2f20a8a7f609e5c614d Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Tue, 9 May 2023 11:36:53 -0400 Subject: [PATCH 08/49] duration parameter (#319) --- .../metrics/get_account_metrics_parameters.go | 33 +++++++++++++++++ .../get_environment_metrics_parameters.go | 32 ++++++++++++++++ .../metrics/get_share_metrics_parameters.go | 32 ++++++++++++++++ rest_server_zrok/embedded_spec.go | 34 +++++++++++++++++ .../metrics/get_account_metrics_parameters.go | 37 +++++++++++++++++++ .../metrics/get_account_metrics_urlbuilder.go | 18 +++++++++ .../get_environment_metrics_parameters.go | 36 ++++++++++++++++++ .../get_environment_metrics_urlbuilder.go | 16 ++++++++ .../metrics/get_share_metrics_parameters.go | 36 ++++++++++++++++++ .../metrics/get_share_metrics_urlbuilder.go | 16 ++++++++ specs/zrok.yml | 10 +++++ ui/src/api/metrics.js | 29 +++++++++++++-- .../console/detail/environment/SharesTab.js | 2 +- ui/src/console/detail/share/ShareDetail.js | 2 +- ui/src/console/visualizer/Network.js | 4 +- ui/src/console/visualizer/graph.js | 4 +- 16 files changed, 331 insertions(+), 10 deletions(-) diff --git a/rest_client_zrok/metrics/get_account_metrics_parameters.go b/rest_client_zrok/metrics/get_account_metrics_parameters.go index 5af21fb7..b06d0b6d 100644 --- a/rest_client_zrok/metrics/get_account_metrics_parameters.go +++ b/rest_client_zrok/metrics/get_account_metrics_parameters.go @@ -14,6 +14,7 @@ import ( "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) // NewGetAccountMetricsParams creates a new GetAccountMetricsParams object, @@ -60,6 +61,10 @@ GetAccountMetricsParams contains all the parameters to send to the API endpoint Typically these are written to a http.Request. */ type GetAccountMetricsParams struct { + + // Duration. + Duration *float64 + timeout time.Duration Context context.Context HTTPClient *http.Client @@ -113,6 +118,17 @@ func (o *GetAccountMetricsParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } +// WithDuration adds the duration to the get account metrics params +func (o *GetAccountMetricsParams) WithDuration(duration *float64) *GetAccountMetricsParams { + o.SetDuration(duration) + return o +} + +// SetDuration adds the duration to the get account metrics params +func (o *GetAccountMetricsParams) SetDuration(duration *float64) { + o.Duration = duration +} + // WriteToRequest writes these params to a swagger request func (o *GetAccountMetricsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { @@ -121,6 +137,23 @@ func (o *GetAccountMetricsParams) WriteToRequest(r runtime.ClientRequest, reg st } var res []error + if o.Duration != nil { + + // query param duration + var qrDuration float64 + + if o.Duration != nil { + qrDuration = *o.Duration + } + qDuration := swag.FormatFloat64(qrDuration) + if qDuration != "" { + + if err := r.SetQueryParam("duration", qDuration); err != nil { + return err + } + } + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } diff --git a/rest_client_zrok/metrics/get_environment_metrics_parameters.go b/rest_client_zrok/metrics/get_environment_metrics_parameters.go index 538d0074..796adaae 100644 --- a/rest_client_zrok/metrics/get_environment_metrics_parameters.go +++ b/rest_client_zrok/metrics/get_environment_metrics_parameters.go @@ -14,6 +14,7 @@ import ( "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) // NewGetEnvironmentMetricsParams creates a new GetEnvironmentMetricsParams object, @@ -61,6 +62,9 @@ GetEnvironmentMetricsParams contains all the parameters to send to the API endpo */ type GetEnvironmentMetricsParams struct { + // Duration. + Duration *float64 + // EnvID. EnvID string @@ -117,6 +121,17 @@ func (o *GetEnvironmentMetricsParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } +// WithDuration adds the duration to the get environment metrics params +func (o *GetEnvironmentMetricsParams) WithDuration(duration *float64) *GetEnvironmentMetricsParams { + o.SetDuration(duration) + return o +} + +// SetDuration adds the duration to the get environment metrics params +func (o *GetEnvironmentMetricsParams) SetDuration(duration *float64) { + o.Duration = duration +} + // WithEnvID adds the envID to the get environment metrics params func (o *GetEnvironmentMetricsParams) WithEnvID(envID string) *GetEnvironmentMetricsParams { o.SetEnvID(envID) @@ -136,6 +151,23 @@ func (o *GetEnvironmentMetricsParams) WriteToRequest(r runtime.ClientRequest, re } var res []error + if o.Duration != nil { + + // query param duration + var qrDuration float64 + + if o.Duration != nil { + qrDuration = *o.Duration + } + qDuration := swag.FormatFloat64(qrDuration) + if qDuration != "" { + + if err := r.SetQueryParam("duration", qDuration); err != nil { + return err + } + } + } + // path param envId if err := r.SetPathParam("envId", o.EnvID); err != nil { return err diff --git a/rest_client_zrok/metrics/get_share_metrics_parameters.go b/rest_client_zrok/metrics/get_share_metrics_parameters.go index 690b280f..359c3c47 100644 --- a/rest_client_zrok/metrics/get_share_metrics_parameters.go +++ b/rest_client_zrok/metrics/get_share_metrics_parameters.go @@ -14,6 +14,7 @@ import ( "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) // NewGetShareMetricsParams creates a new GetShareMetricsParams object, @@ -61,6 +62,9 @@ GetShareMetricsParams contains all the parameters to send to the API endpoint */ type GetShareMetricsParams struct { + // Duration. + Duration *float64 + // ShrToken. ShrToken string @@ -117,6 +121,17 @@ func (o *GetShareMetricsParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } +// WithDuration adds the duration to the get share metrics params +func (o *GetShareMetricsParams) WithDuration(duration *float64) *GetShareMetricsParams { + o.SetDuration(duration) + return o +} + +// SetDuration adds the duration to the get share metrics params +func (o *GetShareMetricsParams) SetDuration(duration *float64) { + o.Duration = duration +} + // WithShrToken adds the shrToken to the get share metrics params func (o *GetShareMetricsParams) WithShrToken(shrToken string) *GetShareMetricsParams { o.SetShrToken(shrToken) @@ -136,6 +151,23 @@ func (o *GetShareMetricsParams) WriteToRequest(r runtime.ClientRequest, reg strf } var res []error + if o.Duration != nil { + + // query param duration + var qrDuration float64 + + if o.Duration != nil { + qrDuration = *o.Duration + } + qDuration := swag.FormatFloat64(qrDuration) + if qDuration != "" { + + if err := r.SetQueryParam("duration", qDuration); err != nil { + return err + } + } + } + // path param shrToken if err := r.SetPathParam("shrToken", o.ShrToken); err != nil { return err diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 59341826..46e97057 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -531,6 +531,13 @@ func init() { "metrics" ], "operationId": "getAccountMetrics", + "parameters": [ + { + "type": "number", + "name": "duration", + "in": "query" + } + ], "responses": { "200": { "description": "account metrics", @@ -558,6 +565,11 @@ func init() { "name": "envId", "in": "path", "required": true + }, + { + "type": "number", + "name": "duration", + "in": "query" } ], "responses": { @@ -590,6 +602,11 @@ func init() { "name": "shrToken", "in": "path", "required": true + }, + { + "type": "number", + "name": "duration", + "in": "query" } ], "responses": { @@ -1924,6 +1941,13 @@ func init() { "metrics" ], "operationId": "getAccountMetrics", + "parameters": [ + { + "type": "number", + "name": "duration", + "in": "query" + } + ], "responses": { "200": { "description": "account metrics", @@ -1951,6 +1975,11 @@ func init() { "name": "envId", "in": "path", "required": true + }, + { + "type": "number", + "name": "duration", + "in": "query" } ], "responses": { @@ -1983,6 +2012,11 @@ func init() { "name": "shrToken", "in": "path", "required": true + }, + { + "type": "number", + "name": "duration", + "in": "query" } ], "responses": { diff --git a/rest_server_zrok/operations/metrics/get_account_metrics_parameters.go b/rest_server_zrok/operations/metrics/get_account_metrics_parameters.go index 98bb9b8c..afe23af1 100644 --- a/rest_server_zrok/operations/metrics/get_account_metrics_parameters.go +++ b/rest_server_zrok/operations/metrics/get_account_metrics_parameters.go @@ -9,7 +9,10 @@ import ( "net/http" "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) // NewGetAccountMetricsParams creates a new GetAccountMetricsParams object @@ -28,6 +31,11 @@ type GetAccountMetricsParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` + + /* + In: query + */ + Duration *float64 } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface @@ -39,8 +47,37 @@ func (o *GetAccountMetricsParams) BindRequest(r *http.Request, route *middleware o.HTTPRequest = r + qs := runtime.Values(r.URL.Query()) + + qDuration, qhkDuration, _ := qs.GetOK("duration") + if err := o.bindDuration(qDuration, qhkDuration, route.Formats); err != nil { + res = append(res, err) + } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } + +// bindDuration binds and validates parameter Duration from query. +func (o *GetAccountMetricsParams) bindDuration(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + return nil + } + + value, err := swag.ConvertFloat64(raw) + if err != nil { + return errors.InvalidType("duration", "query", "float64", raw) + } + o.Duration = &value + + return nil +} diff --git a/rest_server_zrok/operations/metrics/get_account_metrics_urlbuilder.go b/rest_server_zrok/operations/metrics/get_account_metrics_urlbuilder.go index 16b79838..79001f68 100644 --- a/rest_server_zrok/operations/metrics/get_account_metrics_urlbuilder.go +++ b/rest_server_zrok/operations/metrics/get_account_metrics_urlbuilder.go @@ -9,11 +9,17 @@ import ( "errors" "net/url" golangswaggerpaths "path" + + "github.com/go-openapi/swag" ) // GetAccountMetricsURL generates an URL for the get account metrics operation type GetAccountMetricsURL struct { + Duration *float64 + _basePath string + // avoid unkeyed usage + _ struct{} } // WithBasePath sets the base path for this url builder, only required when it's different from the @@ -43,6 +49,18 @@ func (o *GetAccountMetricsURL) Build() (*url.URL, error) { } _result.Path = golangswaggerpaths.Join(_basePath, _path) + qs := make(url.Values) + + var durationQ string + if o.Duration != nil { + durationQ = swag.FormatFloat64(*o.Duration) + } + if durationQ != "" { + qs.Set("duration", durationQ) + } + + _result.RawQuery = qs.Encode() + return &_result, nil } diff --git a/rest_server_zrok/operations/metrics/get_environment_metrics_parameters.go b/rest_server_zrok/operations/metrics/get_environment_metrics_parameters.go index 1f10409f..69bfb627 100644 --- a/rest_server_zrok/operations/metrics/get_environment_metrics_parameters.go +++ b/rest_server_zrok/operations/metrics/get_environment_metrics_parameters.go @@ -9,8 +9,10 @@ import ( "net/http" "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) // NewGetEnvironmentMetricsParams creates a new GetEnvironmentMetricsParams object @@ -30,6 +32,10 @@ type GetEnvironmentMetricsParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` + /* + In: query + */ + Duration *float64 /* Required: true In: path @@ -46,6 +52,13 @@ func (o *GetEnvironmentMetricsParams) BindRequest(r *http.Request, route *middle o.HTTPRequest = r + qs := runtime.Values(r.URL.Query()) + + qDuration, qhkDuration, _ := qs.GetOK("duration") + if err := o.bindDuration(qDuration, qhkDuration, route.Formats); err != nil { + res = append(res, err) + } + rEnvID, rhkEnvID, _ := route.Params.GetOK("envId") if err := o.bindEnvID(rEnvID, rhkEnvID, route.Formats); err != nil { res = append(res, err) @@ -56,6 +69,29 @@ func (o *GetEnvironmentMetricsParams) BindRequest(r *http.Request, route *middle return nil } +// bindDuration binds and validates parameter Duration from query. +func (o *GetEnvironmentMetricsParams) bindDuration(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + return nil + } + + value, err := swag.ConvertFloat64(raw) + if err != nil { + return errors.InvalidType("duration", "query", "float64", raw) + } + o.Duration = &value + + return nil +} + // bindEnvID binds and validates parameter EnvID from path. func (o *GetEnvironmentMetricsParams) bindEnvID(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string diff --git a/rest_server_zrok/operations/metrics/get_environment_metrics_urlbuilder.go b/rest_server_zrok/operations/metrics/get_environment_metrics_urlbuilder.go index a31dfd88..9693cf12 100644 --- a/rest_server_zrok/operations/metrics/get_environment_metrics_urlbuilder.go +++ b/rest_server_zrok/operations/metrics/get_environment_metrics_urlbuilder.go @@ -10,12 +10,16 @@ import ( "net/url" golangswaggerpaths "path" "strings" + + "github.com/go-openapi/swag" ) // GetEnvironmentMetricsURL generates an URL for the get environment metrics operation type GetEnvironmentMetricsURL struct { EnvID string + Duration *float64 + _basePath string // avoid unkeyed usage _ struct{} @@ -55,6 +59,18 @@ func (o *GetEnvironmentMetricsURL) Build() (*url.URL, error) { } _result.Path = golangswaggerpaths.Join(_basePath, _path) + qs := make(url.Values) + + var durationQ string + if o.Duration != nil { + durationQ = swag.FormatFloat64(*o.Duration) + } + if durationQ != "" { + qs.Set("duration", durationQ) + } + + _result.RawQuery = qs.Encode() + return &_result, nil } diff --git a/rest_server_zrok/operations/metrics/get_share_metrics_parameters.go b/rest_server_zrok/operations/metrics/get_share_metrics_parameters.go index 1013bb7a..efc3d81d 100644 --- a/rest_server_zrok/operations/metrics/get_share_metrics_parameters.go +++ b/rest_server_zrok/operations/metrics/get_share_metrics_parameters.go @@ -9,8 +9,10 @@ import ( "net/http" "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) // NewGetShareMetricsParams creates a new GetShareMetricsParams object @@ -30,6 +32,10 @@ type GetShareMetricsParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` + /* + In: query + */ + Duration *float64 /* Required: true In: path @@ -46,6 +52,13 @@ func (o *GetShareMetricsParams) BindRequest(r *http.Request, route *middleware.M o.HTTPRequest = r + qs := runtime.Values(r.URL.Query()) + + qDuration, qhkDuration, _ := qs.GetOK("duration") + if err := o.bindDuration(qDuration, qhkDuration, route.Formats); err != nil { + res = append(res, err) + } + rShrToken, rhkShrToken, _ := route.Params.GetOK("shrToken") if err := o.bindShrToken(rShrToken, rhkShrToken, route.Formats); err != nil { res = append(res, err) @@ -56,6 +69,29 @@ func (o *GetShareMetricsParams) BindRequest(r *http.Request, route *middleware.M return nil } +// bindDuration binds and validates parameter Duration from query. +func (o *GetShareMetricsParams) bindDuration(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + return nil + } + + value, err := swag.ConvertFloat64(raw) + if err != nil { + return errors.InvalidType("duration", "query", "float64", raw) + } + o.Duration = &value + + return nil +} + // bindShrToken binds and validates parameter ShrToken from path. func (o *GetShareMetricsParams) bindShrToken(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string diff --git a/rest_server_zrok/operations/metrics/get_share_metrics_urlbuilder.go b/rest_server_zrok/operations/metrics/get_share_metrics_urlbuilder.go index deeb05e3..3477a21a 100644 --- a/rest_server_zrok/operations/metrics/get_share_metrics_urlbuilder.go +++ b/rest_server_zrok/operations/metrics/get_share_metrics_urlbuilder.go @@ -10,12 +10,16 @@ import ( "net/url" golangswaggerpaths "path" "strings" + + "github.com/go-openapi/swag" ) // GetShareMetricsURL generates an URL for the get share metrics operation type GetShareMetricsURL struct { ShrToken string + Duration *float64 + _basePath string // avoid unkeyed usage _ struct{} @@ -55,6 +59,18 @@ func (o *GetShareMetricsURL) Build() (*url.URL, error) { } _result.Path = golangswaggerpaths.Join(_basePath, _path) + qs := make(url.Values) + + var durationQ string + if o.Duration != nil { + durationQ = swag.FormatFloat64(*o.Duration) + } + if durationQ != "" { + qs.Set("duration", durationQ) + } + + _result.RawQuery = qs.Encode() + return &_result, nil } diff --git a/specs/zrok.yml b/specs/zrok.yml index ffe081b8..7c9f97fc 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -415,6 +415,10 @@ paths: security: - key: [] operationId: getAccountMetrics + parameters: + - name: duration + in: query + type: number responses: 200: description: account metrics @@ -433,6 +437,9 @@ paths: in: path type: string required: true + - name: duration + in: query + type: number responses: 200: description: environment metrics @@ -453,6 +460,9 @@ paths: in: path type: string required: true + - name: duration + in: query + type: number responses: 200: description: share metrics diff --git a/ui/src/api/metrics.js b/ui/src/api/metrics.js index 5c1111dd..cc0063de 100644 --- a/ui/src/api/metrics.js +++ b/ui/src/api/metrics.js @@ -3,19 +3,34 @@ import * as gateway from './gateway' /** + * @param {object} options Optional options + * @param {number} [options.duration] + * @return {Promise<module:types.metrics>} account metrics */ -export function getAccountMetrics() { - return gateway.request(getAccountMetricsOperation) +export function getAccountMetrics(options) { + if (!options) options = {} + const parameters = { + query: { + duration: options.duration + } + } + return gateway.request(getAccountMetricsOperation, parameters) } /** * @param {string} envId + * @param {object} options Optional options + * @param {number} [options.duration] * @return {Promise<module:types.metrics>} environment metrics */ -export function getEnvironmentMetrics(envId) { +export function getEnvironmentMetrics(envId, options) { + if (!options) options = {} const parameters = { path: { envId + }, + query: { + duration: options.duration } } return gateway.request(getEnvironmentMetricsOperation, parameters) @@ -23,12 +38,18 @@ export function getEnvironmentMetrics(envId) { /** * @param {string} shrToken + * @param {object} options Optional options + * @param {number} [options.duration] * @return {Promise<module:types.metrics>} share metrics */ -export function getShareMetrics(shrToken) { +export function getShareMetrics(shrToken, options) { + if (!options) options = {} const parameters = { path: { shrToken + }, + query: { + duration: options.duration } } return gateway.request(getShareMetricsOperation, parameters) diff --git a/ui/src/console/detail/environment/SharesTab.js b/ui/src/console/detail/environment/SharesTab.js index e902529c..6fb01439 100644 --- a/ui/src/console/detail/environment/SharesTab.js +++ b/ui/src/console/detail/environment/SharesTab.js @@ -46,7 +46,7 @@ const SharesTab = (props) => { cell: row => { return <ResponsiveContainer width={"100%"} height={"100%"}> <AreaChart data={row.metrics}> - <Area type="basis" dataKey={(v) => v} stroke={"#231069"} fillOpacity={1} fill={"#655796"} isAnimationActive={false} dot={false} /> + <Area type="basis" dataKey={(v) => v} stroke={"#777"} fillOpacity={0.5} fill={"#04adef"} isAnimationActive={false} dot={false} /> </AreaChart> </ResponsiveContainer> } diff --git a/ui/src/console/detail/share/ShareDetail.js b/ui/src/console/detail/share/ShareDetail.js index 78371e1f..31501afc 100644 --- a/ui/src/console/detail/share/ShareDetail.js +++ b/ui/src/console/detail/share/ShareDetail.js @@ -42,7 +42,7 @@ const ShareDetail = (props) => { metrics: row => ( <ResponsiveContainer width={"100%"} height={"100%"}> <AreaChart data={row.value}> - <Area type="basis" dataKey={(v) => v} stroke={"#231069"} fillOpacity={1} fill={"#655796"} isAnimationActive={false} dot={false} /> + <Area type="basis" dataKey={(v) => v} stroke={"#777"} fillOpacity={0.5} fill={"#04adef"} isAnimationActive={false} dot={false} /> </AreaChart> </ResponsiveContainer> ), diff --git a/ui/src/console/visualizer/Network.js b/ui/src/console/visualizer/Network.js index 3de59eb9..c75421c4 100644 --- a/ui/src/console/visualizer/Network.js +++ b/ui/src/console/visualizer/Network.js @@ -21,8 +21,8 @@ const Network = (props) => { }, []); const paintNode = (node, ctx) => { - let nodeColor = node.selected ? "#04adef" : "#9BF316"; - let textColor = node.selected ? "white" : "black"; + let nodeColor = node.selected ? "#9BF316" : "#04adef"; + let textColor = node.selected ? "black" : "white"; ctx.textBaseline = "middle"; ctx.textAlign = "center"; diff --git a/ui/src/console/visualizer/graph.js b/ui/src/console/visualizer/graph.js index 4e068555..7a445081 100644 --- a/ui/src/console/visualizer/graph.js +++ b/ui/src/console/visualizer/graph.js @@ -40,7 +40,7 @@ export const mergeGraph = (oldGraph, user, newOverview) => { newGraph.links.push({ target: accountNode.id, source: envNode.id, - color: "#9BF316" + color: "#04adef" }); if(env.shares) { env.shares.forEach(shr => { @@ -60,7 +60,7 @@ export const mergeGraph = (oldGraph, user, newOverview) => { newGraph.links.push({ target: envNode.id, source: shrNode.id, - color: "#9BF316" + color: "#04adef" }); }); } From 6b078abcd77ac7aeef6cc35f236b38e79592d8f6 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Tue, 9 May 2023 14:16:01 -0400 Subject: [PATCH 09/49] account metrics endpoint (#319) --- controller/controller.go | 3 + controller/metrics.go | 84 +++++++ .../get_account_metrics_parameters.go | 160 +++++++++++++ .../metadata/get_account_metrics_responses.go | 212 ++++++++++++++++++ .../get_environment_metrics_parameters.go | 179 +++++++++++++++ .../get_environment_metrics_responses.go | 155 +++++++++++++ .../metadata/get_share_metrics_parameters.go | 179 +++++++++++++++ .../metadata/get_share_metrics_responses.go | 155 +++++++++++++ rest_client_zrok/metadata/metadata_client.go | 123 ++++++++++ rest_client_zrok/zrok_client.go | 5 - rest_server_zrok/embedded_spec.go | 36 ++- .../metadata/get_account_metrics.go | 71 ++++++ .../get_account_metrics_parameters.go | 77 +++++++ .../metadata/get_account_metrics_responses.go | 109 +++++++++ .../get_account_metrics_urlbuilder.go | 103 +++++++++ .../metadata/get_environment_metrics.go | 71 ++++++ .../get_environment_metrics_parameters.go | 101 +++++++++ .../get_environment_metrics_responses.go | 84 +++++++ .../get_environment_metrics_urlbuilder.go | 113 ++++++++++ .../operations/metadata/get_share_metrics.go | 71 ++++++ .../metadata/get_share_metrics_parameters.go | 101 +++++++++ .../metadata/get_share_metrics_responses.go | 84 +++++++ .../metadata/get_share_metrics_urlbuilder.go | 113 ++++++++++ rest_server_zrok/operations/zrok_api.go | 43 ++-- specs/zrok.yml | 42 ++-- ui/src/api/metadata.js | 83 +++++++ ui/src/api/metrics.js | 86 ------- 27 files changed, 2498 insertions(+), 145 deletions(-) create mode 100644 controller/metrics.go create mode 100644 rest_client_zrok/metadata/get_account_metrics_parameters.go create mode 100644 rest_client_zrok/metadata/get_account_metrics_responses.go create mode 100644 rest_client_zrok/metadata/get_environment_metrics_parameters.go create mode 100644 rest_client_zrok/metadata/get_environment_metrics_responses.go create mode 100644 rest_client_zrok/metadata/get_share_metrics_parameters.go create mode 100644 rest_client_zrok/metadata/get_share_metrics_responses.go create mode 100644 rest_server_zrok/operations/metadata/get_account_metrics.go create mode 100644 rest_server_zrok/operations/metadata/get_account_metrics_parameters.go create mode 100644 rest_server_zrok/operations/metadata/get_account_metrics_responses.go create mode 100644 rest_server_zrok/operations/metadata/get_account_metrics_urlbuilder.go create mode 100644 rest_server_zrok/operations/metadata/get_environment_metrics.go create mode 100644 rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go create mode 100644 rest_server_zrok/operations/metadata/get_environment_metrics_responses.go create mode 100644 rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go create mode 100644 rest_server_zrok/operations/metadata/get_share_metrics.go create mode 100644 rest_server_zrok/operations/metadata/get_share_metrics_parameters.go create mode 100644 rest_server_zrok/operations/metadata/get_share_metrics_responses.go create mode 100644 rest_server_zrok/operations/metadata/get_share_metrics_urlbuilder.go delete mode 100644 ui/src/api/metrics.js diff --git a/controller/controller.go b/controller/controller.go index 1d05e128..34905ed8 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -47,6 +47,9 @@ func Run(inCfg *config.Config) error { api.EnvironmentEnableHandler = newEnableHandler() api.EnvironmentDisableHandler = newDisableHandler() api.MetadataConfigurationHandler = newConfigurationHandler(cfg) + if cfg.Metrics != nil && cfg.Metrics.Influx != nil { + api.MetadataGetAccountMetricsHandler = newGetAccountMetricsHandler(cfg.Metrics.Influx) + } api.MetadataGetEnvironmentDetailHandler = newEnvironmentDetailHandler() api.MetadataGetShareDetailHandler = newShareDetailHandler() api.MetadataOverviewHandler = metadata.OverviewHandlerFunc(overviewHandler) diff --git a/controller/metrics.go b/controller/metrics.go new file mode 100644 index 00000000..a5091b0f --- /dev/null +++ b/controller/metrics.go @@ -0,0 +1,84 @@ +package controller + +import ( + "context" + "fmt" + "github.com/go-openapi/runtime/middleware" + influxdb2 "github.com/influxdata/influxdb-client-go/v2" + "github.com/influxdata/influxdb-client-go/v2/api" + "github.com/openziti/zrok/controller/metrics" + "github.com/openziti/zrok/rest_model_zrok" + "github.com/openziti/zrok/rest_server_zrok/operations/metadata" + "github.com/sirupsen/logrus" + "time" +) + +type getAccountMetricsHandler struct { + cfg *metrics.InfluxConfig + idb influxdb2.Client + queryApi api.QueryAPI +} + +func newGetAccountMetricsHandler(cfg *metrics.InfluxConfig) *getAccountMetricsHandler { + idb := influxdb2.NewClient(cfg.Url, cfg.Token) + queryApi := idb.QueryAPI(cfg.Org) + return &getAccountMetricsHandler{ + cfg: cfg, + idb: idb, + queryApi: queryApi, + } +} + +func (h *getAccountMetricsHandler) Handle(params metadata.GetAccountMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + duration := 30 * 24 * time.Hour + if params.Duration != nil { + v, err := time.ParseDuration(*params.Duration) + if err != nil { + logrus.Errorf("bad duration '%v' for '%v': %v", params.Duration, principal.Email, err) + return metadata.NewGetAccountMetricsBadRequest() + } + duration = v + } + slice := duration / 200 + + query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + + fmt.Sprintf("|> range(start -%v)\n", duration) + + "|> filter(fn: (r) => r[\"_measurement\"] == \"xfer\")\n" + + "|> filter(fn: (r) => r[\"_field\"] == \"rx\" or r[\"_field\"] == \"tx\")\n" + + "|> filter(fn: (r) => r[\"namespace\"] == \"backend\")\n" + + fmt.Sprintf("|> filter(fn: (r) => r[\"acctId\"] == \"%d\")\n", principal.ID) + + "|> drop(columns: [\"share\", \"envId\"])\n" + + fmt.Sprintf("|> aggregateWindow(every: %v, fn: sum, createEmpty: true)", slice) + + rx, tx, err := runFluxForRxTxArray(query, h.queryApi) + if err != nil { + logrus.Errorf("error running account metrics query for '%v': %v", principal.Email, err) + return metadata.NewGetAccountMetricsInternalServerError() + } + + response := &rest_model_zrok.Metrics{ + ID: fmt.Sprintf("%d", principal.ID), + Period: duration.Seconds(), + Rx: rx, + Tx: tx, + } + return metadata.NewGetAccountMetricsOK().WithPayload(response) +} + +func runFluxForRxTxArray(query string, queryApi api.QueryAPI) (rx, tx []float64, err error) { + result, err := queryApi.Query(context.Background(), query) + if err != nil { + return nil, nil, err + } + for result.Next() { + if v, ok := result.Record().Value().(int64); ok { + switch result.Record().Field() { + case "rx": + rx = append(rx, float64(v)) + case "tx": + tx = append(tx, float64(v)) + } + } + } + return rx, tx, nil +} diff --git a/rest_client_zrok/metadata/get_account_metrics_parameters.go b/rest_client_zrok/metadata/get_account_metrics_parameters.go new file mode 100644 index 00000000..2c74d9b9 --- /dev/null +++ b/rest_client_zrok/metadata/get_account_metrics_parameters.go @@ -0,0 +1,160 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewGetAccountMetricsParams creates a new GetAccountMetricsParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetAccountMetricsParams() *GetAccountMetricsParams { + return &GetAccountMetricsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetAccountMetricsParamsWithTimeout creates a new GetAccountMetricsParams object +// with the ability to set a timeout on a request. +func NewGetAccountMetricsParamsWithTimeout(timeout time.Duration) *GetAccountMetricsParams { + return &GetAccountMetricsParams{ + timeout: timeout, + } +} + +// NewGetAccountMetricsParamsWithContext creates a new GetAccountMetricsParams object +// with the ability to set a context for a request. +func NewGetAccountMetricsParamsWithContext(ctx context.Context) *GetAccountMetricsParams { + return &GetAccountMetricsParams{ + Context: ctx, + } +} + +// NewGetAccountMetricsParamsWithHTTPClient creates a new GetAccountMetricsParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetAccountMetricsParamsWithHTTPClient(client *http.Client) *GetAccountMetricsParams { + return &GetAccountMetricsParams{ + HTTPClient: client, + } +} + +/* +GetAccountMetricsParams contains all the parameters to send to the API endpoint + + for the get account metrics operation. + + Typically these are written to a http.Request. +*/ +type GetAccountMetricsParams struct { + + // Duration. + Duration *string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get account metrics params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetAccountMetricsParams) WithDefaults() *GetAccountMetricsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get account metrics params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetAccountMetricsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get account metrics params +func (o *GetAccountMetricsParams) WithTimeout(timeout time.Duration) *GetAccountMetricsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get account metrics params +func (o *GetAccountMetricsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get account metrics params +func (o *GetAccountMetricsParams) WithContext(ctx context.Context) *GetAccountMetricsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get account metrics params +func (o *GetAccountMetricsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get account metrics params +func (o *GetAccountMetricsParams) WithHTTPClient(client *http.Client) *GetAccountMetricsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get account metrics params +func (o *GetAccountMetricsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithDuration adds the duration to the get account metrics params +func (o *GetAccountMetricsParams) WithDuration(duration *string) *GetAccountMetricsParams { + o.SetDuration(duration) + return o +} + +// SetDuration adds the duration to the get account metrics params +func (o *GetAccountMetricsParams) SetDuration(duration *string) { + o.Duration = duration +} + +// WriteToRequest writes these params to a swagger request +func (o *GetAccountMetricsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if o.Duration != nil { + + // query param duration + var qrDuration string + + if o.Duration != nil { + qrDuration = *o.Duration + } + qDuration := qrDuration + if qDuration != "" { + + if err := r.SetQueryParam("duration", qDuration); err != nil { + return err + } + } + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_client_zrok/metadata/get_account_metrics_responses.go b/rest_client_zrok/metadata/get_account_metrics_responses.go new file mode 100644 index 00000000..5613b3a5 --- /dev/null +++ b/rest_client_zrok/metadata/get_account_metrics_responses.go @@ -0,0 +1,212 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetAccountMetricsReader is a Reader for the GetAccountMetrics structure. +type GetAccountMetricsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetAccountMetricsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetAccountMetricsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 400: + result := NewGetAccountMetricsBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 500: + result := NewGetAccountMetricsInternalServerError() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewGetAccountMetricsOK creates a GetAccountMetricsOK with default headers values +func NewGetAccountMetricsOK() *GetAccountMetricsOK { + return &GetAccountMetricsOK{} +} + +/* +GetAccountMetricsOK describes a response with status code 200, with default header values. + +account metrics +*/ +type GetAccountMetricsOK struct { + Payload *rest_model_zrok.Metrics +} + +// IsSuccess returns true when this get account metrics o k response has a 2xx status code +func (o *GetAccountMetricsOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get account metrics o k response has a 3xx status code +func (o *GetAccountMetricsOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get account metrics o k response has a 4xx status code +func (o *GetAccountMetricsOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get account metrics o k response has a 5xx status code +func (o *GetAccountMetricsOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get account metrics o k response a status code equal to that given +func (o *GetAccountMetricsOK) IsCode(code int) bool { + return code == 200 +} + +func (o *GetAccountMetricsOK) Error() string { + return fmt.Sprintf("[GET /metrics/account][%d] getAccountMetricsOK %+v", 200, o.Payload) +} + +func (o *GetAccountMetricsOK) String() string { + return fmt.Sprintf("[GET /metrics/account][%d] getAccountMetricsOK %+v", 200, o.Payload) +} + +func (o *GetAccountMetricsOK) GetPayload() *rest_model_zrok.Metrics { + return o.Payload +} + +func (o *GetAccountMetricsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(rest_model_zrok.Metrics) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetAccountMetricsBadRequest creates a GetAccountMetricsBadRequest with default headers values +func NewGetAccountMetricsBadRequest() *GetAccountMetricsBadRequest { + return &GetAccountMetricsBadRequest{} +} + +/* +GetAccountMetricsBadRequest describes a response with status code 400, with default header values. + +bad request +*/ +type GetAccountMetricsBadRequest struct { +} + +// IsSuccess returns true when this get account metrics bad request response has a 2xx status code +func (o *GetAccountMetricsBadRequest) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get account metrics bad request response has a 3xx status code +func (o *GetAccountMetricsBadRequest) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get account metrics bad request response has a 4xx status code +func (o *GetAccountMetricsBadRequest) IsClientError() bool { + return true +} + +// IsServerError returns true when this get account metrics bad request response has a 5xx status code +func (o *GetAccountMetricsBadRequest) IsServerError() bool { + return false +} + +// IsCode returns true when this get account metrics bad request response a status code equal to that given +func (o *GetAccountMetricsBadRequest) IsCode(code int) bool { + return code == 400 +} + +func (o *GetAccountMetricsBadRequest) Error() string { + return fmt.Sprintf("[GET /metrics/account][%d] getAccountMetricsBadRequest ", 400) +} + +func (o *GetAccountMetricsBadRequest) String() string { + return fmt.Sprintf("[GET /metrics/account][%d] getAccountMetricsBadRequest ", 400) +} + +func (o *GetAccountMetricsBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + +// NewGetAccountMetricsInternalServerError creates a GetAccountMetricsInternalServerError with default headers values +func NewGetAccountMetricsInternalServerError() *GetAccountMetricsInternalServerError { + return &GetAccountMetricsInternalServerError{} +} + +/* +GetAccountMetricsInternalServerError describes a response with status code 500, with default header values. + +internal server error +*/ +type GetAccountMetricsInternalServerError struct { +} + +// IsSuccess returns true when this get account metrics internal server error response has a 2xx status code +func (o *GetAccountMetricsInternalServerError) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get account metrics internal server error response has a 3xx status code +func (o *GetAccountMetricsInternalServerError) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get account metrics internal server error response has a 4xx status code +func (o *GetAccountMetricsInternalServerError) IsClientError() bool { + return false +} + +// IsServerError returns true when this get account metrics internal server error response has a 5xx status code +func (o *GetAccountMetricsInternalServerError) IsServerError() bool { + return true +} + +// IsCode returns true when this get account metrics internal server error response a status code equal to that given +func (o *GetAccountMetricsInternalServerError) IsCode(code int) bool { + return code == 500 +} + +func (o *GetAccountMetricsInternalServerError) Error() string { + return fmt.Sprintf("[GET /metrics/account][%d] getAccountMetricsInternalServerError ", 500) +} + +func (o *GetAccountMetricsInternalServerError) String() string { + return fmt.Sprintf("[GET /metrics/account][%d] getAccountMetricsInternalServerError ", 500) +} + +func (o *GetAccountMetricsInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} diff --git a/rest_client_zrok/metadata/get_environment_metrics_parameters.go b/rest_client_zrok/metadata/get_environment_metrics_parameters.go new file mode 100644 index 00000000..be7aae16 --- /dev/null +++ b/rest_client_zrok/metadata/get_environment_metrics_parameters.go @@ -0,0 +1,179 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewGetEnvironmentMetricsParams creates a new GetEnvironmentMetricsParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetEnvironmentMetricsParams() *GetEnvironmentMetricsParams { + return &GetEnvironmentMetricsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetEnvironmentMetricsParamsWithTimeout creates a new GetEnvironmentMetricsParams object +// with the ability to set a timeout on a request. +func NewGetEnvironmentMetricsParamsWithTimeout(timeout time.Duration) *GetEnvironmentMetricsParams { + return &GetEnvironmentMetricsParams{ + timeout: timeout, + } +} + +// NewGetEnvironmentMetricsParamsWithContext creates a new GetEnvironmentMetricsParams object +// with the ability to set a context for a request. +func NewGetEnvironmentMetricsParamsWithContext(ctx context.Context) *GetEnvironmentMetricsParams { + return &GetEnvironmentMetricsParams{ + Context: ctx, + } +} + +// NewGetEnvironmentMetricsParamsWithHTTPClient creates a new GetEnvironmentMetricsParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetEnvironmentMetricsParamsWithHTTPClient(client *http.Client) *GetEnvironmentMetricsParams { + return &GetEnvironmentMetricsParams{ + HTTPClient: client, + } +} + +/* +GetEnvironmentMetricsParams contains all the parameters to send to the API endpoint + + for the get environment metrics operation. + + Typically these are written to a http.Request. +*/ +type GetEnvironmentMetricsParams struct { + + // Duration. + Duration *string + + // EnvID. + EnvID string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get environment metrics params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetEnvironmentMetricsParams) WithDefaults() *GetEnvironmentMetricsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get environment metrics params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetEnvironmentMetricsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get environment metrics params +func (o *GetEnvironmentMetricsParams) WithTimeout(timeout time.Duration) *GetEnvironmentMetricsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get environment metrics params +func (o *GetEnvironmentMetricsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get environment metrics params +func (o *GetEnvironmentMetricsParams) WithContext(ctx context.Context) *GetEnvironmentMetricsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get environment metrics params +func (o *GetEnvironmentMetricsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get environment metrics params +func (o *GetEnvironmentMetricsParams) WithHTTPClient(client *http.Client) *GetEnvironmentMetricsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get environment metrics params +func (o *GetEnvironmentMetricsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithDuration adds the duration to the get environment metrics params +func (o *GetEnvironmentMetricsParams) WithDuration(duration *string) *GetEnvironmentMetricsParams { + o.SetDuration(duration) + return o +} + +// SetDuration adds the duration to the get environment metrics params +func (o *GetEnvironmentMetricsParams) SetDuration(duration *string) { + o.Duration = duration +} + +// WithEnvID adds the envID to the get environment metrics params +func (o *GetEnvironmentMetricsParams) WithEnvID(envID string) *GetEnvironmentMetricsParams { + o.SetEnvID(envID) + return o +} + +// SetEnvID adds the envId to the get environment metrics params +func (o *GetEnvironmentMetricsParams) SetEnvID(envID string) { + o.EnvID = envID +} + +// WriteToRequest writes these params to a swagger request +func (o *GetEnvironmentMetricsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if o.Duration != nil { + + // query param duration + var qrDuration string + + if o.Duration != nil { + qrDuration = *o.Duration + } + qDuration := qrDuration + if qDuration != "" { + + if err := r.SetQueryParam("duration", qDuration); err != nil { + return err + } + } + } + + // path param envId + if err := r.SetPathParam("envId", o.EnvID); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_client_zrok/metadata/get_environment_metrics_responses.go b/rest_client_zrok/metadata/get_environment_metrics_responses.go new file mode 100644 index 00000000..36e1e2f6 --- /dev/null +++ b/rest_client_zrok/metadata/get_environment_metrics_responses.go @@ -0,0 +1,155 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetEnvironmentMetricsReader is a Reader for the GetEnvironmentMetrics structure. +type GetEnvironmentMetricsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetEnvironmentMetricsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetEnvironmentMetricsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 401: + result := NewGetEnvironmentMetricsUnauthorized() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewGetEnvironmentMetricsOK creates a GetEnvironmentMetricsOK with default headers values +func NewGetEnvironmentMetricsOK() *GetEnvironmentMetricsOK { + return &GetEnvironmentMetricsOK{} +} + +/* +GetEnvironmentMetricsOK describes a response with status code 200, with default header values. + +environment metrics +*/ +type GetEnvironmentMetricsOK struct { + Payload *rest_model_zrok.Metrics +} + +// IsSuccess returns true when this get environment metrics o k response has a 2xx status code +func (o *GetEnvironmentMetricsOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get environment metrics o k response has a 3xx status code +func (o *GetEnvironmentMetricsOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get environment metrics o k response has a 4xx status code +func (o *GetEnvironmentMetricsOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get environment metrics o k response has a 5xx status code +func (o *GetEnvironmentMetricsOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get environment metrics o k response a status code equal to that given +func (o *GetEnvironmentMetricsOK) IsCode(code int) bool { + return code == 200 +} + +func (o *GetEnvironmentMetricsOK) Error() string { + return fmt.Sprintf("[GET /metrics/environment/{envId}][%d] getEnvironmentMetricsOK %+v", 200, o.Payload) +} + +func (o *GetEnvironmentMetricsOK) String() string { + return fmt.Sprintf("[GET /metrics/environment/{envId}][%d] getEnvironmentMetricsOK %+v", 200, o.Payload) +} + +func (o *GetEnvironmentMetricsOK) GetPayload() *rest_model_zrok.Metrics { + return o.Payload +} + +func (o *GetEnvironmentMetricsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(rest_model_zrok.Metrics) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetEnvironmentMetricsUnauthorized creates a GetEnvironmentMetricsUnauthorized with default headers values +func NewGetEnvironmentMetricsUnauthorized() *GetEnvironmentMetricsUnauthorized { + return &GetEnvironmentMetricsUnauthorized{} +} + +/* +GetEnvironmentMetricsUnauthorized describes a response with status code 401, with default header values. + +unauthorized +*/ +type GetEnvironmentMetricsUnauthorized struct { +} + +// IsSuccess returns true when this get environment metrics unauthorized response has a 2xx status code +func (o *GetEnvironmentMetricsUnauthorized) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get environment metrics unauthorized response has a 3xx status code +func (o *GetEnvironmentMetricsUnauthorized) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get environment metrics unauthorized response has a 4xx status code +func (o *GetEnvironmentMetricsUnauthorized) IsClientError() bool { + return true +} + +// IsServerError returns true when this get environment metrics unauthorized response has a 5xx status code +func (o *GetEnvironmentMetricsUnauthorized) IsServerError() bool { + return false +} + +// IsCode returns true when this get environment metrics unauthorized response a status code equal to that given +func (o *GetEnvironmentMetricsUnauthorized) IsCode(code int) bool { + return code == 401 +} + +func (o *GetEnvironmentMetricsUnauthorized) Error() string { + return fmt.Sprintf("[GET /metrics/environment/{envId}][%d] getEnvironmentMetricsUnauthorized ", 401) +} + +func (o *GetEnvironmentMetricsUnauthorized) String() string { + return fmt.Sprintf("[GET /metrics/environment/{envId}][%d] getEnvironmentMetricsUnauthorized ", 401) +} + +func (o *GetEnvironmentMetricsUnauthorized) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} diff --git a/rest_client_zrok/metadata/get_share_metrics_parameters.go b/rest_client_zrok/metadata/get_share_metrics_parameters.go new file mode 100644 index 00000000..96f5ac62 --- /dev/null +++ b/rest_client_zrok/metadata/get_share_metrics_parameters.go @@ -0,0 +1,179 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewGetShareMetricsParams creates a new GetShareMetricsParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetShareMetricsParams() *GetShareMetricsParams { + return &GetShareMetricsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetShareMetricsParamsWithTimeout creates a new GetShareMetricsParams object +// with the ability to set a timeout on a request. +func NewGetShareMetricsParamsWithTimeout(timeout time.Duration) *GetShareMetricsParams { + return &GetShareMetricsParams{ + timeout: timeout, + } +} + +// NewGetShareMetricsParamsWithContext creates a new GetShareMetricsParams object +// with the ability to set a context for a request. +func NewGetShareMetricsParamsWithContext(ctx context.Context) *GetShareMetricsParams { + return &GetShareMetricsParams{ + Context: ctx, + } +} + +// NewGetShareMetricsParamsWithHTTPClient creates a new GetShareMetricsParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetShareMetricsParamsWithHTTPClient(client *http.Client) *GetShareMetricsParams { + return &GetShareMetricsParams{ + HTTPClient: client, + } +} + +/* +GetShareMetricsParams contains all the parameters to send to the API endpoint + + for the get share metrics operation. + + Typically these are written to a http.Request. +*/ +type GetShareMetricsParams struct { + + // Duration. + Duration *string + + // ShrToken. + ShrToken string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get share metrics params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetShareMetricsParams) WithDefaults() *GetShareMetricsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get share metrics params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetShareMetricsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get share metrics params +func (o *GetShareMetricsParams) WithTimeout(timeout time.Duration) *GetShareMetricsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get share metrics params +func (o *GetShareMetricsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get share metrics params +func (o *GetShareMetricsParams) WithContext(ctx context.Context) *GetShareMetricsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get share metrics params +func (o *GetShareMetricsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get share metrics params +func (o *GetShareMetricsParams) WithHTTPClient(client *http.Client) *GetShareMetricsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get share metrics params +func (o *GetShareMetricsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithDuration adds the duration to the get share metrics params +func (o *GetShareMetricsParams) WithDuration(duration *string) *GetShareMetricsParams { + o.SetDuration(duration) + return o +} + +// SetDuration adds the duration to the get share metrics params +func (o *GetShareMetricsParams) SetDuration(duration *string) { + o.Duration = duration +} + +// WithShrToken adds the shrToken to the get share metrics params +func (o *GetShareMetricsParams) WithShrToken(shrToken string) *GetShareMetricsParams { + o.SetShrToken(shrToken) + return o +} + +// SetShrToken adds the shrToken to the get share metrics params +func (o *GetShareMetricsParams) SetShrToken(shrToken string) { + o.ShrToken = shrToken +} + +// WriteToRequest writes these params to a swagger request +func (o *GetShareMetricsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if o.Duration != nil { + + // query param duration + var qrDuration string + + if o.Duration != nil { + qrDuration = *o.Duration + } + qDuration := qrDuration + if qDuration != "" { + + if err := r.SetQueryParam("duration", qDuration); err != nil { + return err + } + } + } + + // path param shrToken + if err := r.SetPathParam("shrToken", o.ShrToken); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_client_zrok/metadata/get_share_metrics_responses.go b/rest_client_zrok/metadata/get_share_metrics_responses.go new file mode 100644 index 00000000..2ed61ab8 --- /dev/null +++ b/rest_client_zrok/metadata/get_share_metrics_responses.go @@ -0,0 +1,155 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetShareMetricsReader is a Reader for the GetShareMetrics structure. +type GetShareMetricsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetShareMetricsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetShareMetricsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 401: + result := NewGetShareMetricsUnauthorized() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewGetShareMetricsOK creates a GetShareMetricsOK with default headers values +func NewGetShareMetricsOK() *GetShareMetricsOK { + return &GetShareMetricsOK{} +} + +/* +GetShareMetricsOK describes a response with status code 200, with default header values. + +share metrics +*/ +type GetShareMetricsOK struct { + Payload *rest_model_zrok.Metrics +} + +// IsSuccess returns true when this get share metrics o k response has a 2xx status code +func (o *GetShareMetricsOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get share metrics o k response has a 3xx status code +func (o *GetShareMetricsOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get share metrics o k response has a 4xx status code +func (o *GetShareMetricsOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get share metrics o k response has a 5xx status code +func (o *GetShareMetricsOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get share metrics o k response a status code equal to that given +func (o *GetShareMetricsOK) IsCode(code int) bool { + return code == 200 +} + +func (o *GetShareMetricsOK) Error() string { + return fmt.Sprintf("[GET /metrics/share/{shrToken}][%d] getShareMetricsOK %+v", 200, o.Payload) +} + +func (o *GetShareMetricsOK) String() string { + return fmt.Sprintf("[GET /metrics/share/{shrToken}][%d] getShareMetricsOK %+v", 200, o.Payload) +} + +func (o *GetShareMetricsOK) GetPayload() *rest_model_zrok.Metrics { + return o.Payload +} + +func (o *GetShareMetricsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(rest_model_zrok.Metrics) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetShareMetricsUnauthorized creates a GetShareMetricsUnauthorized with default headers values +func NewGetShareMetricsUnauthorized() *GetShareMetricsUnauthorized { + return &GetShareMetricsUnauthorized{} +} + +/* +GetShareMetricsUnauthorized describes a response with status code 401, with default header values. + +unauthorized +*/ +type GetShareMetricsUnauthorized struct { +} + +// IsSuccess returns true when this get share metrics unauthorized response has a 2xx status code +func (o *GetShareMetricsUnauthorized) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get share metrics unauthorized response has a 3xx status code +func (o *GetShareMetricsUnauthorized) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get share metrics unauthorized response has a 4xx status code +func (o *GetShareMetricsUnauthorized) IsClientError() bool { + return true +} + +// IsServerError returns true when this get share metrics unauthorized response has a 5xx status code +func (o *GetShareMetricsUnauthorized) IsServerError() bool { + return false +} + +// IsCode returns true when this get share metrics unauthorized response a status code equal to that given +func (o *GetShareMetricsUnauthorized) IsCode(code int) bool { + return code == 401 +} + +func (o *GetShareMetricsUnauthorized) Error() string { + return fmt.Sprintf("[GET /metrics/share/{shrToken}][%d] getShareMetricsUnauthorized ", 401) +} + +func (o *GetShareMetricsUnauthorized) String() string { + return fmt.Sprintf("[GET /metrics/share/{shrToken}][%d] getShareMetricsUnauthorized ", 401) +} + +func (o *GetShareMetricsUnauthorized) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} diff --git a/rest_client_zrok/metadata/metadata_client.go b/rest_client_zrok/metadata/metadata_client.go index 0253fbf0..11e3a1ca 100644 --- a/rest_client_zrok/metadata/metadata_client.go +++ b/rest_client_zrok/metadata/metadata_client.go @@ -32,10 +32,16 @@ type ClientOption func(*runtime.ClientOperation) type ClientService interface { Configuration(params *ConfigurationParams, opts ...ClientOption) (*ConfigurationOK, error) + GetAccountMetrics(params *GetAccountMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetAccountMetricsOK, error) + GetEnvironmentDetail(params *GetEnvironmentDetailParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetEnvironmentDetailOK, error) + GetEnvironmentMetrics(params *GetEnvironmentMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetEnvironmentMetricsOK, error) + GetShareDetail(params *GetShareDetailParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetShareDetailOK, error) + GetShareMetrics(params *GetShareMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetShareMetricsOK, error) + Overview(params *OverviewParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*OverviewOK, error) Version(params *VersionParams, opts ...ClientOption) (*VersionOK, error) @@ -81,6 +87,45 @@ func (a *Client) Configuration(params *ConfigurationParams, opts ...ClientOption panic(msg) } +/* +GetAccountMetrics get account metrics API +*/ +func (a *Client) GetAccountMetrics(params *GetAccountMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetAccountMetricsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetAccountMetricsParams() + } + op := &runtime.ClientOperation{ + ID: "getAccountMetrics", + Method: "GET", + PathPattern: "/metrics/account", + ProducesMediaTypes: []string{"application/zrok.v1+json"}, + ConsumesMediaTypes: []string{"application/zrok.v1+json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetAccountMetricsReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetAccountMetricsOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for getAccountMetrics: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + /* GetEnvironmentDetail get environment detail API */ @@ -120,6 +165,45 @@ func (a *Client) GetEnvironmentDetail(params *GetEnvironmentDetailParams, authIn panic(msg) } +/* +GetEnvironmentMetrics get environment metrics API +*/ +func (a *Client) GetEnvironmentMetrics(params *GetEnvironmentMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetEnvironmentMetricsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetEnvironmentMetricsParams() + } + op := &runtime.ClientOperation{ + ID: "getEnvironmentMetrics", + Method: "GET", + PathPattern: "/metrics/environment/{envId}", + ProducesMediaTypes: []string{"application/zrok.v1+json"}, + ConsumesMediaTypes: []string{"application/zrok.v1+json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetEnvironmentMetricsReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetEnvironmentMetricsOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for getEnvironmentMetrics: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + /* GetShareDetail get share detail API */ @@ -159,6 +243,45 @@ func (a *Client) GetShareDetail(params *GetShareDetailParams, authInfo runtime.C panic(msg) } +/* +GetShareMetrics get share metrics API +*/ +func (a *Client) GetShareMetrics(params *GetShareMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetShareMetricsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetShareMetricsParams() + } + op := &runtime.ClientOperation{ + ID: "getShareMetrics", + Method: "GET", + PathPattern: "/metrics/share/{shrToken}", + ProducesMediaTypes: []string{"application/zrok.v1+json"}, + ConsumesMediaTypes: []string{"application/zrok.v1+json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetShareMetricsReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetShareMetricsOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for getShareMetrics: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + /* Overview overview API */ diff --git a/rest_client_zrok/zrok_client.go b/rest_client_zrok/zrok_client.go index c5f02b8c..a4200e67 100644 --- a/rest_client_zrok/zrok_client.go +++ b/rest_client_zrok/zrok_client.go @@ -14,7 +14,6 @@ import ( "github.com/openziti/zrok/rest_client_zrok/admin" "github.com/openziti/zrok/rest_client_zrok/environment" "github.com/openziti/zrok/rest_client_zrok/metadata" - "github.com/openziti/zrok/rest_client_zrok/metrics" "github.com/openziti/zrok/rest_client_zrok/share" ) @@ -64,7 +63,6 @@ func New(transport runtime.ClientTransport, formats strfmt.Registry) *Zrok { cli.Admin = admin.New(transport, formats) cli.Environment = environment.New(transport, formats) cli.Metadata = metadata.New(transport, formats) - cli.Metrics = metrics.New(transport, formats) cli.Share = share.New(transport, formats) return cli } @@ -118,8 +116,6 @@ type Zrok struct { Metadata metadata.ClientService - Metrics metrics.ClientService - Share share.ClientService Transport runtime.ClientTransport @@ -132,6 +128,5 @@ func (c *Zrok) SetTransport(transport runtime.ClientTransport) { c.Admin.SetTransport(transport) c.Environment.SetTransport(transport) c.Metadata.SetTransport(transport) - c.Metrics.SetTransport(transport) c.Share.SetTransport(transport) } diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 46e97057..2d8d53fc 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -528,12 +528,12 @@ func init() { } ], "tags": [ - "metrics" + "metadata" ], "operationId": "getAccountMetrics", "parameters": [ { - "type": "number", + "type": "string", "name": "duration", "in": "query" } @@ -544,6 +544,12 @@ func init() { "schema": { "$ref": "#/definitions/metrics" } + }, + "400": { + "description": "bad request" + }, + "500": { + "description": "internal server error" } } } @@ -556,7 +562,7 @@ func init() { } ], "tags": [ - "metrics" + "metadata" ], "operationId": "getEnvironmentMetrics", "parameters": [ @@ -567,7 +573,7 @@ func init() { "required": true }, { - "type": "number", + "type": "string", "name": "duration", "in": "query" } @@ -593,7 +599,7 @@ func init() { } ], "tags": [ - "metrics" + "metadata" ], "operationId": "getShareMetrics", "parameters": [ @@ -604,7 +610,7 @@ func init() { "required": true }, { - "type": "number", + "type": "string", "name": "duration", "in": "query" } @@ -1938,12 +1944,12 @@ func init() { } ], "tags": [ - "metrics" + "metadata" ], "operationId": "getAccountMetrics", "parameters": [ { - "type": "number", + "type": "string", "name": "duration", "in": "query" } @@ -1954,6 +1960,12 @@ func init() { "schema": { "$ref": "#/definitions/metrics" } + }, + "400": { + "description": "bad request" + }, + "500": { + "description": "internal server error" } } } @@ -1966,7 +1978,7 @@ func init() { } ], "tags": [ - "metrics" + "metadata" ], "operationId": "getEnvironmentMetrics", "parameters": [ @@ -1977,7 +1989,7 @@ func init() { "required": true }, { - "type": "number", + "type": "string", "name": "duration", "in": "query" } @@ -2003,7 +2015,7 @@ func init() { } ], "tags": [ - "metrics" + "metadata" ], "operationId": "getShareMetrics", "parameters": [ @@ -2014,7 +2026,7 @@ func init() { "required": true }, { - "type": "number", + "type": "string", "name": "duration", "in": "query" } diff --git a/rest_server_zrok/operations/metadata/get_account_metrics.go b/rest_server_zrok/operations/metadata/get_account_metrics.go new file mode 100644 index 00000000..bc438177 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_account_metrics.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetAccountMetricsHandlerFunc turns a function with the right signature into a get account metrics handler +type GetAccountMetricsHandlerFunc func(GetAccountMetricsParams, *rest_model_zrok.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetAccountMetricsHandlerFunc) Handle(params GetAccountMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + return fn(params, principal) +} + +// GetAccountMetricsHandler interface for that can handle valid get account metrics params +type GetAccountMetricsHandler interface { + Handle(GetAccountMetricsParams, *rest_model_zrok.Principal) middleware.Responder +} + +// NewGetAccountMetrics creates a new http.Handler for the get account metrics operation +func NewGetAccountMetrics(ctx *middleware.Context, handler GetAccountMetricsHandler) *GetAccountMetrics { + return &GetAccountMetrics{Context: ctx, Handler: handler} +} + +/* + GetAccountMetrics swagger:route GET /metrics/account metadata getAccountMetrics + +GetAccountMetrics get account metrics API +*/ +type GetAccountMetrics struct { + Context *middleware.Context + Handler GetAccountMetricsHandler +} + +func (o *GetAccountMetrics) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetAccountMetricsParams() + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + *r = *aCtx + } + var principal *rest_model_zrok.Principal + if uprinc != nil { + principal = uprinc.(*rest_model_zrok.Principal) // this is really a rest_model_zrok.Principal, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/rest_server_zrok/operations/metadata/get_account_metrics_parameters.go b/rest_server_zrok/operations/metadata/get_account_metrics_parameters.go new file mode 100644 index 00000000..705f0397 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_account_metrics_parameters.go @@ -0,0 +1,77 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" +) + +// NewGetAccountMetricsParams creates a new GetAccountMetricsParams object +// +// There are no default values defined in the spec. +func NewGetAccountMetricsParams() GetAccountMetricsParams { + + return GetAccountMetricsParams{} +} + +// GetAccountMetricsParams contains all the bound params for the get account metrics operation +// typically these are obtained from a http.Request +// +// swagger:parameters getAccountMetrics +type GetAccountMetricsParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + In: query + */ + Duration *string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetAccountMetricsParams() beforehand. +func (o *GetAccountMetricsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + qs := runtime.Values(r.URL.Query()) + + qDuration, qhkDuration, _ := qs.GetOK("duration") + if err := o.bindDuration(qDuration, qhkDuration, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindDuration binds and validates parameter Duration from query. +func (o *GetAccountMetricsParams) bindDuration(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + return nil + } + o.Duration = &raw + + return nil +} diff --git a/rest_server_zrok/operations/metadata/get_account_metrics_responses.go b/rest_server_zrok/operations/metadata/get_account_metrics_responses.go new file mode 100644 index 00000000..9fb4bd4f --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_account_metrics_responses.go @@ -0,0 +1,109 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetAccountMetricsOKCode is the HTTP code returned for type GetAccountMetricsOK +const GetAccountMetricsOKCode int = 200 + +/* +GetAccountMetricsOK account metrics + +swagger:response getAccountMetricsOK +*/ +type GetAccountMetricsOK struct { + + /* + In: Body + */ + Payload *rest_model_zrok.Metrics `json:"body,omitempty"` +} + +// NewGetAccountMetricsOK creates GetAccountMetricsOK with default headers values +func NewGetAccountMetricsOK() *GetAccountMetricsOK { + + return &GetAccountMetricsOK{} +} + +// WithPayload adds the payload to the get account metrics o k response +func (o *GetAccountMetricsOK) WithPayload(payload *rest_model_zrok.Metrics) *GetAccountMetricsOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get account metrics o k response +func (o *GetAccountMetricsOK) SetPayload(payload *rest_model_zrok.Metrics) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetAccountMetricsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetAccountMetricsBadRequestCode is the HTTP code returned for type GetAccountMetricsBadRequest +const GetAccountMetricsBadRequestCode int = 400 + +/* +GetAccountMetricsBadRequest bad request + +swagger:response getAccountMetricsBadRequest +*/ +type GetAccountMetricsBadRequest struct { +} + +// NewGetAccountMetricsBadRequest creates GetAccountMetricsBadRequest with default headers values +func NewGetAccountMetricsBadRequest() *GetAccountMetricsBadRequest { + + return &GetAccountMetricsBadRequest{} +} + +// WriteResponse to the client +func (o *GetAccountMetricsBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(400) +} + +// GetAccountMetricsInternalServerErrorCode is the HTTP code returned for type GetAccountMetricsInternalServerError +const GetAccountMetricsInternalServerErrorCode int = 500 + +/* +GetAccountMetricsInternalServerError internal server error + +swagger:response getAccountMetricsInternalServerError +*/ +type GetAccountMetricsInternalServerError struct { +} + +// NewGetAccountMetricsInternalServerError creates GetAccountMetricsInternalServerError with default headers values +func NewGetAccountMetricsInternalServerError() *GetAccountMetricsInternalServerError { + + return &GetAccountMetricsInternalServerError{} +} + +// WriteResponse to the client +func (o *GetAccountMetricsInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(500) +} diff --git a/rest_server_zrok/operations/metadata/get_account_metrics_urlbuilder.go b/rest_server_zrok/operations/metadata/get_account_metrics_urlbuilder.go new file mode 100644 index 00000000..b6065cf7 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_account_metrics_urlbuilder.go @@ -0,0 +1,103 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// GetAccountMetricsURL generates an URL for the get account metrics operation +type GetAccountMetricsURL struct { + Duration *string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetAccountMetricsURL) WithBasePath(bp string) *GetAccountMetricsURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetAccountMetricsURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetAccountMetricsURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/metrics/account" + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + qs := make(url.Values) + + var durationQ string + if o.Duration != nil { + durationQ = *o.Duration + } + if durationQ != "" { + qs.Set("duration", durationQ) + } + + _result.RawQuery = qs.Encode() + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetAccountMetricsURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetAccountMetricsURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetAccountMetricsURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetAccountMetricsURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetAccountMetricsURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetAccountMetricsURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/rest_server_zrok/operations/metadata/get_environment_metrics.go b/rest_server_zrok/operations/metadata/get_environment_metrics.go new file mode 100644 index 00000000..244b28e1 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_environment_metrics.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetEnvironmentMetricsHandlerFunc turns a function with the right signature into a get environment metrics handler +type GetEnvironmentMetricsHandlerFunc func(GetEnvironmentMetricsParams, *rest_model_zrok.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetEnvironmentMetricsHandlerFunc) Handle(params GetEnvironmentMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + return fn(params, principal) +} + +// GetEnvironmentMetricsHandler interface for that can handle valid get environment metrics params +type GetEnvironmentMetricsHandler interface { + Handle(GetEnvironmentMetricsParams, *rest_model_zrok.Principal) middleware.Responder +} + +// NewGetEnvironmentMetrics creates a new http.Handler for the get environment metrics operation +func NewGetEnvironmentMetrics(ctx *middleware.Context, handler GetEnvironmentMetricsHandler) *GetEnvironmentMetrics { + return &GetEnvironmentMetrics{Context: ctx, Handler: handler} +} + +/* + GetEnvironmentMetrics swagger:route GET /metrics/environment/{envId} metadata getEnvironmentMetrics + +GetEnvironmentMetrics get environment metrics API +*/ +type GetEnvironmentMetrics struct { + Context *middleware.Context + Handler GetEnvironmentMetricsHandler +} + +func (o *GetEnvironmentMetrics) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetEnvironmentMetricsParams() + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + *r = *aCtx + } + var principal *rest_model_zrok.Principal + if uprinc != nil { + principal = uprinc.(*rest_model_zrok.Principal) // this is really a rest_model_zrok.Principal, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go b/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go new file mode 100644 index 00000000..6ea94376 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go @@ -0,0 +1,101 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" +) + +// NewGetEnvironmentMetricsParams creates a new GetEnvironmentMetricsParams object +// +// There are no default values defined in the spec. +func NewGetEnvironmentMetricsParams() GetEnvironmentMetricsParams { + + return GetEnvironmentMetricsParams{} +} + +// GetEnvironmentMetricsParams contains all the bound params for the get environment metrics operation +// typically these are obtained from a http.Request +// +// swagger:parameters getEnvironmentMetrics +type GetEnvironmentMetricsParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + In: query + */ + Duration *string + /* + Required: true + In: path + */ + EnvID string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetEnvironmentMetricsParams() beforehand. +func (o *GetEnvironmentMetricsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + qs := runtime.Values(r.URL.Query()) + + qDuration, qhkDuration, _ := qs.GetOK("duration") + if err := o.bindDuration(qDuration, qhkDuration, route.Formats); err != nil { + res = append(res, err) + } + + rEnvID, rhkEnvID, _ := route.Params.GetOK("envId") + if err := o.bindEnvID(rEnvID, rhkEnvID, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindDuration binds and validates parameter Duration from query. +func (o *GetEnvironmentMetricsParams) bindDuration(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + return nil + } + o.Duration = &raw + + return nil +} + +// bindEnvID binds and validates parameter EnvID from path. +func (o *GetEnvironmentMetricsParams) bindEnvID(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.EnvID = raw + + return nil +} diff --git a/rest_server_zrok/operations/metadata/get_environment_metrics_responses.go b/rest_server_zrok/operations/metadata/get_environment_metrics_responses.go new file mode 100644 index 00000000..290fc938 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_environment_metrics_responses.go @@ -0,0 +1,84 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetEnvironmentMetricsOKCode is the HTTP code returned for type GetEnvironmentMetricsOK +const GetEnvironmentMetricsOKCode int = 200 + +/* +GetEnvironmentMetricsOK environment metrics + +swagger:response getEnvironmentMetricsOK +*/ +type GetEnvironmentMetricsOK struct { + + /* + In: Body + */ + Payload *rest_model_zrok.Metrics `json:"body,omitempty"` +} + +// NewGetEnvironmentMetricsOK creates GetEnvironmentMetricsOK with default headers values +func NewGetEnvironmentMetricsOK() *GetEnvironmentMetricsOK { + + return &GetEnvironmentMetricsOK{} +} + +// WithPayload adds the payload to the get environment metrics o k response +func (o *GetEnvironmentMetricsOK) WithPayload(payload *rest_model_zrok.Metrics) *GetEnvironmentMetricsOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get environment metrics o k response +func (o *GetEnvironmentMetricsOK) SetPayload(payload *rest_model_zrok.Metrics) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetEnvironmentMetricsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetEnvironmentMetricsUnauthorizedCode is the HTTP code returned for type GetEnvironmentMetricsUnauthorized +const GetEnvironmentMetricsUnauthorizedCode int = 401 + +/* +GetEnvironmentMetricsUnauthorized unauthorized + +swagger:response getEnvironmentMetricsUnauthorized +*/ +type GetEnvironmentMetricsUnauthorized struct { +} + +// NewGetEnvironmentMetricsUnauthorized creates GetEnvironmentMetricsUnauthorized with default headers values +func NewGetEnvironmentMetricsUnauthorized() *GetEnvironmentMetricsUnauthorized { + + return &GetEnvironmentMetricsUnauthorized{} +} + +// WriteResponse to the client +func (o *GetEnvironmentMetricsUnauthorized) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(401) +} diff --git a/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go b/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go new file mode 100644 index 00000000..a7787fa2 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go @@ -0,0 +1,113 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// GetEnvironmentMetricsURL generates an URL for the get environment metrics operation +type GetEnvironmentMetricsURL struct { + EnvID string + + Duration *string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetEnvironmentMetricsURL) WithBasePath(bp string) *GetEnvironmentMetricsURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetEnvironmentMetricsURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetEnvironmentMetricsURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/metrics/environment/{envId}" + + envID := o.EnvID + if envID != "" { + _path = strings.Replace(_path, "{envId}", envID, -1) + } else { + return nil, errors.New("envId is required on GetEnvironmentMetricsURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + qs := make(url.Values) + + var durationQ string + if o.Duration != nil { + durationQ = *o.Duration + } + if durationQ != "" { + qs.Set("duration", durationQ) + } + + _result.RawQuery = qs.Encode() + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetEnvironmentMetricsURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetEnvironmentMetricsURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetEnvironmentMetricsURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetEnvironmentMetricsURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetEnvironmentMetricsURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetEnvironmentMetricsURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/rest_server_zrok/operations/metadata/get_share_metrics.go b/rest_server_zrok/operations/metadata/get_share_metrics.go new file mode 100644 index 00000000..db0b2055 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_share_metrics.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetShareMetricsHandlerFunc turns a function with the right signature into a get share metrics handler +type GetShareMetricsHandlerFunc func(GetShareMetricsParams, *rest_model_zrok.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetShareMetricsHandlerFunc) Handle(params GetShareMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + return fn(params, principal) +} + +// GetShareMetricsHandler interface for that can handle valid get share metrics params +type GetShareMetricsHandler interface { + Handle(GetShareMetricsParams, *rest_model_zrok.Principal) middleware.Responder +} + +// NewGetShareMetrics creates a new http.Handler for the get share metrics operation +func NewGetShareMetrics(ctx *middleware.Context, handler GetShareMetricsHandler) *GetShareMetrics { + return &GetShareMetrics{Context: ctx, Handler: handler} +} + +/* + GetShareMetrics swagger:route GET /metrics/share/{shrToken} metadata getShareMetrics + +GetShareMetrics get share metrics API +*/ +type GetShareMetrics struct { + Context *middleware.Context + Handler GetShareMetricsHandler +} + +func (o *GetShareMetrics) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetShareMetricsParams() + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + *r = *aCtx + } + var principal *rest_model_zrok.Principal + if uprinc != nil { + principal = uprinc.(*rest_model_zrok.Principal) // this is really a rest_model_zrok.Principal, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/rest_server_zrok/operations/metadata/get_share_metrics_parameters.go b/rest_server_zrok/operations/metadata/get_share_metrics_parameters.go new file mode 100644 index 00000000..f945c949 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_share_metrics_parameters.go @@ -0,0 +1,101 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" +) + +// NewGetShareMetricsParams creates a new GetShareMetricsParams object +// +// There are no default values defined in the spec. +func NewGetShareMetricsParams() GetShareMetricsParams { + + return GetShareMetricsParams{} +} + +// GetShareMetricsParams contains all the bound params for the get share metrics operation +// typically these are obtained from a http.Request +// +// swagger:parameters getShareMetrics +type GetShareMetricsParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + In: query + */ + Duration *string + /* + Required: true + In: path + */ + ShrToken string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetShareMetricsParams() beforehand. +func (o *GetShareMetricsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + qs := runtime.Values(r.URL.Query()) + + qDuration, qhkDuration, _ := qs.GetOK("duration") + if err := o.bindDuration(qDuration, qhkDuration, route.Formats); err != nil { + res = append(res, err) + } + + rShrToken, rhkShrToken, _ := route.Params.GetOK("shrToken") + if err := o.bindShrToken(rShrToken, rhkShrToken, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindDuration binds and validates parameter Duration from query. +func (o *GetShareMetricsParams) bindDuration(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + return nil + } + o.Duration = &raw + + return nil +} + +// bindShrToken binds and validates parameter ShrToken from path. +func (o *GetShareMetricsParams) bindShrToken(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.ShrToken = raw + + return nil +} diff --git a/rest_server_zrok/operations/metadata/get_share_metrics_responses.go b/rest_server_zrok/operations/metadata/get_share_metrics_responses.go new file mode 100644 index 00000000..9f08dc90 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_share_metrics_responses.go @@ -0,0 +1,84 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetShareMetricsOKCode is the HTTP code returned for type GetShareMetricsOK +const GetShareMetricsOKCode int = 200 + +/* +GetShareMetricsOK share metrics + +swagger:response getShareMetricsOK +*/ +type GetShareMetricsOK struct { + + /* + In: Body + */ + Payload *rest_model_zrok.Metrics `json:"body,omitempty"` +} + +// NewGetShareMetricsOK creates GetShareMetricsOK with default headers values +func NewGetShareMetricsOK() *GetShareMetricsOK { + + return &GetShareMetricsOK{} +} + +// WithPayload adds the payload to the get share metrics o k response +func (o *GetShareMetricsOK) WithPayload(payload *rest_model_zrok.Metrics) *GetShareMetricsOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get share metrics o k response +func (o *GetShareMetricsOK) SetPayload(payload *rest_model_zrok.Metrics) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetShareMetricsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetShareMetricsUnauthorizedCode is the HTTP code returned for type GetShareMetricsUnauthorized +const GetShareMetricsUnauthorizedCode int = 401 + +/* +GetShareMetricsUnauthorized unauthorized + +swagger:response getShareMetricsUnauthorized +*/ +type GetShareMetricsUnauthorized struct { +} + +// NewGetShareMetricsUnauthorized creates GetShareMetricsUnauthorized with default headers values +func NewGetShareMetricsUnauthorized() *GetShareMetricsUnauthorized { + + return &GetShareMetricsUnauthorized{} +} + +// WriteResponse to the client +func (o *GetShareMetricsUnauthorized) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(401) +} diff --git a/rest_server_zrok/operations/metadata/get_share_metrics_urlbuilder.go b/rest_server_zrok/operations/metadata/get_share_metrics_urlbuilder.go new file mode 100644 index 00000000..74258138 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_share_metrics_urlbuilder.go @@ -0,0 +1,113 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// GetShareMetricsURL generates an URL for the get share metrics operation +type GetShareMetricsURL struct { + ShrToken string + + Duration *string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetShareMetricsURL) WithBasePath(bp string) *GetShareMetricsURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetShareMetricsURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetShareMetricsURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/metrics/share/{shrToken}" + + shrToken := o.ShrToken + if shrToken != "" { + _path = strings.Replace(_path, "{shrToken}", shrToken, -1) + } else { + return nil, errors.New("shrToken is required on GetShareMetricsURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + qs := make(url.Values) + + var durationQ string + if o.Duration != nil { + durationQ = *o.Duration + } + if durationQ != "" { + qs.Set("duration", durationQ) + } + + _result.RawQuery = qs.Encode() + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetShareMetricsURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetShareMetricsURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetShareMetricsURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetShareMetricsURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetShareMetricsURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetShareMetricsURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/rest_server_zrok/operations/zrok_api.go b/rest_server_zrok/operations/zrok_api.go index 19a12af8..61b9bad1 100644 --- a/rest_server_zrok/operations/zrok_api.go +++ b/rest_server_zrok/operations/zrok_api.go @@ -24,7 +24,6 @@ import ( "github.com/openziti/zrok/rest_server_zrok/operations/admin" "github.com/openziti/zrok/rest_server_zrok/operations/environment" "github.com/openziti/zrok/rest_server_zrok/operations/metadata" - "github.com/openziti/zrok/rest_server_zrok/operations/metrics" "github.com/openziti/zrok/rest_server_zrok/operations/share" ) @@ -71,20 +70,20 @@ func NewZrokAPI(spec *loads.Document) *ZrokAPI { EnvironmentEnableHandler: environment.EnableHandlerFunc(func(params environment.EnableParams, principal *rest_model_zrok.Principal) middleware.Responder { return middleware.NotImplemented("operation environment.Enable has not yet been implemented") }), - MetricsGetAccountMetricsHandler: metrics.GetAccountMetricsHandlerFunc(func(params metrics.GetAccountMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { - return middleware.NotImplemented("operation metrics.GetAccountMetrics has not yet been implemented") + MetadataGetAccountMetricsHandler: metadata.GetAccountMetricsHandlerFunc(func(params metadata.GetAccountMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + return middleware.NotImplemented("operation metadata.GetAccountMetrics has not yet been implemented") }), MetadataGetEnvironmentDetailHandler: metadata.GetEnvironmentDetailHandlerFunc(func(params metadata.GetEnvironmentDetailParams, principal *rest_model_zrok.Principal) middleware.Responder { return middleware.NotImplemented("operation metadata.GetEnvironmentDetail has not yet been implemented") }), - MetricsGetEnvironmentMetricsHandler: metrics.GetEnvironmentMetricsHandlerFunc(func(params metrics.GetEnvironmentMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { - return middleware.NotImplemented("operation metrics.GetEnvironmentMetrics has not yet been implemented") + MetadataGetEnvironmentMetricsHandler: metadata.GetEnvironmentMetricsHandlerFunc(func(params metadata.GetEnvironmentMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + return middleware.NotImplemented("operation metadata.GetEnvironmentMetrics has not yet been implemented") }), MetadataGetShareDetailHandler: metadata.GetShareDetailHandlerFunc(func(params metadata.GetShareDetailParams, principal *rest_model_zrok.Principal) middleware.Responder { return middleware.NotImplemented("operation metadata.GetShareDetail has not yet been implemented") }), - MetricsGetShareMetricsHandler: metrics.GetShareMetricsHandlerFunc(func(params metrics.GetShareMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { - return middleware.NotImplemented("operation metrics.GetShareMetrics has not yet been implemented") + MetadataGetShareMetricsHandler: metadata.GetShareMetricsHandlerFunc(func(params metadata.GetShareMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + return middleware.NotImplemented("operation metadata.GetShareMetrics has not yet been implemented") }), AccountInviteHandler: account.InviteHandlerFunc(func(params account.InviteParams) middleware.Responder { return middleware.NotImplemented("operation account.Invite has not yet been implemented") @@ -195,16 +194,16 @@ type ZrokAPI struct { EnvironmentDisableHandler environment.DisableHandler // EnvironmentEnableHandler sets the operation handler for the enable operation EnvironmentEnableHandler environment.EnableHandler - // MetricsGetAccountMetricsHandler sets the operation handler for the get account metrics operation - MetricsGetAccountMetricsHandler metrics.GetAccountMetricsHandler + // MetadataGetAccountMetricsHandler sets the operation handler for the get account metrics operation + MetadataGetAccountMetricsHandler metadata.GetAccountMetricsHandler // MetadataGetEnvironmentDetailHandler sets the operation handler for the get environment detail operation MetadataGetEnvironmentDetailHandler metadata.GetEnvironmentDetailHandler - // MetricsGetEnvironmentMetricsHandler sets the operation handler for the get environment metrics operation - MetricsGetEnvironmentMetricsHandler metrics.GetEnvironmentMetricsHandler + // MetadataGetEnvironmentMetricsHandler sets the operation handler for the get environment metrics operation + MetadataGetEnvironmentMetricsHandler metadata.GetEnvironmentMetricsHandler // MetadataGetShareDetailHandler sets the operation handler for the get share detail operation MetadataGetShareDetailHandler metadata.GetShareDetailHandler - // MetricsGetShareMetricsHandler sets the operation handler for the get share metrics operation - MetricsGetShareMetricsHandler metrics.GetShareMetricsHandler + // MetadataGetShareMetricsHandler sets the operation handler for the get share metrics operation + MetadataGetShareMetricsHandler metadata.GetShareMetricsHandler // AccountInviteHandler sets the operation handler for the invite operation AccountInviteHandler account.InviteHandler // AdminInviteTokenGenerateHandler sets the operation handler for the invite token generate operation @@ -337,20 +336,20 @@ func (o *ZrokAPI) Validate() error { if o.EnvironmentEnableHandler == nil { unregistered = append(unregistered, "environment.EnableHandler") } - if o.MetricsGetAccountMetricsHandler == nil { - unregistered = append(unregistered, "metrics.GetAccountMetricsHandler") + if o.MetadataGetAccountMetricsHandler == nil { + unregistered = append(unregistered, "metadata.GetAccountMetricsHandler") } if o.MetadataGetEnvironmentDetailHandler == nil { unregistered = append(unregistered, "metadata.GetEnvironmentDetailHandler") } - if o.MetricsGetEnvironmentMetricsHandler == nil { - unregistered = append(unregistered, "metrics.GetEnvironmentMetricsHandler") + if o.MetadataGetEnvironmentMetricsHandler == nil { + unregistered = append(unregistered, "metadata.GetEnvironmentMetricsHandler") } if o.MetadataGetShareDetailHandler == nil { unregistered = append(unregistered, "metadata.GetShareDetailHandler") } - if o.MetricsGetShareMetricsHandler == nil { - unregistered = append(unregistered, "metrics.GetShareMetricsHandler") + if o.MetadataGetShareMetricsHandler == nil { + unregistered = append(unregistered, "metadata.GetShareMetricsHandler") } if o.AccountInviteHandler == nil { unregistered = append(unregistered, "account.InviteHandler") @@ -527,7 +526,7 @@ func (o *ZrokAPI) initHandlerCache() { if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } - o.handlers["GET"]["/metrics/account"] = metrics.NewGetAccountMetrics(o.context, o.MetricsGetAccountMetricsHandler) + o.handlers["GET"]["/metrics/account"] = metadata.NewGetAccountMetrics(o.context, o.MetadataGetAccountMetricsHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } @@ -535,7 +534,7 @@ func (o *ZrokAPI) initHandlerCache() { if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } - o.handlers["GET"]["/metrics/environment/{envId}"] = metrics.NewGetEnvironmentMetrics(o.context, o.MetricsGetEnvironmentMetricsHandler) + o.handlers["GET"]["/metrics/environment/{envId}"] = metadata.NewGetEnvironmentMetrics(o.context, o.MetadataGetEnvironmentMetricsHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } @@ -543,7 +542,7 @@ func (o *ZrokAPI) initHandlerCache() { if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } - o.handlers["GET"]["/metrics/share/{shrToken}"] = metrics.NewGetShareMetrics(o.context, o.MetricsGetShareMetricsHandler) + o.handlers["GET"]["/metrics/share/{shrToken}"] = metadata.NewGetShareMetrics(o.context, o.MetadataGetShareMetricsHandler) if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) } diff --git a/specs/zrok.yml b/specs/zrok.yml index 7c9f97fc..a73ab913 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -394,41 +394,31 @@ paths: schema: $ref: "#/definitions/errorMessage" - /version: - get: - tags: - - metadata - operationId: version - responses: - 200: - description: current server version - schema: - $ref: "#/definitions/version" - - # - # metrics - # /metrics/account: get: tags: - - metrics + - metadata security: - key: [] operationId: getAccountMetrics parameters: - name: duration in: query - type: number + type: string responses: 200: description: account metrics schema: $ref: "#/definitions/metrics" + 400: + description: bad request + 500: + description: internal server error /metrics/environment/{envId}: get: tags: - - metrics + - metadata security: - key: [] operationId: getEnvironmentMetrics @@ -439,7 +429,7 @@ paths: required: true - name: duration in: query - type: number + type: string responses: 200: description: environment metrics @@ -451,7 +441,7 @@ paths: /metrics/share/{shrToken}: get: tags: - - metrics + - metadata security: - key: [] operationId: getShareMetrics @@ -462,7 +452,7 @@ paths: required: true - name: duration in: query - type: number + type: string responses: 200: description: share metrics @@ -470,6 +460,18 @@ paths: $ref: "#/definitions/metrics" 401: description: unauthorized + + /version: + get: + tags: + - metadata + operationId: version + responses: + 200: + description: current server version + schema: + $ref: "#/definitions/version" + # # share # diff --git a/ui/src/api/metadata.js b/ui/src/api/metadata.js index 16b2d86e..7b8af17a 100644 --- a/ui/src/api/metadata.js +++ b/ui/src/api/metadata.js @@ -40,6 +40,59 @@ export function overview() { return gateway.request(overviewOperation) } +/** + * @param {object} options Optional options + * @param {string} [options.duration] + * @return {Promise<module:types.metrics>} account metrics + */ +export function getAccountMetrics(options) { + if (!options) options = {} + const parameters = { + query: { + duration: options.duration + } + } + return gateway.request(getAccountMetricsOperation, parameters) +} + +/** + * @param {string} envId + * @param {object} options Optional options + * @param {string} [options.duration] + * @return {Promise<module:types.metrics>} environment metrics + */ +export function getEnvironmentMetrics(envId, options) { + if (!options) options = {} + const parameters = { + path: { + envId + }, + query: { + duration: options.duration + } + } + return gateway.request(getEnvironmentMetricsOperation, parameters) +} + +/** + * @param {string} shrToken + * @param {object} options Optional options + * @param {string} [options.duration] + * @return {Promise<module:types.metrics>} share metrics + */ +export function getShareMetrics(shrToken, options) { + if (!options) options = {} + const parameters = { + path: { + shrToken + }, + query: { + duration: options.duration + } + } + return gateway.request(getShareMetricsOperation, parameters) +} + /** */ export function version() { @@ -81,6 +134,36 @@ const overviewOperation = { ] } +const getAccountMetricsOperation = { + path: '/metrics/account', + method: 'get', + security: [ + { + id: 'key' + } + ] +} + +const getEnvironmentMetricsOperation = { + path: '/metrics/environment/{envId}', + method: 'get', + security: [ + { + id: 'key' + } + ] +} + +const getShareMetricsOperation = { + path: '/metrics/share/{shrToken}', + method: 'get', + security: [ + { + id: 'key' + } + ] +} + const versionOperation = { path: '/version', method: 'get' diff --git a/ui/src/api/metrics.js b/ui/src/api/metrics.js deleted file mode 100644 index cc0063de..00000000 --- a/ui/src/api/metrics.js +++ /dev/null @@ -1,86 +0,0 @@ -/** @module metrics */ -// Auto-generated, edits will be overwritten -import * as gateway from './gateway' - -/** - * @param {object} options Optional options - * @param {number} [options.duration] - * @return {Promise<module:types.metrics>} account metrics - */ -export function getAccountMetrics(options) { - if (!options) options = {} - const parameters = { - query: { - duration: options.duration - } - } - return gateway.request(getAccountMetricsOperation, parameters) -} - -/** - * @param {string} envId - * @param {object} options Optional options - * @param {number} [options.duration] - * @return {Promise<module:types.metrics>} environment metrics - */ -export function getEnvironmentMetrics(envId, options) { - if (!options) options = {} - const parameters = { - path: { - envId - }, - query: { - duration: options.duration - } - } - return gateway.request(getEnvironmentMetricsOperation, parameters) -} - -/** - * @param {string} shrToken - * @param {object} options Optional options - * @param {number} [options.duration] - * @return {Promise<module:types.metrics>} share metrics - */ -export function getShareMetrics(shrToken, options) { - if (!options) options = {} - const parameters = { - path: { - shrToken - }, - query: { - duration: options.duration - } - } - return gateway.request(getShareMetricsOperation, parameters) -} - -const getAccountMetricsOperation = { - path: '/metrics/account', - method: 'get', - security: [ - { - id: 'key' - } - ] -} - -const getEnvironmentMetricsOperation = { - path: '/metrics/environment/{envId}', - method: 'get', - security: [ - { - id: 'key' - } - ] -} - -const getShareMetricsOperation = { - path: '/metrics/share/{shrToken}', - method: 'get', - security: [ - { - id: 'key' - } - ] -} From 9f29bb59c7340d5e43044d0a402f0a1ca9e8e3f6 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Tue, 9 May 2023 16:22:30 -0400 Subject: [PATCH 10/49] alternate metrics model with sample objects (#319) --- controller/metrics.go | 21 +++-- rest_model_zrok/metrics.go | 75 ++++++++++++++-- rest_model_zrok/metrics_sample.go | 56 ++++++++++++ rest_server_zrok/embedded_spec.go | 40 ++++++--- specs/zrok.yml | 18 ++-- ui/src/api/types.js | 12 ++- .../console/detail/account/AccountDetail.js | 89 ++++++++++++++++++- 7 files changed, 277 insertions(+), 34 deletions(-) create mode 100644 rest_model_zrok/metrics_sample.go diff --git a/controller/metrics.go b/controller/metrics.go index a5091b0f..0197dbce 100644 --- a/controller/metrics.go +++ b/controller/metrics.go @@ -42,7 +42,7 @@ func (h *getAccountMetricsHandler) Handle(params metadata.GetAccountMetricsParam slice := duration / 200 query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + - fmt.Sprintf("|> range(start -%v)\n", duration) + + fmt.Sprintf("|> range(start: -%v)\n", duration) + "|> filter(fn: (r) => r[\"_measurement\"] == \"xfer\")\n" + "|> filter(fn: (r) => r[\"_field\"] == \"rx\" or r[\"_field\"] == \"tx\")\n" + "|> filter(fn: (r) => r[\"namespace\"] == \"backend\")\n" + @@ -50,35 +50,42 @@ func (h *getAccountMetricsHandler) Handle(params metadata.GetAccountMetricsParam "|> drop(columns: [\"share\", \"envId\"])\n" + fmt.Sprintf("|> aggregateWindow(every: %v, fn: sum, createEmpty: true)", slice) - rx, tx, err := runFluxForRxTxArray(query, h.queryApi) + rx, tx, timestamps, err := runFluxForRxTxArray(query, h.queryApi) if err != nil { logrus.Errorf("error running account metrics query for '%v': %v", principal.Email, err) return metadata.NewGetAccountMetricsInternalServerError() } response := &rest_model_zrok.Metrics{ + Scope: "account", ID: fmt.Sprintf("%d", principal.ID), Period: duration.Seconds(), - Rx: rx, - Tx: tx, + } + for i := 0; i < len(rx) && i < len(tx) && i < len(timestamps); i++ { + response.Samples = append(response.Samples, &rest_model_zrok.MetricsSample{ + Rx: rx[i], + Tx: tx[i], + Timestamp: timestamps[i], + }) } return metadata.NewGetAccountMetricsOK().WithPayload(response) } -func runFluxForRxTxArray(query string, queryApi api.QueryAPI) (rx, tx []float64, err error) { +func runFluxForRxTxArray(query string, queryApi api.QueryAPI) (rx, tx, timestamps []float64, err error) { result, err := queryApi.Query(context.Background(), query) if err != nil { - return nil, nil, err + return nil, nil, nil, err } for result.Next() { if v, ok := result.Record().Value().(int64); ok { switch result.Record().Field() { case "rx": rx = append(rx, float64(v)) + timestamps = append(timestamps, float64(result.Record().Time().UnixMilli())) case "tx": tx = append(tx, float64(v)) } } } - return rx, tx, nil + return rx, tx, timestamps, nil } diff --git a/rest_model_zrok/metrics.go b/rest_model_zrok/metrics.go index dfb9bf08..1aa24172 100644 --- a/rest_model_zrok/metrics.go +++ b/rest_model_zrok/metrics.go @@ -7,7 +7,9 @@ package rest_model_zrok import ( "context" + "strconv" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" ) @@ -23,23 +25,84 @@ type Metrics struct { // period Period float64 `json:"period,omitempty"` - // rx - Rx []float64 `json:"rx"` + // samples + Samples []*MetricsSample `json:"samples"` // scope Scope string `json:"scope,omitempty"` - - // tx - Tx []float64 `json:"tx"` } // Validate validates this metrics func (m *Metrics) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateSamples(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } return nil } -// ContextValidate validates this metrics based on context it is used +func (m *Metrics) validateSamples(formats strfmt.Registry) error { + if swag.IsZero(m.Samples) { // not required + return nil + } + + for i := 0; i < len(m.Samples); i++ { + if swag.IsZero(m.Samples[i]) { // not required + continue + } + + if m.Samples[i] != nil { + if err := m.Samples[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("samples" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("samples" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this metrics based on the context it is used func (m *Metrics) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateSamples(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Metrics) contextValidateSamples(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Samples); i++ { + + if m.Samples[i] != nil { + if err := m.Samples[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("samples" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("samples" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + return nil } diff --git a/rest_model_zrok/metrics_sample.go b/rest_model_zrok/metrics_sample.go new file mode 100644 index 00000000..84869de4 --- /dev/null +++ b/rest_model_zrok/metrics_sample.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package rest_model_zrok + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// MetricsSample metrics sample +// +// swagger:model metricsSample +type MetricsSample struct { + + // rx + Rx float64 `json:"rx,omitempty"` + + // timestamp + Timestamp float64 `json:"timestamp,omitempty"` + + // tx + Tx float64 `json:"tx,omitempty"` +} + +// Validate validates this metrics sample +func (m *MetricsSample) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this metrics sample based on context it is used +func (m *MetricsSample) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *MetricsSample) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *MetricsSample) UnmarshalBinary(b []byte) error { + var res MetricsSample + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 2d8d53fc..d268d47c 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -1147,20 +1147,28 @@ func init() { "period": { "type": "number" }, - "rx": { + "samples": { "type": "array", "items": { - "type": "number" + "$ref": "#/definitions/metricsSample" } }, "scope": { "type": "string" + } + } + }, + "metricsSample": { + "type": "object", + "properties": { + "rx": { + "type": "number" + }, + "timestamp": { + "type": "number" }, "tx": { - "type": "array", - "items": { - "type": "number" - } + "type": "number" } } }, @@ -2563,20 +2571,28 @@ func init() { "period": { "type": "number" }, - "rx": { + "samples": { "type": "array", "items": { - "type": "number" + "$ref": "#/definitions/metricsSample" } }, "scope": { "type": "string" + } + } + }, + "metricsSample": { + "type": "object", + "properties": { + "rx": { + "type": "number" + }, + "timestamp": { + "type": "number" }, "tx": { - "type": "array", - "items": { - "type": "number" - } + "type": "number" } } }, diff --git a/specs/zrok.yml b/specs/zrok.yml index a73ab913..fe129fc8 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -743,14 +743,20 @@ definitions: type: string period: type: number + samples: + type: array + items: + $ref: "#/definitions/metricsSample" + + metricsSample: + type: object + properties: rx: - type: array - items: - type: number + type: number tx: - type: array - items: - type: number + type: number + timestamp: + type: number principal: type: object diff --git a/ui/src/api/types.js b/ui/src/api/types.js index 6f5a6317..01c83b83 100644 --- a/ui/src/api/types.js +++ b/ui/src/api/types.js @@ -130,8 +130,16 @@ * @property {string} scope * @property {string} id * @property {number} period - * @property {number[]} rx - * @property {number[]} tx + * @property {module:types.metricsSample[]} samples + */ + +/** + * @typedef metricsSample + * @memberof module:types + * + * @property {number} rx + * @property {number} tx + * @property {number} timestamp */ /** diff --git a/ui/src/console/detail/account/AccountDetail.js b/ui/src/console/detail/account/AccountDetail.js index 679b51aa..bd39617d 100644 --- a/ui/src/console/detail/account/AccountDetail.js +++ b/ui/src/console/detail/account/AccountDetail.js @@ -1,8 +1,11 @@ import {mdiAccountBox} from "@mdi/js"; import Icon from "@mdi/react"; import PropertyTable from "../../PropertyTable"; -import {Tab, Tabs} from "react-bootstrap"; +import {Tab, Tabs, Tooltip} from "react-bootstrap"; import SecretToggle from "../../SecretToggle"; +import React, {useEffect, useState} from "react"; +import * as metadata from "../../../api/metadata"; +import {Area, AreaChart, CartesianGrid, Line, LineChart, ResponsiveContainer, XAxis, YAxis} from "recharts"; const AccountDetail = (props) => { const customProperties = { @@ -16,9 +19,93 @@ const AccountDetail = (props) => { <Tab eventKey={"detail"} title={"Detail"}> <PropertyTable object={props.user} custom={customProperties}/> </Tab> + <Tab eventKey={"metrics"} title={"Metrics"}> + <MetricsTab /> + </Tab> </Tabs> </div> ); } +const MetricsTab = (props) => { + const [metrics, setMetrics] = useState({}); + const [tx, setTx] = useState(0); + const [rx, setRx] = useState(0) + + useEffect(() => { + metadata.getAccountMetrics() + .then(resp => { + setMetrics(resp.data); + }); + }, []); + + useEffect(() => { + let mounted = true; + let interval = setInterval(() => { + metadata.getAccountMetrics() + .then(resp => { + if(mounted) { + setMetrics(resp.data); + } + }); + }, 1000); + return () => { + mounted = false; + clearInterval(interval); + } + }, []); + + useEffect(() => { + let txAccum = 0 + let rxAccum = 0 + if(metrics.samples) { + metrics.samples.forEach(sample => { + txAccum += sample.tx + rxAccum += sample.rx + }) + } + setTx(txAccum); + setRx(rxAccum); + }, [metrics]) + + console.log(metrics); + + return ( + <div> + <div> + <h1>RX: {bytesToSize(rx)}, TX: {bytesToSize(tx)}</h1> + </div> + <ResponsiveContainer width={"100%"} height={300}> + <LineChart data={metrics.samples}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => new Date(v.timestamp)} /> + <YAxis /> + <Line type={"linear"} stroke={"red"} dataKey={"rx"} activeDot={{ r: 8 }}/> + <Line type={"linear"} stroke={"green"} dataKey={"tx"} /> + <Tooltip /> + </LineChart> + </ResponsiveContainer> + </div> + ); +} + +const bytesToSize = (sz) => { + let absSz = sz; + if(absSz < 0) { + absSz *= -1; + } + const unit = 1000 + if(absSz < unit) { + return '' + absSz + ' B'; + } + let div = unit + let exp = 0 + for(let n = absSz / unit; n >= unit; n /= unit) { + div *= unit; + exp++; + } + + return '' + (sz / div).toFixed(2) + "kMGTPE"[exp]; +} + export default AccountDetail; \ No newline at end of file From 02c996b54563873e9903698a2e6c521ae74ad0ab Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Tue, 9 May 2023 16:36:50 -0400 Subject: [PATCH 11/49] environment metrics handler (#319) --- controller/metrics.go | 75 ++++++++++++ .../get_environment_metrics_parameters.go | 9 +- .../get_environment_metrics_responses.go | 114 ++++++++++++++++++ .../metadata/get_share_metrics_responses.go | 114 ++++++++++++++++++ rest_server_zrok/embedded_spec.go | 28 ++++- .../get_environment_metrics_parameters.go | 10 +- .../get_environment_metrics_responses.go | 50 ++++++++ .../get_environment_metrics_urlbuilder.go | 6 +- .../metadata/get_share_metrics_responses.go | 50 ++++++++ specs/zrok.yml | 12 +- ui/src/api/metadata.js | 2 +- 11 files changed, 458 insertions(+), 12 deletions(-) diff --git a/controller/metrics.go b/controller/metrics.go index 0197dbce..355a651a 100644 --- a/controller/metrics.go +++ b/controller/metrics.go @@ -71,6 +71,81 @@ func (h *getAccountMetricsHandler) Handle(params metadata.GetAccountMetricsParam return metadata.NewGetAccountMetricsOK().WithPayload(response) } +type getEnvironmentMetricsHandler struct { + cfg *metrics.InfluxConfig + idb influxdb2.Client + queryApi api.QueryAPI +} + +func newGetEnvironmentMetricsHAndler(cfg *metrics.InfluxConfig) *getEnvironmentMetricsHandler { + idb := influxdb2.NewClient(cfg.Url, cfg.Token) + queryApi := idb.QueryAPI(cfg.Org) + return &getEnvironmentMetricsHandler{ + cfg: cfg, + idb: idb, + queryApi: queryApi, + } +} + +func (h *getEnvironmentMetricsHandler) Handle(params metadata.GetEnvironmentMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + trx, err := str.Begin() + if err != nil { + logrus.Errorf("error starting transaction: %v", err) + return metadata.NewGetEnvironmentMetricsInternalServerError() + } + defer func() { _ = trx.Rollback() }() + env, err := str.GetEnvironment(int(params.EnvID), trx) + if err != nil { + logrus.Errorf("error finding environment '%d': %v", int(params.EnvID), err) + return metadata.NewGetEnvironmentMetricsUnauthorized() + } + if int64(env.Id) != principal.ID { + logrus.Errorf("unauthorized environemnt '%d' for '%v'", int(params.EnvID), principal.Email) + return metadata.NewGetEnvironmentMetricsUnauthorized() + } + + duration := 30 * 24 * time.Hour + if params.Duration != nil { + v, err := time.ParseDuration(*params.Duration) + if err != nil { + logrus.Errorf("bad duration '%v' for '%v': %v", params.Duration, principal.Email, err) + return metadata.NewGetAccountMetricsBadRequest() + } + duration = v + } + slice := duration / 200 + + query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + + fmt.Sprintf("|> range(start: -%v)\n", duration) + + "|> filter(fn: (r) => r[\"_measurement\"] == \"xfer\")\n" + + "|> filter(fn: (r) => r[\"_field\"] == \"rx\" or r[\"_field\"] == \"tx\")\n" + + "|> filter(fn: (r) => r[\"namespace\"] == \"backend\")\n" + + fmt.Sprintf("|> filter(fn: (r) => r[\"envId\"] == \"%d\")\n", int64(env.Id)) + + "|> drop(columns: [\"share\", \"acctId\"])\n" + + fmt.Sprintf("|> aggregateWindow(every: %v, fn: sum, createEmpty: true)", slice) + + rx, tx, timestamps, err := runFluxForRxTxArray(query, h.queryApi) + if err != nil { + logrus.Errorf("error running account metrics query for '%v': %v", principal.Email, err) + return metadata.NewGetAccountMetricsInternalServerError() + } + + response := &rest_model_zrok.Metrics{ + Scope: "account", + ID: fmt.Sprintf("%d", principal.ID), + Period: duration.Seconds(), + } + for i := 0; i < len(rx) && i < len(tx) && i < len(timestamps); i++ { + response.Samples = append(response.Samples, &rest_model_zrok.MetricsSample{ + Rx: rx[i], + Tx: tx[i], + Timestamp: timestamps[i], + }) + } + + return metadata.NewGetEnvironmentMetricsOK().WithPayload(response) +} + func runFluxForRxTxArray(query string, queryApi api.QueryAPI) (rx, tx, timestamps []float64, err error) { result, err := queryApi.Query(context.Background(), query) if err != nil { diff --git a/rest_client_zrok/metadata/get_environment_metrics_parameters.go b/rest_client_zrok/metadata/get_environment_metrics_parameters.go index be7aae16..6bdd8404 100644 --- a/rest_client_zrok/metadata/get_environment_metrics_parameters.go +++ b/rest_client_zrok/metadata/get_environment_metrics_parameters.go @@ -14,6 +14,7 @@ import ( "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) // NewGetEnvironmentMetricsParams creates a new GetEnvironmentMetricsParams object, @@ -65,7 +66,7 @@ type GetEnvironmentMetricsParams struct { Duration *string // EnvID. - EnvID string + EnvID float64 timeout time.Duration Context context.Context @@ -132,13 +133,13 @@ func (o *GetEnvironmentMetricsParams) SetDuration(duration *string) { } // WithEnvID adds the envID to the get environment metrics params -func (o *GetEnvironmentMetricsParams) WithEnvID(envID string) *GetEnvironmentMetricsParams { +func (o *GetEnvironmentMetricsParams) WithEnvID(envID float64) *GetEnvironmentMetricsParams { o.SetEnvID(envID) return o } // SetEnvID adds the envId to the get environment metrics params -func (o *GetEnvironmentMetricsParams) SetEnvID(envID string) { +func (o *GetEnvironmentMetricsParams) SetEnvID(envID float64) { o.EnvID = envID } @@ -168,7 +169,7 @@ func (o *GetEnvironmentMetricsParams) WriteToRequest(r runtime.ClientRequest, re } // path param envId - if err := r.SetPathParam("envId", o.EnvID); err != nil { + if err := r.SetPathParam("envId", swag.FormatFloat64(o.EnvID)); err != nil { return err } diff --git a/rest_client_zrok/metadata/get_environment_metrics_responses.go b/rest_client_zrok/metadata/get_environment_metrics_responses.go index 36e1e2f6..e0fb3eac 100644 --- a/rest_client_zrok/metadata/get_environment_metrics_responses.go +++ b/rest_client_zrok/metadata/get_environment_metrics_responses.go @@ -29,12 +29,24 @@ func (o *GetEnvironmentMetricsReader) ReadResponse(response runtime.ClientRespon return nil, err } return result, nil + case 400: + result := NewGetEnvironmentMetricsBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result case 401: result := NewGetEnvironmentMetricsUnauthorized() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return nil, result + case 500: + result := NewGetEnvironmentMetricsInternalServerError() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result default: return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) } @@ -103,6 +115,57 @@ func (o *GetEnvironmentMetricsOK) readResponse(response runtime.ClientResponse, return nil } +// NewGetEnvironmentMetricsBadRequest creates a GetEnvironmentMetricsBadRequest with default headers values +func NewGetEnvironmentMetricsBadRequest() *GetEnvironmentMetricsBadRequest { + return &GetEnvironmentMetricsBadRequest{} +} + +/* +GetEnvironmentMetricsBadRequest describes a response with status code 400, with default header values. + +bad request +*/ +type GetEnvironmentMetricsBadRequest struct { +} + +// IsSuccess returns true when this get environment metrics bad request response has a 2xx status code +func (o *GetEnvironmentMetricsBadRequest) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get environment metrics bad request response has a 3xx status code +func (o *GetEnvironmentMetricsBadRequest) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get environment metrics bad request response has a 4xx status code +func (o *GetEnvironmentMetricsBadRequest) IsClientError() bool { + return true +} + +// IsServerError returns true when this get environment metrics bad request response has a 5xx status code +func (o *GetEnvironmentMetricsBadRequest) IsServerError() bool { + return false +} + +// IsCode returns true when this get environment metrics bad request response a status code equal to that given +func (o *GetEnvironmentMetricsBadRequest) IsCode(code int) bool { + return code == 400 +} + +func (o *GetEnvironmentMetricsBadRequest) Error() string { + return fmt.Sprintf("[GET /metrics/environment/{envId}][%d] getEnvironmentMetricsBadRequest ", 400) +} + +func (o *GetEnvironmentMetricsBadRequest) String() string { + return fmt.Sprintf("[GET /metrics/environment/{envId}][%d] getEnvironmentMetricsBadRequest ", 400) +} + +func (o *GetEnvironmentMetricsBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + // NewGetEnvironmentMetricsUnauthorized creates a GetEnvironmentMetricsUnauthorized with default headers values func NewGetEnvironmentMetricsUnauthorized() *GetEnvironmentMetricsUnauthorized { return &GetEnvironmentMetricsUnauthorized{} @@ -153,3 +216,54 @@ func (o *GetEnvironmentMetricsUnauthorized) readResponse(response runtime.Client return nil } + +// NewGetEnvironmentMetricsInternalServerError creates a GetEnvironmentMetricsInternalServerError with default headers values +func NewGetEnvironmentMetricsInternalServerError() *GetEnvironmentMetricsInternalServerError { + return &GetEnvironmentMetricsInternalServerError{} +} + +/* +GetEnvironmentMetricsInternalServerError describes a response with status code 500, with default header values. + +internal server error +*/ +type GetEnvironmentMetricsInternalServerError struct { +} + +// IsSuccess returns true when this get environment metrics internal server error response has a 2xx status code +func (o *GetEnvironmentMetricsInternalServerError) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get environment metrics internal server error response has a 3xx status code +func (o *GetEnvironmentMetricsInternalServerError) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get environment metrics internal server error response has a 4xx status code +func (o *GetEnvironmentMetricsInternalServerError) IsClientError() bool { + return false +} + +// IsServerError returns true when this get environment metrics internal server error response has a 5xx status code +func (o *GetEnvironmentMetricsInternalServerError) IsServerError() bool { + return true +} + +// IsCode returns true when this get environment metrics internal server error response a status code equal to that given +func (o *GetEnvironmentMetricsInternalServerError) IsCode(code int) bool { + return code == 500 +} + +func (o *GetEnvironmentMetricsInternalServerError) Error() string { + return fmt.Sprintf("[GET /metrics/environment/{envId}][%d] getEnvironmentMetricsInternalServerError ", 500) +} + +func (o *GetEnvironmentMetricsInternalServerError) String() string { + return fmt.Sprintf("[GET /metrics/environment/{envId}][%d] getEnvironmentMetricsInternalServerError ", 500) +} + +func (o *GetEnvironmentMetricsInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} diff --git a/rest_client_zrok/metadata/get_share_metrics_responses.go b/rest_client_zrok/metadata/get_share_metrics_responses.go index 2ed61ab8..d6d0500d 100644 --- a/rest_client_zrok/metadata/get_share_metrics_responses.go +++ b/rest_client_zrok/metadata/get_share_metrics_responses.go @@ -29,12 +29,24 @@ func (o *GetShareMetricsReader) ReadResponse(response runtime.ClientResponse, co return nil, err } return result, nil + case 400: + result := NewGetShareMetricsBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result case 401: result := NewGetShareMetricsUnauthorized() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return nil, result + case 500: + result := NewGetShareMetricsInternalServerError() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result default: return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) } @@ -103,6 +115,57 @@ func (o *GetShareMetricsOK) readResponse(response runtime.ClientResponse, consum return nil } +// NewGetShareMetricsBadRequest creates a GetShareMetricsBadRequest with default headers values +func NewGetShareMetricsBadRequest() *GetShareMetricsBadRequest { + return &GetShareMetricsBadRequest{} +} + +/* +GetShareMetricsBadRequest describes a response with status code 400, with default header values. + +bad request +*/ +type GetShareMetricsBadRequest struct { +} + +// IsSuccess returns true when this get share metrics bad request response has a 2xx status code +func (o *GetShareMetricsBadRequest) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get share metrics bad request response has a 3xx status code +func (o *GetShareMetricsBadRequest) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get share metrics bad request response has a 4xx status code +func (o *GetShareMetricsBadRequest) IsClientError() bool { + return true +} + +// IsServerError returns true when this get share metrics bad request response has a 5xx status code +func (o *GetShareMetricsBadRequest) IsServerError() bool { + return false +} + +// IsCode returns true when this get share metrics bad request response a status code equal to that given +func (o *GetShareMetricsBadRequest) IsCode(code int) bool { + return code == 400 +} + +func (o *GetShareMetricsBadRequest) Error() string { + return fmt.Sprintf("[GET /metrics/share/{shrToken}][%d] getShareMetricsBadRequest ", 400) +} + +func (o *GetShareMetricsBadRequest) String() string { + return fmt.Sprintf("[GET /metrics/share/{shrToken}][%d] getShareMetricsBadRequest ", 400) +} + +func (o *GetShareMetricsBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + // NewGetShareMetricsUnauthorized creates a GetShareMetricsUnauthorized with default headers values func NewGetShareMetricsUnauthorized() *GetShareMetricsUnauthorized { return &GetShareMetricsUnauthorized{} @@ -153,3 +216,54 @@ func (o *GetShareMetricsUnauthorized) readResponse(response runtime.ClientRespon return nil } + +// NewGetShareMetricsInternalServerError creates a GetShareMetricsInternalServerError with default headers values +func NewGetShareMetricsInternalServerError() *GetShareMetricsInternalServerError { + return &GetShareMetricsInternalServerError{} +} + +/* +GetShareMetricsInternalServerError describes a response with status code 500, with default header values. + +internal server error +*/ +type GetShareMetricsInternalServerError struct { +} + +// IsSuccess returns true when this get share metrics internal server error response has a 2xx status code +func (o *GetShareMetricsInternalServerError) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get share metrics internal server error response has a 3xx status code +func (o *GetShareMetricsInternalServerError) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get share metrics internal server error response has a 4xx status code +func (o *GetShareMetricsInternalServerError) IsClientError() bool { + return false +} + +// IsServerError returns true when this get share metrics internal server error response has a 5xx status code +func (o *GetShareMetricsInternalServerError) IsServerError() bool { + return true +} + +// IsCode returns true when this get share metrics internal server error response a status code equal to that given +func (o *GetShareMetricsInternalServerError) IsCode(code int) bool { + return code == 500 +} + +func (o *GetShareMetricsInternalServerError) Error() string { + return fmt.Sprintf("[GET /metrics/share/{shrToken}][%d] getShareMetricsInternalServerError ", 500) +} + +func (o *GetShareMetricsInternalServerError) String() string { + return fmt.Sprintf("[GET /metrics/share/{shrToken}][%d] getShareMetricsInternalServerError ", 500) +} + +func (o *GetShareMetricsInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index d268d47c..0b5fcdb8 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -567,7 +567,7 @@ func init() { "operationId": "getEnvironmentMetrics", "parameters": [ { - "type": "string", + "type": "number", "name": "envId", "in": "path", "required": true @@ -585,8 +585,14 @@ func init() { "$ref": "#/definitions/metrics" } }, + "400": { + "description": "bad request" + }, "401": { "description": "unauthorized" + }, + "500": { + "description": "internal server error" } } } @@ -622,8 +628,14 @@ func init() { "$ref": "#/definitions/metrics" } }, + "400": { + "description": "bad request" + }, "401": { "description": "unauthorized" + }, + "500": { + "description": "internal server error" } } } @@ -1991,7 +2003,7 @@ func init() { "operationId": "getEnvironmentMetrics", "parameters": [ { - "type": "string", + "type": "number", "name": "envId", "in": "path", "required": true @@ -2009,8 +2021,14 @@ func init() { "$ref": "#/definitions/metrics" } }, + "400": { + "description": "bad request" + }, "401": { "description": "unauthorized" + }, + "500": { + "description": "internal server error" } } } @@ -2046,8 +2064,14 @@ func init() { "$ref": "#/definitions/metrics" } }, + "400": { + "description": "bad request" + }, "401": { "description": "unauthorized" + }, + "500": { + "description": "internal server error" } } } diff --git a/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go b/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go index 6ea94376..9b6a9423 100644 --- a/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go +++ b/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go @@ -12,6 +12,7 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) // NewGetEnvironmentMetricsParams creates a new GetEnvironmentMetricsParams object @@ -39,7 +40,7 @@ type GetEnvironmentMetricsParams struct { Required: true In: path */ - EnvID string + EnvID float64 } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface @@ -95,7 +96,12 @@ func (o *GetEnvironmentMetricsParams) bindEnvID(rawData []string, hasKey bool, f // Required: true // Parameter is provided by construction from the route - o.EnvID = raw + + value, err := swag.ConvertFloat64(raw) + if err != nil { + return errors.InvalidType("envId", "path", "float64", raw) + } + o.EnvID = value return nil } diff --git a/rest_server_zrok/operations/metadata/get_environment_metrics_responses.go b/rest_server_zrok/operations/metadata/get_environment_metrics_responses.go index 290fc938..945fe314 100644 --- a/rest_server_zrok/operations/metadata/get_environment_metrics_responses.go +++ b/rest_server_zrok/operations/metadata/get_environment_metrics_responses.go @@ -58,6 +58,31 @@ func (o *GetEnvironmentMetricsOK) WriteResponse(rw http.ResponseWriter, producer } } +// GetEnvironmentMetricsBadRequestCode is the HTTP code returned for type GetEnvironmentMetricsBadRequest +const GetEnvironmentMetricsBadRequestCode int = 400 + +/* +GetEnvironmentMetricsBadRequest bad request + +swagger:response getEnvironmentMetricsBadRequest +*/ +type GetEnvironmentMetricsBadRequest struct { +} + +// NewGetEnvironmentMetricsBadRequest creates GetEnvironmentMetricsBadRequest with default headers values +func NewGetEnvironmentMetricsBadRequest() *GetEnvironmentMetricsBadRequest { + + return &GetEnvironmentMetricsBadRequest{} +} + +// WriteResponse to the client +func (o *GetEnvironmentMetricsBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(400) +} + // GetEnvironmentMetricsUnauthorizedCode is the HTTP code returned for type GetEnvironmentMetricsUnauthorized const GetEnvironmentMetricsUnauthorizedCode int = 401 @@ -82,3 +107,28 @@ func (o *GetEnvironmentMetricsUnauthorized) WriteResponse(rw http.ResponseWriter rw.WriteHeader(401) } + +// GetEnvironmentMetricsInternalServerErrorCode is the HTTP code returned for type GetEnvironmentMetricsInternalServerError +const GetEnvironmentMetricsInternalServerErrorCode int = 500 + +/* +GetEnvironmentMetricsInternalServerError internal server error + +swagger:response getEnvironmentMetricsInternalServerError +*/ +type GetEnvironmentMetricsInternalServerError struct { +} + +// NewGetEnvironmentMetricsInternalServerError creates GetEnvironmentMetricsInternalServerError with default headers values +func NewGetEnvironmentMetricsInternalServerError() *GetEnvironmentMetricsInternalServerError { + + return &GetEnvironmentMetricsInternalServerError{} +} + +// WriteResponse to the client +func (o *GetEnvironmentMetricsInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(500) +} diff --git a/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go b/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go index a7787fa2..fc8d98fe 100644 --- a/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go +++ b/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go @@ -10,11 +10,13 @@ import ( "net/url" golangswaggerpaths "path" "strings" + + "github.com/go-openapi/swag" ) // GetEnvironmentMetricsURL generates an URL for the get environment metrics operation type GetEnvironmentMetricsURL struct { - EnvID string + EnvID float64 Duration *string @@ -44,7 +46,7 @@ func (o *GetEnvironmentMetricsURL) Build() (*url.URL, error) { var _path = "/metrics/environment/{envId}" - envID := o.EnvID + envID := swag.FormatFloat64(o.EnvID) if envID != "" { _path = strings.Replace(_path, "{envId}", envID, -1) } else { diff --git a/rest_server_zrok/operations/metadata/get_share_metrics_responses.go b/rest_server_zrok/operations/metadata/get_share_metrics_responses.go index 9f08dc90..5259fa64 100644 --- a/rest_server_zrok/operations/metadata/get_share_metrics_responses.go +++ b/rest_server_zrok/operations/metadata/get_share_metrics_responses.go @@ -58,6 +58,31 @@ func (o *GetShareMetricsOK) WriteResponse(rw http.ResponseWriter, producer runti } } +// GetShareMetricsBadRequestCode is the HTTP code returned for type GetShareMetricsBadRequest +const GetShareMetricsBadRequestCode int = 400 + +/* +GetShareMetricsBadRequest bad request + +swagger:response getShareMetricsBadRequest +*/ +type GetShareMetricsBadRequest struct { +} + +// NewGetShareMetricsBadRequest creates GetShareMetricsBadRequest with default headers values +func NewGetShareMetricsBadRequest() *GetShareMetricsBadRequest { + + return &GetShareMetricsBadRequest{} +} + +// WriteResponse to the client +func (o *GetShareMetricsBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(400) +} + // GetShareMetricsUnauthorizedCode is the HTTP code returned for type GetShareMetricsUnauthorized const GetShareMetricsUnauthorizedCode int = 401 @@ -82,3 +107,28 @@ func (o *GetShareMetricsUnauthorized) WriteResponse(rw http.ResponseWriter, prod rw.WriteHeader(401) } + +// GetShareMetricsInternalServerErrorCode is the HTTP code returned for type GetShareMetricsInternalServerError +const GetShareMetricsInternalServerErrorCode int = 500 + +/* +GetShareMetricsInternalServerError internal server error + +swagger:response getShareMetricsInternalServerError +*/ +type GetShareMetricsInternalServerError struct { +} + +// NewGetShareMetricsInternalServerError creates GetShareMetricsInternalServerError with default headers values +func NewGetShareMetricsInternalServerError() *GetShareMetricsInternalServerError { + + return &GetShareMetricsInternalServerError{} +} + +// WriteResponse to the client +func (o *GetShareMetricsInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(500) +} diff --git a/specs/zrok.yml b/specs/zrok.yml index fe129fc8..a83947ab 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -425,7 +425,7 @@ paths: parameters: - name: envId in: path - type: string + type: number required: true - name: duration in: query @@ -435,8 +435,13 @@ paths: description: environment metrics schema: $ref: "#/definitions/metrics" + 400: + description: bad request 401: description: unauthorized + 500: + description: internal server error + /metrics/share/{shrToken}: get: @@ -458,8 +463,13 @@ paths: description: share metrics schema: $ref: "#/definitions/metrics" + 400: + description: bad request 401: description: unauthorized + 500: + description: internal server error + /version: get: diff --git a/ui/src/api/metadata.js b/ui/src/api/metadata.js index 7b8af17a..7a59d7fc 100644 --- a/ui/src/api/metadata.js +++ b/ui/src/api/metadata.js @@ -56,7 +56,7 @@ export function getAccountMetrics(options) { } /** - * @param {string} envId + * @param {number} envId * @param {object} options Optional options * @param {string} [options.duration] * @return {Promise<module:types.metrics>} environment metrics From 3f8c939adb3563defa5796824b7dac28b91ae1cb Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Tue, 9 May 2023 16:47:22 -0400 Subject: [PATCH 12/49] share metrics handler (#319) --- controller/metrics.go | 81 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/controller/metrics.go b/controller/metrics.go index 355a651a..2c8dd5d6 100644 --- a/controller/metrics.go +++ b/controller/metrics.go @@ -77,7 +77,7 @@ type getEnvironmentMetricsHandler struct { queryApi api.QueryAPI } -func newGetEnvironmentMetricsHAndler(cfg *metrics.InfluxConfig) *getEnvironmentMetricsHandler { +func newGetEnvironmentMetricsHandler(cfg *metrics.InfluxConfig) *getEnvironmentMetricsHandler { idb := influxdb2.NewClient(cfg.Url, cfg.Token) queryApi := idb.QueryAPI(cfg.Org) return &getEnvironmentMetricsHandler{ @@ -146,6 +146,85 @@ func (h *getEnvironmentMetricsHandler) Handle(params metadata.GetEnvironmentMetr return metadata.NewGetEnvironmentMetricsOK().WithPayload(response) } +type getShareMetricsHandler struct { + cfg *metrics.InfluxConfig + idb influxdb2.Client + queryApi api.QueryAPI +} + +func newGetShareMetricsHandler(cfg *metrics.InfluxConfig) *getShareMetricsHandler { + idb := influxdb2.NewClient(cfg.Url, cfg.Token) + queryApi := idb.QueryAPI(cfg.Org) + return &getShareMetricsHandler{ + cfg: cfg, + idb: idb, + queryApi: queryApi, + } +} + +func (h *getShareMetricsHandler) Handle(params metadata.GetShareMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { + trx, err := str.Begin() + if err != nil { + logrus.Errorf("error starting transaction: %v", err) + return metadata.NewGetEnvironmentMetricsInternalServerError() + } + defer func() { _ = trx.Rollback() }() + shr, err := str.FindShareWithToken(params.ShrToken, trx) + if err != nil { + logrus.Errorf("error finding share '%v' for '%v': %v", params.ShrToken, principal.Email, err) + return metadata.NewGetShareMetricsUnauthorized() + } + env, err := str.GetEnvironment(shr.EnvironmentId, trx) + if err != nil { + logrus.Errorf("error finding environment '%d' for '%v': %v", shr.EnvironmentId, principal.Email, err) + return metadata.NewGetShareMetricsUnauthorized() + } + if int64(env.Id) != principal.ID { + logrus.Errorf("user '%v' does not own share '%v'", principal.Email, params.ShrToken) + return metadata.NewGetShareMetricsUnauthorized() + } + + duration := 30 * 24 * time.Hour + if params.Duration != nil { + v, err := time.ParseDuration(*params.Duration) + if err != nil { + logrus.Errorf("bad duration '%v' for '%v': %v", params.Duration, principal.Email, err) + return metadata.NewGetAccountMetricsBadRequest() + } + duration = v + } + slice := duration / 200 + + query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + + fmt.Sprintf("|> range(start: -%v)\n", duration) + + "|> filter(fn: (r) => r[\"_measurement\"] == \"xfer\")\n" + + "|> filter(fn: (r) => r[\"_field\"] == \"rx\" or r[\"_field\"] == \"tx\")\n" + + "|> filter(fn: (r) => r[\"namespace\"] == \"backend\")\n" + + fmt.Sprintf("|> filter(fn: (r) => r[\"share\"] == \"%v\")\n", shr.Token) + + fmt.Sprintf("|> aggregateWindow(every: %v, fn: sum, createEmpty: true)", slice) + + rx, tx, timestamps, err := runFluxForRxTxArray(query, h.queryApi) + if err != nil { + logrus.Errorf("error running account metrics query for '%v': %v", principal.Email, err) + return metadata.NewGetAccountMetricsInternalServerError() + } + + response := &rest_model_zrok.Metrics{ + Scope: "account", + ID: fmt.Sprintf("%d", principal.ID), + Period: duration.Seconds(), + } + for i := 0; i < len(rx) && i < len(tx) && i < len(timestamps); i++ { + response.Samples = append(response.Samples, &rest_model_zrok.MetricsSample{ + Rx: rx[i], + Tx: tx[i], + Timestamp: timestamps[i], + }) + } + + return metadata.NewGetShareMetricsOK().WithPayload(response) +} + func runFluxForRxTxArray(query string, queryApi api.QueryAPI) (rx, tx, timestamps []float64, err error) { result, err := queryApi.Query(context.Background(), query) if err != nil { From e7048e40511374a91d285992fd7bfa718dcc541d Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Tue, 9 May 2023 16:48:11 -0400 Subject: [PATCH 13/49] wire in environment and share handlers (#319) --- controller/controller.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/controller/controller.go b/controller/controller.go index 34905ed8..706bf16f 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -49,6 +49,8 @@ func Run(inCfg *config.Config) error { api.MetadataConfigurationHandler = newConfigurationHandler(cfg) if cfg.Metrics != nil && cfg.Metrics.Influx != nil { api.MetadataGetAccountMetricsHandler = newGetAccountMetricsHandler(cfg.Metrics.Influx) + api.MetadataGetEnvironmentMetricsHandler = newGetEnvironmentMetricsHandler(cfg.Metrics.Influx) + api.MetadataGetShareMetricsHandler = newGetShareMetricsHandler(cfg.Metrics.Influx) } api.MetadataGetEnvironmentDetailHandler = newEnvironmentDetailHandler() api.MetadataGetShareDetailHandler = newShareDetailHandler() From 7d611fda30fdff67d891e0c6a5867a37061bc916 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 10 May 2023 13:37:07 -0400 Subject: [PATCH 14/49] less timeslices in metrics output (#321) --- controller/metrics.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/controller/metrics.go b/controller/metrics.go index 2c8dd5d6..65a29f19 100644 --- a/controller/metrics.go +++ b/controller/metrics.go @@ -39,7 +39,7 @@ func (h *getAccountMetricsHandler) Handle(params metadata.GetAccountMetricsParam } duration = v } - slice := duration / 200 + slice := duration / 50 query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + fmt.Sprintf("|> range(start: -%v)\n", duration) + @@ -113,7 +113,7 @@ func (h *getEnvironmentMetricsHandler) Handle(params metadata.GetEnvironmentMetr } duration = v } - slice := duration / 200 + slice := duration / 50 query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + fmt.Sprintf("|> range(start: -%v)\n", duration) + @@ -193,7 +193,7 @@ func (h *getShareMetricsHandler) Handle(params metadata.GetShareMetricsParams, p } duration = v } - slice := duration / 200 + slice := duration / 50 query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + fmt.Sprintf("|> range(start: -%v)\n", duration) + From b5b3385b46622b6497ae06d1ae86af4806767c88 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 10 May 2023 14:17:10 -0400 Subject: [PATCH 15/49] recharts; account metrics (#319, #321) --- controller/metrics.go | 12 +- ui/package-lock.json | 28 +++- ui/package.json | 3 +- .../console/detail/account/AccountDetail.js | 151 +++++++++++++----- 4 files changed, 141 insertions(+), 53 deletions(-) diff --git a/controller/metrics.go b/controller/metrics.go index 65a29f19..c823ec7f 100644 --- a/controller/metrics.go +++ b/controller/metrics.go @@ -34,12 +34,12 @@ func (h *getAccountMetricsHandler) Handle(params metadata.GetAccountMetricsParam if params.Duration != nil { v, err := time.ParseDuration(*params.Duration) if err != nil { - logrus.Errorf("bad duration '%v' for '%v': %v", params.Duration, principal.Email, err) + logrus.Errorf("bad duration '%v' for '%v': %v", *params.Duration, principal.Email, err) return metadata.NewGetAccountMetricsBadRequest() } duration = v } - slice := duration / 50 + slice := duration / 30 query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + fmt.Sprintf("|> range(start: -%v)\n", duration) + @@ -108,12 +108,12 @@ func (h *getEnvironmentMetricsHandler) Handle(params metadata.GetEnvironmentMetr if params.Duration != nil { v, err := time.ParseDuration(*params.Duration) if err != nil { - logrus.Errorf("bad duration '%v' for '%v': %v", params.Duration, principal.Email, err) + logrus.Errorf("bad duration '%v' for '%v': %v", *params.Duration, principal.Email, err) return metadata.NewGetAccountMetricsBadRequest() } duration = v } - slice := duration / 50 + slice := duration / 30 query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + fmt.Sprintf("|> range(start: -%v)\n", duration) + @@ -188,12 +188,12 @@ func (h *getShareMetricsHandler) Handle(params metadata.GetShareMetricsParams, p if params.Duration != nil { v, err := time.ParseDuration(*params.Duration) if err != nil { - logrus.Errorf("bad duration '%v' for '%v': %v", params.Duration, principal.Email, err) + logrus.Errorf("bad duration '%v' for '%v': %v", *params.Duration, principal.Email, err) return metadata.NewGetAccountMetricsBadRequest() } duration = v } - slice := duration / 50 + slice := duration / 30 query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + fmt.Sprintf("|> range(start: -%v)\n", duration) + diff --git a/ui/package-lock.json b/ui/package-lock.json index 845ba995..316469bd 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -17,6 +17,7 @@ "dagre": "^0.8.5", "eslint-config-react-app": "^7.0.1", "humanize-duration": "^3.27.3", + "moment": "^2.29.4", "react": "^18.2.0", "react-bootstrap": "^2.7.0", "react-data-table-component": "^7.5.2", @@ -24,7 +25,7 @@ "react-force-graph": "^1.43.0", "react-router-dom": "^6.4.0", "react-sizeme": "^3.0.2", - "recharts": "^2.5.0", + "recharts": "^2.6.1", "styled-components": "^5.3.5", "svgo": "^3.0.2" }, @@ -13651,6 +13652,14 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -16545,9 +16554,9 @@ } }, "node_modules/recharts": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.5.0.tgz", - "integrity": "sha512-0EQYz3iA18r1Uq8VqGZ4dABW52AKBnio37kJgnztIqprELJXpOEsa0SzkqU1vjAhpCXCv52Dx1hiL9119xsqsQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.6.1.tgz", + "integrity": "sha512-eGNNqQTSg737HB0tfFkPZbPW8ji7Q8joQM0P2yAEkJkB8CO+LJPgLpx/NUxNHJsxoXvSblMFoy5RSVBYfLU+HA==", "dependencies": { "classnames": "^2.2.5", "eventemitter3": "^4.0.1", @@ -29808,6 +29817,11 @@ "minimist": "^1.2.6" } }, + "moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -31796,9 +31810,9 @@ } }, "recharts": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.5.0.tgz", - "integrity": "sha512-0EQYz3iA18r1Uq8VqGZ4dABW52AKBnio37kJgnztIqprELJXpOEsa0SzkqU1vjAhpCXCv52Dx1hiL9119xsqsQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.6.1.tgz", + "integrity": "sha512-eGNNqQTSg737HB0tfFkPZbPW8ji7Q8joQM0P2yAEkJkB8CO+LJPgLpx/NUxNHJsxoXvSblMFoy5RSVBYfLU+HA==", "requires": { "classnames": "^2.2.5", "eventemitter3": "^4.0.1", diff --git a/ui/package.json b/ui/package.json index 782354c0..a585b11d 100644 --- a/ui/package.json +++ b/ui/package.json @@ -12,6 +12,7 @@ "dagre": "^0.8.5", "eslint-config-react-app": "^7.0.1", "humanize-duration": "^3.27.3", + "moment": "^2.29.4", "react": "^18.2.0", "react-bootstrap": "^2.7.0", "react-data-table-component": "^7.5.2", @@ -19,7 +20,7 @@ "react-force-graph": "^1.43.0", "react-router-dom": "^6.4.0", "react-sizeme": "^3.0.2", - "recharts": "^2.5.0", + "recharts": "^2.6.1", "styled-components": "^5.3.5", "svgo": "^3.0.2" }, diff --git a/ui/src/console/detail/account/AccountDetail.js b/ui/src/console/detail/account/AccountDetail.js index bd39617d..ec614835 100644 --- a/ui/src/console/detail/account/AccountDetail.js +++ b/ui/src/console/detail/account/AccountDetail.js @@ -1,11 +1,12 @@ import {mdiAccountBox} from "@mdi/js"; import Icon from "@mdi/react"; import PropertyTable from "../../PropertyTable"; -import {Tab, Tabs, Tooltip} from "react-bootstrap"; +import {Col, Container, Row, Tab, Tabs, Tooltip} from "react-bootstrap"; import SecretToggle from "../../SecretToggle"; import React, {useEffect, useState} from "react"; import * as metadata from "../../../api/metadata"; -import {Area, AreaChart, CartesianGrid, Line, LineChart, ResponsiveContainer, XAxis, YAxis} from "recharts"; +import { Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis } from "recharts"; +import moment from "moment"; const AccountDetail = (props) => { const customProperties = { @@ -28,14 +29,22 @@ const AccountDetail = (props) => { } const MetricsTab = (props) => { - const [metrics, setMetrics] = useState({}); - const [tx, setTx] = useState(0); - const [rx, setRx] = useState(0) + const [metrics30, setMetrics30] = useState(buildMetrics([])); + const [metrics7, setMetrics7] = useState(buildMetrics([])); + const [metrics1, setMetrics1] = useState(buildMetrics([])); useEffect(() => { metadata.getAccountMetrics() .then(resp => { - setMetrics(resp.data); + setMetrics30(buildMetrics(resp.data)); + }); + metadata.getAccountMetrics({duration: "168h"}) + .then(resp => { + setMetrics7(buildMetrics(resp.data)); + }); + metadata.getAccountMetrics({duration: "24h"}) + .then(resp => { + setMetrics1(buildMetrics(resp.data)); }); }, []); @@ -45,50 +54,114 @@ const MetricsTab = (props) => { metadata.getAccountMetrics() .then(resp => { if(mounted) { - setMetrics(resp.data); + setMetrics30(buildMetrics(resp.data)); } }); - }, 1000); + metadata.getAccountMetrics({duration: "168h"}) + .then(resp => { + setMetrics7(buildMetrics(resp.data)); + }); + metadata.getAccountMetrics({duration: "24h"}) + .then(resp => { + setMetrics1(buildMetrics(resp.data)); + }); + }, 5000); return () => { mounted = false; clearInterval(interval); } }, []); - useEffect(() => { - let txAccum = 0 - let rxAccum = 0 - if(metrics.samples) { - metrics.samples.forEach(sample => { - txAccum += sample.tx - rxAccum += sample.rx - }) - } - setTx(txAccum); - setRx(rxAccum); - }, [metrics]) - - console.log(metrics); - return ( - <div> - <div> - <h1>RX: {bytesToSize(rx)}, TX: {bytesToSize(tx)}</h1> - </div> - <ResponsiveContainer width={"100%"} height={300}> - <LineChart data={metrics.samples}> - <CartesianGrid strokeDasharay={"3 3"} /> - <XAxis dataKey={(v) => new Date(v.timestamp)} /> - <YAxis /> - <Line type={"linear"} stroke={"red"} dataKey={"rx"} activeDot={{ r: 8 }}/> - <Line type={"linear"} stroke={"green"} dataKey={"tx"} /> - <Tooltip /> - </LineChart> - </ResponsiveContainer> - </div> + <Container> + <Row> + <Col> + <h3>Last 30 Days:</h3> + </Col> + </Row> + <Row> + <Col><p>Received: {bytesToSize(metrics30.rx)}</p></Col> + <Col><p>Sent: {bytesToSize(metrics30.tx)}</p></Col> + </Row> + <Row> + <Col> + <ResponsiveContainer width={"100%"} height={150}> + <BarChart data={metrics30.data}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Tooltip /> + </BarChart> + </ResponsiveContainer> + </Col> + </Row> + <Row> + <Col> + <h3>Last 7 Days:</h3> + </Col> + </Row> + <Row> + <Col><p>Received: {bytesToSize(metrics7.rx)}</p></Col> + <Col><p>Sent: {bytesToSize(metrics7.tx)}</p></Col> + </Row> + <Row> + <Col> + <ResponsiveContainer width={"100%"} height={150}> + <BarChart data={metrics7.data}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Tooltip /> + </BarChart> + </ResponsiveContainer> + </Col> + </Row> + <Row> + <Col> + <h3>Last 24 Hours:</h3> + </Col> + </Row> + <Row> + <Col><p>Received: {bytesToSize(metrics1.rx)}</p></Col> + <Col><p>Sent: {bytesToSize(metrics1.tx)}</p></Col> + </Row> + <Row> + <Col> + <ResponsiveContainer width={"100%"} height={150}> + <BarChart data={metrics1.data}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Tooltip /> + </BarChart> + </ResponsiveContainer> + </Col> + </Row> + </Container> ); } +const buildMetrics = (m) => { + let metrics = { + data: m.samples, + rx: 0, + tx: 0 + } + if(m.samples) { + m.samples.forEach(s => { + metrics.rx += s.rx; + metrics.tx += s.tx; + }); + } + return metrics; +} + const bytesToSize = (sz) => { let absSz = sz; if(absSz < 0) { @@ -105,7 +178,7 @@ const bytesToSize = (sz) => { exp++; } - return '' + (sz / div).toFixed(2) + "kMGTPE"[exp]; + return '' + (sz / div).toFixed(1) + ' ' + "kMGTPE"[exp] + 'B'; } export default AccountDetail; \ No newline at end of file From 0f6d05f449e36aa88c2bfb5be813c021ebd310f3 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 10 May 2023 14:23:32 -0400 Subject: [PATCH 16/49] metrics tab first, active by default (#234) --- ui/src/console/detail/account/AccountDetail.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/console/detail/account/AccountDetail.js b/ui/src/console/detail/account/AccountDetail.js index ec614835..5b2b3675 100644 --- a/ui/src/console/detail/account/AccountDetail.js +++ b/ui/src/console/detail/account/AccountDetail.js @@ -16,13 +16,13 @@ const AccountDetail = (props) => { return ( <div> <h2><Icon path={mdiAccountBox} size={2} />{" "}{props.user.email}</h2> - <Tabs defaultActiveKey={"detail"}> - <Tab eventKey={"detail"} title={"Detail"}> - <PropertyTable object={props.user} custom={customProperties}/> - </Tab> + <Tabs defaultActiveKey={"metrics"}> <Tab eventKey={"metrics"} title={"Metrics"}> <MetricsTab /> </Tab> + <Tab eventKey={"detail"} title={"Detail"}> + <PropertyTable object={props.user} custom={customProperties}/> + </Tab> </Tabs> </div> ); From 43e6c56ec10fb18454b6a427237e6428ac594287 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 10 May 2023 14:51:46 -0400 Subject: [PATCH 17/49] environment metrics wired in and working (#324) --- controller/metrics.go | 8 +- .../get_environment_metrics_parameters.go | 9 +- rest_server_zrok/embedded_spec.go | 4 +- .../get_environment_metrics_parameters.go | 10 +- .../get_environment_metrics_urlbuilder.go | 6 +- specs/zrok.yml | 2 +- ui/src/api/metadata.js | 2 +- .../console/detail/account/AccountDetail.js | 37 +---- .../detail/environment/EnvironmentDetail.js | 4 + .../console/detail/environment/MetricsTab.js | 129 ++++++++++++++++++ ui/src/console/metrics.js | 33 +++++ 11 files changed, 182 insertions(+), 62 deletions(-) create mode 100644 ui/src/console/detail/environment/MetricsTab.js create mode 100644 ui/src/console/metrics.js diff --git a/controller/metrics.go b/controller/metrics.go index c823ec7f..d4582002 100644 --- a/controller/metrics.go +++ b/controller/metrics.go @@ -94,13 +94,9 @@ func (h *getEnvironmentMetricsHandler) Handle(params metadata.GetEnvironmentMetr return metadata.NewGetEnvironmentMetricsInternalServerError() } defer func() { _ = trx.Rollback() }() - env, err := str.GetEnvironment(int(params.EnvID), trx) + env, err := str.FindEnvironmentForAccount(params.EnvID, int(principal.ID), trx) if err != nil { - logrus.Errorf("error finding environment '%d': %v", int(params.EnvID), err) - return metadata.NewGetEnvironmentMetricsUnauthorized() - } - if int64(env.Id) != principal.ID { - logrus.Errorf("unauthorized environemnt '%d' for '%v'", int(params.EnvID), principal.Email) + logrus.Errorf("error finding environment '%s' for '%s': %v", params.EnvID, principal.Email, err) return metadata.NewGetEnvironmentMetricsUnauthorized() } diff --git a/rest_client_zrok/metadata/get_environment_metrics_parameters.go b/rest_client_zrok/metadata/get_environment_metrics_parameters.go index 6bdd8404..be7aae16 100644 --- a/rest_client_zrok/metadata/get_environment_metrics_parameters.go +++ b/rest_client_zrok/metadata/get_environment_metrics_parameters.go @@ -14,7 +14,6 @@ import ( "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" - "github.com/go-openapi/swag" ) // NewGetEnvironmentMetricsParams creates a new GetEnvironmentMetricsParams object, @@ -66,7 +65,7 @@ type GetEnvironmentMetricsParams struct { Duration *string // EnvID. - EnvID float64 + EnvID string timeout time.Duration Context context.Context @@ -133,13 +132,13 @@ func (o *GetEnvironmentMetricsParams) SetDuration(duration *string) { } // WithEnvID adds the envID to the get environment metrics params -func (o *GetEnvironmentMetricsParams) WithEnvID(envID float64) *GetEnvironmentMetricsParams { +func (o *GetEnvironmentMetricsParams) WithEnvID(envID string) *GetEnvironmentMetricsParams { o.SetEnvID(envID) return o } // SetEnvID adds the envId to the get environment metrics params -func (o *GetEnvironmentMetricsParams) SetEnvID(envID float64) { +func (o *GetEnvironmentMetricsParams) SetEnvID(envID string) { o.EnvID = envID } @@ -169,7 +168,7 @@ func (o *GetEnvironmentMetricsParams) WriteToRequest(r runtime.ClientRequest, re } // path param envId - if err := r.SetPathParam("envId", swag.FormatFloat64(o.EnvID)); err != nil { + if err := r.SetPathParam("envId", o.EnvID); err != nil { return err } diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 0b5fcdb8..fc9e8858 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -567,7 +567,7 @@ func init() { "operationId": "getEnvironmentMetrics", "parameters": [ { - "type": "number", + "type": "string", "name": "envId", "in": "path", "required": true @@ -2003,7 +2003,7 @@ func init() { "operationId": "getEnvironmentMetrics", "parameters": [ { - "type": "number", + "type": "string", "name": "envId", "in": "path", "required": true diff --git a/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go b/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go index 9b6a9423..6ea94376 100644 --- a/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go +++ b/rest_server_zrok/operations/metadata/get_environment_metrics_parameters.go @@ -12,7 +12,6 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" - "github.com/go-openapi/swag" ) // NewGetEnvironmentMetricsParams creates a new GetEnvironmentMetricsParams object @@ -40,7 +39,7 @@ type GetEnvironmentMetricsParams struct { Required: true In: path */ - EnvID float64 + EnvID string } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface @@ -96,12 +95,7 @@ func (o *GetEnvironmentMetricsParams) bindEnvID(rawData []string, hasKey bool, f // Required: true // Parameter is provided by construction from the route - - value, err := swag.ConvertFloat64(raw) - if err != nil { - return errors.InvalidType("envId", "path", "float64", raw) - } - o.EnvID = value + o.EnvID = raw return nil } diff --git a/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go b/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go index fc8d98fe..a7787fa2 100644 --- a/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go +++ b/rest_server_zrok/operations/metadata/get_environment_metrics_urlbuilder.go @@ -10,13 +10,11 @@ import ( "net/url" golangswaggerpaths "path" "strings" - - "github.com/go-openapi/swag" ) // GetEnvironmentMetricsURL generates an URL for the get environment metrics operation type GetEnvironmentMetricsURL struct { - EnvID float64 + EnvID string Duration *string @@ -46,7 +44,7 @@ func (o *GetEnvironmentMetricsURL) Build() (*url.URL, error) { var _path = "/metrics/environment/{envId}" - envID := swag.FormatFloat64(o.EnvID) + envID := o.EnvID if envID != "" { _path = strings.Replace(_path, "{envId}", envID, -1) } else { diff --git a/specs/zrok.yml b/specs/zrok.yml index a83947ab..d83417e0 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -425,7 +425,7 @@ paths: parameters: - name: envId in: path - type: number + type: string required: true - name: duration in: query diff --git a/ui/src/api/metadata.js b/ui/src/api/metadata.js index 7a59d7fc..7b8af17a 100644 --- a/ui/src/api/metadata.js +++ b/ui/src/api/metadata.js @@ -56,7 +56,7 @@ export function getAccountMetrics(options) { } /** - * @param {number} envId + * @param {string} envId * @param {object} options Optional options * @param {string} [options.duration] * @return {Promise<module:types.metrics>} environment metrics diff --git a/ui/src/console/detail/account/AccountDetail.js b/ui/src/console/detail/account/AccountDetail.js index 5b2b3675..94b85b8d 100644 --- a/ui/src/console/detail/account/AccountDetail.js +++ b/ui/src/console/detail/account/AccountDetail.js @@ -7,6 +7,7 @@ import React, {useEffect, useState} from "react"; import * as metadata from "../../../api/metadata"; import { Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis } from "recharts"; import moment from "moment"; +import {buildMetrics, bytesToSize} from "../../metrics"; const AccountDetail = (props) => { const customProperties = { @@ -28,7 +29,7 @@ const AccountDetail = (props) => { ); } -const MetricsTab = (props) => { +const MetricsTab = () => { const [metrics30, setMetrics30] = useState(buildMetrics([])); const [metrics7, setMetrics7] = useState(buildMetrics([])); const [metrics1, setMetrics1] = useState(buildMetrics([])); @@ -147,38 +148,4 @@ const MetricsTab = (props) => { ); } -const buildMetrics = (m) => { - let metrics = { - data: m.samples, - rx: 0, - tx: 0 - } - if(m.samples) { - m.samples.forEach(s => { - metrics.rx += s.rx; - metrics.tx += s.tx; - }); - } - return metrics; -} - -const bytesToSize = (sz) => { - let absSz = sz; - if(absSz < 0) { - absSz *= -1; - } - const unit = 1000 - if(absSz < unit) { - return '' + absSz + ' B'; - } - let div = unit - let exp = 0 - for(let n = absSz / unit; n >= unit; n /= unit) { - div *= unit; - exp++; - } - - return '' + (sz / div).toFixed(1) + ' ' + "kMGTPE"[exp] + 'B'; -} - export default AccountDetail; \ No newline at end of file diff --git a/ui/src/console/detail/environment/EnvironmentDetail.js b/ui/src/console/detail/environment/EnvironmentDetail.js index af8865e3..e3861f53 100644 --- a/ui/src/console/detail/environment/EnvironmentDetail.js +++ b/ui/src/console/detail/environment/EnvironmentDetail.js @@ -6,6 +6,7 @@ import {mdiConsoleNetwork} from "@mdi/js"; import {getEnvironmentDetail} from "../../../api/metadata"; import DetailTab from "./DetailTab"; import ActionsTab from "./ActionsTab"; +import MetricsTab from "./MetricsTab"; const EnvironmentDetail = (props) => { const [detail, setDetail] = useState({}); @@ -25,6 +26,9 @@ const EnvironmentDetail = (props) => { <Tab eventKey={"shares"} title={"Shares"}> <SharesTab selection={props.selection} /> </Tab> + <Tab eventKey={"metrics"} title={"Metrics"}> + <MetricsTab selection={props.selection} /> + </Tab> <Tab eventKey={"detail"} title={"Detail"}> <DetailTab environment={detail.environment} /> </Tab> diff --git a/ui/src/console/detail/environment/MetricsTab.js b/ui/src/console/detail/environment/MetricsTab.js new file mode 100644 index 00000000..8cee2249 --- /dev/null +++ b/ui/src/console/detail/environment/MetricsTab.js @@ -0,0 +1,129 @@ +import React, {useEffect, useState} from "react"; +import {buildMetrics, bytesToSize} from "../../metrics"; +import * as metadata from "../../../api/metadata"; +import {Col, Container, Row, Tooltip} from "react-bootstrap"; +import {Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis} from "recharts"; +import moment from "moment/moment"; + +const MetricsTab = (props) => { + const [metrics30, setMetrics30] = useState(buildMetrics([])); + const [metrics7, setMetrics7] = useState(buildMetrics([])); + const [metrics1, setMetrics1] = useState(buildMetrics([])); + + console.log("selection", props.selection); + + useEffect(() => { + metadata.getEnvironmentMetrics(props.selection.id) + .then(resp => { + setMetrics30(buildMetrics(resp.data)); + }); + metadata.getEnvironmentMetrics(props.selection.id, {duration: "168h"}) + .then(resp => { + setMetrics7(buildMetrics(resp.data)); + }); + metadata.getEnvironmentMetrics(props.selection.id, {duration: "24h"}) + .then(resp => { + setMetrics1(buildMetrics(resp.data)); + }); + }, []); + + useEffect(() => { + let mounted = true; + let interval = setInterval(() => { + metadata.getEnvironmentMetrics(props.selection.id) + .then(resp => { + if(mounted) { + setMetrics30(buildMetrics(resp.data)); + } + }); + metadata.getEnvironmentMetrics(props.selection.id, {duration: "168h"}) + .then(resp => { + setMetrics7(buildMetrics(resp.data)); + }); + metadata.getEnvironmentMetrics(props.selection.id, {duration: "24h"}) + .then(resp => { + setMetrics1(buildMetrics(resp.data)); + }); + }, 5000); + return () => { + mounted = false; + clearInterval(interval); + } + }, []); + + return ( + <Container> + <Row> + <Col> + <h3>Last 30 Days:</h3> + </Col> + </Row> + <Row> + <Col><p>Received: {bytesToSize(metrics30.rx)}</p></Col> + <Col><p>Sent: {bytesToSize(metrics30.tx)}</p></Col> + </Row> + <Row> + <Col> + <ResponsiveContainer width={"100%"} height={150}> + <BarChart data={metrics30.data}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Tooltip /> + </BarChart> + </ResponsiveContainer> + </Col> + </Row> + <Row> + <Col> + <h3>Last 7 Days:</h3> + </Col> + </Row> + <Row> + <Col><p>Received: {bytesToSize(metrics7.rx)}</p></Col> + <Col><p>Sent: {bytesToSize(metrics7.tx)}</p></Col> + </Row> + <Row> + <Col> + <ResponsiveContainer width={"100%"} height={150}> + <BarChart data={metrics7.data}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Tooltip /> + </BarChart> + </ResponsiveContainer> + </Col> + </Row> + <Row> + <Col> + <h3>Last 24 Hours:</h3> + </Col> + </Row> + <Row> + <Col><p>Received: {bytesToSize(metrics1.rx)}</p></Col> + <Col><p>Sent: {bytesToSize(metrics1.tx)}</p></Col> + </Row> + <Row> + <Col> + <ResponsiveContainer width={"100%"} height={150}> + <BarChart data={metrics1.data}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Tooltip /> + </BarChart> + </ResponsiveContainer> + </Col> + </Row> + </Container> + ); +}; + +export default MetricsTab; \ No newline at end of file diff --git a/ui/src/console/metrics.js b/ui/src/console/metrics.js new file mode 100644 index 00000000..b316a14a --- /dev/null +++ b/ui/src/console/metrics.js @@ -0,0 +1,33 @@ +export const buildMetrics = (m) => { + let metrics = { + data: m.samples, + rx: 0, + tx: 0 + } + if(m.samples) { + m.samples.forEach(s => { + metrics.rx += s.rx; + metrics.tx += s.tx; + }); + } + return metrics; +} + +export const bytesToSize = (sz) => { + let absSz = sz; + if(absSz < 0) { + absSz *= -1; + } + const unit = 1000 + if(absSz < unit) { + return '' + absSz + ' B'; + } + let div = unit + let exp = 0 + for(let n = absSz / unit; n >= unit; n /= unit) { + div *= unit; + exp++; + } + + return '' + (sz / div).toFixed(1) + ' ' + "kMGTPE"[exp] + 'B'; +} \ No newline at end of file From c193482171f7d9a0694ea9fe2bc4c415ed92e912 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 10 May 2023 14:59:26 -0400 Subject: [PATCH 18/49] better slice size management? (#324) --- controller/metrics.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/controller/metrics.go b/controller/metrics.go index d4582002..0dda77b0 100644 --- a/controller/metrics.go +++ b/controller/metrics.go @@ -39,7 +39,7 @@ func (h *getAccountMetricsHandler) Handle(params metadata.GetAccountMetricsParam } duration = v } - slice := duration / 30 + slice := sliceSize(duration) query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + fmt.Sprintf("|> range(start: -%v)\n", duration) + @@ -109,7 +109,7 @@ func (h *getEnvironmentMetricsHandler) Handle(params metadata.GetEnvironmentMetr } duration = v } - slice := duration / 30 + slice := sliceSize(duration) query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + fmt.Sprintf("|> range(start: -%v)\n", duration) + @@ -189,7 +189,7 @@ func (h *getShareMetricsHandler) Handle(params metadata.GetShareMetricsParams, p } duration = v } - slice := duration / 30 + slice := sliceSize(duration) query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + fmt.Sprintf("|> range(start: -%v)\n", duration) + @@ -239,3 +239,16 @@ func runFluxForRxTxArray(query string, queryApi api.QueryAPI) (rx, tx, timestamp } return rx, tx, timestamps, nil } + +func sliceSize(duration time.Duration) time.Duration { + switch duration { + case 30 * 24 * time.Hour: + return 24 * time.Hour + case 7 * 24 * time.Hour: + return 20 * time.Minute + case 24 * time.Hour: + return 5 * time.Minute + default: + return duration + } +} From 945090f3e85583297ef94be53685a67c386a85d1 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 10 May 2023 15:15:09 -0400 Subject: [PATCH 19/49] share metrics (#324) --- controller/metrics.go | 2 +- .../console/detail/environment/MetricsTab.js | 2 - ui/src/console/detail/share/MetricsTab.js | 129 ++++++++++++++++++ ui/src/console/detail/share/ShareDetail.js | 6 +- ui/src/console/visualizer/graph.js | 1 - 5 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 ui/src/console/detail/share/MetricsTab.js diff --git a/controller/metrics.go b/controller/metrics.go index 0dda77b0..81780e57 100644 --- a/controller/metrics.go +++ b/controller/metrics.go @@ -175,7 +175,7 @@ func (h *getShareMetricsHandler) Handle(params metadata.GetShareMetricsParams, p logrus.Errorf("error finding environment '%d' for '%v': %v", shr.EnvironmentId, principal.Email, err) return metadata.NewGetShareMetricsUnauthorized() } - if int64(env.Id) != principal.ID { + if env.AccountId != nil && int64(*env.AccountId) != principal.ID { logrus.Errorf("user '%v' does not own share '%v'", principal.Email, params.ShrToken) return metadata.NewGetShareMetricsUnauthorized() } diff --git a/ui/src/console/detail/environment/MetricsTab.js b/ui/src/console/detail/environment/MetricsTab.js index 8cee2249..8dfae829 100644 --- a/ui/src/console/detail/environment/MetricsTab.js +++ b/ui/src/console/detail/environment/MetricsTab.js @@ -10,8 +10,6 @@ const MetricsTab = (props) => { const [metrics7, setMetrics7] = useState(buildMetrics([])); const [metrics1, setMetrics1] = useState(buildMetrics([])); - console.log("selection", props.selection); - useEffect(() => { metadata.getEnvironmentMetrics(props.selection.id) .then(resp => { diff --git a/ui/src/console/detail/share/MetricsTab.js b/ui/src/console/detail/share/MetricsTab.js new file mode 100644 index 00000000..4f3b6126 --- /dev/null +++ b/ui/src/console/detail/share/MetricsTab.js @@ -0,0 +1,129 @@ +import React, {useEffect, useState} from "react"; +import {buildMetrics, bytesToSize} from "../../metrics"; +import * as metadata from "../../../api/metadata"; +import {Col, Container, Row, Tooltip} from "react-bootstrap"; +import {Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis} from "recharts"; +import moment from "moment"; + +const MetricsTab = (props) => { + const [metrics30, setMetrics30] = useState(buildMetrics([])); + const [metrics7, setMetrics7] = useState(buildMetrics([])); + const [metrics1, setMetrics1] = useState(buildMetrics([])); + + useEffect(() => { + console.log("token", props.share.token); + metadata.getShareMetrics(props.share.token) + .then(resp => { + setMetrics30(buildMetrics(resp.data)); + }); + metadata.getShareMetrics(props.share.token, {duration: "168h"}) + .then(resp => { + setMetrics7(buildMetrics(resp.data)); + }); + metadata.getShareMetrics(props.share.token, {duration: "24h"}) + .then(resp => { + setMetrics1(buildMetrics(resp.data)); + }); + }, [props.share]); + + useEffect(() => { + let mounted = true; + let interval = setInterval(() => { + console.log("token", props.share.token); + metadata.getShareMetrics(props.share.token) + .then(resp => { + if(mounted) { + setMetrics30(buildMetrics(resp.data)); + } + }); + metadata.getShareMetrics(props.share.token, {duration: "168h"}) + .then(resp => { + setMetrics7(buildMetrics(resp.data)); + }); + metadata.getShareMetrics(props.share.token, {duration: "24h"}) + .then(resp => { + setMetrics1(buildMetrics(resp.data)); + }); + }, 5000); + return () => { + mounted = false; + clearInterval(interval); + } + }, [props.share]); + + return ( + <Container> + <Row> + <Col> + <h3>Last 30 Days:</h3> + </Col> + </Row> + <Row> + <Col><p>Received: {bytesToSize(metrics30.rx)}</p></Col> + <Col><p>Sent: {bytesToSize(metrics30.tx)}</p></Col> + </Row> + <Row> + <Col> + <ResponsiveContainer width={"100%"} height={150}> + <BarChart data={metrics30.data}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Tooltip /> + </BarChart> + </ResponsiveContainer> + </Col> + </Row> + <Row> + <Col> + <h3>Last 7 Days:</h3> + </Col> + </Row> + <Row> + <Col><p>Received: {bytesToSize(metrics7.rx)}</p></Col> + <Col><p>Sent: {bytesToSize(metrics7.tx)}</p></Col> + </Row> + <Row> + <Col> + <ResponsiveContainer width={"100%"} height={150}> + <BarChart data={metrics7.data}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Tooltip /> + </BarChart> + </ResponsiveContainer> + </Col> + </Row> + <Row> + <Col> + <h3>Last 24 Hours:</h3> + </Col> + </Row> + <Row> + <Col><p>Received: {bytesToSize(metrics1.rx)}</p></Col> + <Col><p>Sent: {bytesToSize(metrics1.tx)}</p></Col> + </Row> + <Row> + <Col> + <ResponsiveContainer width={"100%"} height={150}> + <BarChart data={metrics1.data}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Tooltip /> + </BarChart> + </ResponsiveContainer> + </Col> + </Row> + </Container> + ); +} + +export default MetricsTab; \ No newline at end of file diff --git a/ui/src/console/detail/share/ShareDetail.js b/ui/src/console/detail/share/ShareDetail.js index 31501afc..c841af7f 100644 --- a/ui/src/console/detail/share/ShareDetail.js +++ b/ui/src/console/detail/share/ShareDetail.js @@ -7,6 +7,7 @@ import {Tab, Tabs} from "react-bootstrap"; import ActionsTab from "./ActionsTab"; import SecretToggle from "../../SecretToggle"; import {Area, AreaChart, Line, LineChart, ResponsiveContainer, XAxis} from "recharts"; +import MetricsTab from "./MetricsTab"; const ShareDetail = (props) => { const [detail, setDetail] = useState({}); @@ -62,7 +63,10 @@ const ShareDetail = (props) => { return ( <div> <h2><Icon path={mdiShareVariant} size={2} />{" "}{detail.backendProxyEndpoint}</h2> - <Tabs defaultActiveKey={"detail"}> + <Tabs defaultActiveKey={"metrics"}> + <Tab eventKey={"metrics"} title={"Metrics"}> + <MetricsTab share={detail} /> + </Tab> <Tab eventKey={"detail"} title={"Detail"}> <PropertyTable object={detail} custom={customProperties} /> </Tab> diff --git a/ui/src/console/visualizer/graph.js b/ui/src/console/visualizer/graph.js index 7a445081..c10d0ab3 100644 --- a/ui/src/console/visualizer/graph.js +++ b/ui/src/console/visualizer/graph.js @@ -55,7 +55,6 @@ export const mergeGraph = (oldGraph, user, newOverview) => { type: "share", val: 50 }; - console.log('share', shrNode.label); newGraph.nodes.push(shrNode); newGraph.links.push({ target: envNode.id, From 273a680b04ca83fcdac963329442111a6eb32c88 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 11 May 2023 11:50:42 -0400 Subject: [PATCH 20/49] reduce metrics view code duplication (#324) --- .../console/detail/account/AccountDetail.js | 79 +----------------- .../console/detail/environment/MetricsTab.js | 78 +---------------- ui/src/console/detail/share/MetricsTab.js | 76 +---------------- ui/src/console/metrics/MetricsView.js | 83 +++++++++++++++++++ 4 files changed, 92 insertions(+), 224 deletions(-) create mode 100644 ui/src/console/metrics/MetricsView.js diff --git a/ui/src/console/detail/account/AccountDetail.js b/ui/src/console/detail/account/AccountDetail.js index 94b85b8d..dec934b1 100644 --- a/ui/src/console/detail/account/AccountDetail.js +++ b/ui/src/console/detail/account/AccountDetail.js @@ -1,13 +1,12 @@ import {mdiAccountBox} from "@mdi/js"; import Icon from "@mdi/react"; import PropertyTable from "../../PropertyTable"; -import {Col, Container, Row, Tab, Tabs, Tooltip} from "react-bootstrap"; +import {Tab, Tabs} from "react-bootstrap"; import SecretToggle from "../../SecretToggle"; import React, {useEffect, useState} from "react"; import * as metadata from "../../../api/metadata"; -import { Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis } from "recharts"; -import moment from "moment"; -import {buildMetrics, bytesToSize} from "../../metrics"; +import {buildMetrics} from "../../metrics"; +import MetricsView from "../../metrics/MetricsView"; const AccountDetail = (props) => { const customProperties = { @@ -74,77 +73,7 @@ const MetricsTab = () => { }, []); return ( - <Container> - <Row> - <Col> - <h3>Last 30 Days:</h3> - </Col> - </Row> - <Row> - <Col><p>Received: {bytesToSize(metrics30.rx)}</p></Col> - <Col><p>Sent: {bytesToSize(metrics30.tx)}</p></Col> - </Row> - <Row> - <Col> - <ResponsiveContainer width={"100%"} height={150}> - <BarChart data={metrics30.data}> - <CartesianGrid strokeDasharay={"3 3"} /> - <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> - <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> - <Tooltip /> - </BarChart> - </ResponsiveContainer> - </Col> - </Row> - <Row> - <Col> - <h3>Last 7 Days:</h3> - </Col> - </Row> - <Row> - <Col><p>Received: {bytesToSize(metrics7.rx)}</p></Col> - <Col><p>Sent: {bytesToSize(metrics7.tx)}</p></Col> - </Row> - <Row> - <Col> - <ResponsiveContainer width={"100%"} height={150}> - <BarChart data={metrics7.data}> - <CartesianGrid strokeDasharay={"3 3"} /> - <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> - <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> - <Tooltip /> - </BarChart> - </ResponsiveContainer> - </Col> - </Row> - <Row> - <Col> - <h3>Last 24 Hours:</h3> - </Col> - </Row> - <Row> - <Col><p>Received: {bytesToSize(metrics1.rx)}</p></Col> - <Col><p>Sent: {bytesToSize(metrics1.tx)}</p></Col> - </Row> - <Row> - <Col> - <ResponsiveContainer width={"100%"} height={150}> - <BarChart data={metrics1.data}> - <CartesianGrid strokeDasharay={"3 3"} /> - <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> - <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> - <Tooltip /> - </BarChart> - </ResponsiveContainer> - </Col> - </Row> - </Container> + <MetricsView metrics30={metrics30} metrics7={metrics7} metrics1={metrics1} /> ); } diff --git a/ui/src/console/detail/environment/MetricsTab.js b/ui/src/console/detail/environment/MetricsTab.js index 8dfae829..23e6c1de 100644 --- a/ui/src/console/detail/environment/MetricsTab.js +++ b/ui/src/console/detail/environment/MetricsTab.js @@ -1,9 +1,7 @@ import React, {useEffect, useState} from "react"; -import {buildMetrics, bytesToSize} from "../../metrics"; +import {buildMetrics} from "../../metrics"; import * as metadata from "../../../api/metadata"; -import {Col, Container, Row, Tooltip} from "react-bootstrap"; -import {Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis} from "recharts"; -import moment from "moment/moment"; +import MetricsView from "../../metrics/MetricsView"; const MetricsTab = (props) => { const [metrics30, setMetrics30] = useState(buildMetrics([])); @@ -50,77 +48,7 @@ const MetricsTab = (props) => { }, []); return ( - <Container> - <Row> - <Col> - <h3>Last 30 Days:</h3> - </Col> - </Row> - <Row> - <Col><p>Received: {bytesToSize(metrics30.rx)}</p></Col> - <Col><p>Sent: {bytesToSize(metrics30.tx)}</p></Col> - </Row> - <Row> - <Col> - <ResponsiveContainer width={"100%"} height={150}> - <BarChart data={metrics30.data}> - <CartesianGrid strokeDasharay={"3 3"} /> - <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> - <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> - <Tooltip /> - </BarChart> - </ResponsiveContainer> - </Col> - </Row> - <Row> - <Col> - <h3>Last 7 Days:</h3> - </Col> - </Row> - <Row> - <Col><p>Received: {bytesToSize(metrics7.rx)}</p></Col> - <Col><p>Sent: {bytesToSize(metrics7.tx)}</p></Col> - </Row> - <Row> - <Col> - <ResponsiveContainer width={"100%"} height={150}> - <BarChart data={metrics7.data}> - <CartesianGrid strokeDasharay={"3 3"} /> - <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> - <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> - <Tooltip /> - </BarChart> - </ResponsiveContainer> - </Col> - </Row> - <Row> - <Col> - <h3>Last 24 Hours:</h3> - </Col> - </Row> - <Row> - <Col><p>Received: {bytesToSize(metrics1.rx)}</p></Col> - <Col><p>Sent: {bytesToSize(metrics1.tx)}</p></Col> - </Row> - <Row> - <Col> - <ResponsiveContainer width={"100%"} height={150}> - <BarChart data={metrics1.data}> - <CartesianGrid strokeDasharay={"3 3"} /> - <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> - <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> - <Tooltip /> - </BarChart> - </ResponsiveContainer> - </Col> - </Row> - </Container> + <MetricsView metrics30={metrics30} metrics7={metrics7} metrics1={metrics1} /> ); }; diff --git a/ui/src/console/detail/share/MetricsTab.js b/ui/src/console/detail/share/MetricsTab.js index 4f3b6126..e152ffbf 100644 --- a/ui/src/console/detail/share/MetricsTab.js +++ b/ui/src/console/detail/share/MetricsTab.js @@ -1,9 +1,7 @@ import React, {useEffect, useState} from "react"; import {buildMetrics, bytesToSize} from "../../metrics"; import * as metadata from "../../../api/metadata"; -import {Col, Container, Row, Tooltip} from "react-bootstrap"; -import {Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis} from "recharts"; -import moment from "moment"; +import MetricsView from "../../metrics/MetricsView"; const MetricsTab = (props) => { const [metrics30, setMetrics30] = useState(buildMetrics([])); @@ -52,77 +50,7 @@ const MetricsTab = (props) => { }, [props.share]); return ( - <Container> - <Row> - <Col> - <h3>Last 30 Days:</h3> - </Col> - </Row> - <Row> - <Col><p>Received: {bytesToSize(metrics30.rx)}</p></Col> - <Col><p>Sent: {bytesToSize(metrics30.tx)}</p></Col> - </Row> - <Row> - <Col> - <ResponsiveContainer width={"100%"} height={150}> - <BarChart data={metrics30.data}> - <CartesianGrid strokeDasharay={"3 3"} /> - <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> - <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> - <Tooltip /> - </BarChart> - </ResponsiveContainer> - </Col> - </Row> - <Row> - <Col> - <h3>Last 7 Days:</h3> - </Col> - </Row> - <Row> - <Col><p>Received: {bytesToSize(metrics7.rx)}</p></Col> - <Col><p>Sent: {bytesToSize(metrics7.tx)}</p></Col> - </Row> - <Row> - <Col> - <ResponsiveContainer width={"100%"} height={150}> - <BarChart data={metrics7.data}> - <CartesianGrid strokeDasharay={"3 3"} /> - <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> - <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> - <Tooltip /> - </BarChart> - </ResponsiveContainer> - </Col> - </Row> - <Row> - <Col> - <h3>Last 24 Hours:</h3> - </Col> - </Row> - <Row> - <Col><p>Received: {bytesToSize(metrics1.rx)}</p></Col> - <Col><p>Sent: {bytesToSize(metrics1.tx)}</p></Col> - </Row> - <Row> - <Col> - <ResponsiveContainer width={"100%"} height={150}> - <BarChart data={metrics1.data}> - <CartesianGrid strokeDasharay={"3 3"} /> - <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> - <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> - <Tooltip /> - </BarChart> - </ResponsiveContainer> - </Col> - </Row> - </Container> + <MetricsView metrics30={metrics30} metrics7={metrics7} metrics1={metrics1} /> ); } diff --git a/ui/src/console/metrics/MetricsView.js b/ui/src/console/metrics/MetricsView.js new file mode 100644 index 00000000..6454e3c1 --- /dev/null +++ b/ui/src/console/metrics/MetricsView.js @@ -0,0 +1,83 @@ +import {Col, Container, Row, Tooltip} from "react-bootstrap"; +import {bytesToSize} from "../metrics"; +import {Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis} from "recharts"; +import moment from "moment/moment"; +import React from "react"; + +const MetricsView = (props) => { + return ( + <Container> + <Row> + <Col> + <h3>Last 30 Days:</h3> + </Col> + </Row> + <Row> + <Col><p>Received: {bytesToSize(props.metrics30.rx)}</p></Col> + <Col><p>Sent: {bytesToSize(props.metrics30.tx)}</p></Col> + </Row> + <Row> + <Col> + <ResponsiveContainer width={"100%"} height={150}> + <BarChart data={props.metrics30.data}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Tooltip /> + </BarChart> + </ResponsiveContainer> + </Col> + </Row> + <Row> + <Col> + <h3>Last 7 Days:</h3> + </Col> + </Row> + <Row> + <Col><p>Received: {bytesToSize(props.metrics7.rx)}</p></Col> + <Col><p>Sent: {bytesToSize(props.metrics7.tx)}</p></Col> + </Row> + <Row> + <Col> + <ResponsiveContainer width={"100%"} height={150}> + <BarChart data={props.metrics7.data}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Tooltip /> + </BarChart> + </ResponsiveContainer> + </Col> + </Row> + <Row> + <Col> + <h3>Last 24 Hours:</h3> + </Col> + </Row> + <Row> + <Col><p>Received: {bytesToSize(props.metrics1.rx)}</p></Col> + <Col><p>Sent: {bytesToSize(props.metrics1.tx)}</p></Col> + </Row> + <Row> + <Col> + <ResponsiveContainer width={"100%"} height={150}> + <BarChart data={props.metrics1.data}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Tooltip /> + </BarChart> + </ResponsiveContainer> + </Col> + </Row> + </Container> + ); +} + +export default MetricsView; \ No newline at end of file From 761f595c337fbb291a48ecbd59bf70a8a61a6d5f Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 11 May 2023 11:58:09 -0400 Subject: [PATCH 21/49] cleaner metrics component design (#324) --- ui/src/console/detail/share/MetricsTab.js | 2 +- ui/src/console/metrics/MetricsView.js | 92 +++++++++-------------- 2 files changed, 37 insertions(+), 57 deletions(-) diff --git a/ui/src/console/detail/share/MetricsTab.js b/ui/src/console/detail/share/MetricsTab.js index e152ffbf..bf6f2454 100644 --- a/ui/src/console/detail/share/MetricsTab.js +++ b/ui/src/console/detail/share/MetricsTab.js @@ -1,5 +1,5 @@ import React, {useEffect, useState} from "react"; -import {buildMetrics, bytesToSize} from "../../metrics"; +import {buildMetrics} from "../../metrics"; import * as metadata from "../../../api/metadata"; import MetricsView from "../../metrics/MetricsView"; diff --git a/ui/src/console/metrics/MetricsView.js b/ui/src/console/metrics/MetricsView.js index 6454e3c1..9b71b5ac 100644 --- a/ui/src/console/metrics/MetricsView.js +++ b/ui/src/console/metrics/MetricsView.js @@ -4,7 +4,7 @@ import {Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis} from "r import moment from "moment/moment"; import React from "react"; -const MetricsView = (props) => { +const MetricsViews = (props) => { return ( <Container> <Row> @@ -12,72 +12,52 @@ const MetricsView = (props) => { <h3>Last 30 Days:</h3> </Col> </Row> - <Row> - <Col><p>Received: {bytesToSize(props.metrics30.rx)}</p></Col> - <Col><p>Sent: {bytesToSize(props.metrics30.tx)}</p></Col> - </Row> - <Row> - <Col> - <ResponsiveContainer width={"100%"} height={150}> - <BarChart data={props.metrics30.data}> - <CartesianGrid strokeDasharay={"3 3"} /> - <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> - <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> - <Tooltip /> - </BarChart> - </ResponsiveContainer> - </Col> - </Row> + <MetricsSummary metrics={props.metrics30} /> + <MetricsGraph metrics={props.metrics30} /> <Row> <Col> <h3>Last 7 Days:</h3> </Col> </Row> - <Row> - <Col><p>Received: {bytesToSize(props.metrics7.rx)}</p></Col> - <Col><p>Sent: {bytesToSize(props.metrics7.tx)}</p></Col> - </Row> - <Row> - <Col> - <ResponsiveContainer width={"100%"} height={150}> - <BarChart data={props.metrics7.data}> - <CartesianGrid strokeDasharay={"3 3"} /> - <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> - <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> - <Tooltip /> - </BarChart> - </ResponsiveContainer> - </Col> - </Row> + <MetricsSummary metrics={props.metrics7} /> + <MetricsGraph metrics={props.metrics7} /> <Row> <Col> <h3>Last 24 Hours:</h3> </Col> </Row> - <Row> - <Col><p>Received: {bytesToSize(props.metrics1.rx)}</p></Col> - <Col><p>Sent: {bytesToSize(props.metrics1.tx)}</p></Col> - </Row> - <Row> - <Col> - <ResponsiveContainer width={"100%"} height={150}> - <BarChart data={props.metrics1.data}> - <CartesianGrid strokeDasharay={"3 3"} /> - <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> - <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> - <Tooltip /> - </BarChart> - </ResponsiveContainer> - </Col> - </Row> + <MetricsSummary metrics={props.metrics1} /> + <MetricsGraph metrics={props.metrics1} /> </Container> ); } -export default MetricsView; \ No newline at end of file +const MetricsSummary = (props) => { + return ( + <Row> + <Col><p>Received: {bytesToSize(props.metrics.rx)}</p></Col> + <Col><p>Sent: {bytesToSize(props.metrics.tx)}</p></Col> + </Row> + ); +} + +const MetricsGraph = (props) => { + return ( + <Row> + <Col> + <ResponsiveContainer width={"100%"} height={150}> + <BarChart data={props.metrics.data}> + <CartesianGrid strokeDasharay={"3 3"} /> + <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> + <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} /> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Tooltip /> + </BarChart> + </ResponsiveContainer> + </Col> + </Row> + ); +} + +export default MetricsViews; \ No newline at end of file From 4c4f0c30f09f6d3edf351bc2ba65034765b1daf8 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 11 May 2023 12:02:01 -0400 Subject: [PATCH 22/49] polish (#324) --- ui/src/console/detail/account/AccountDetail.js | 2 +- ui/src/console/detail/environment/MetricsTab.js | 2 +- ui/src/console/detail/share/MetricsTab.js | 2 +- ui/src/console/metrics/MetricsView.js | 2 +- ui/src/console/{metrics.js => metrics/util.js} | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename ui/src/console/{metrics.js => metrics/util.js} (100%) diff --git a/ui/src/console/detail/account/AccountDetail.js b/ui/src/console/detail/account/AccountDetail.js index dec934b1..df96c801 100644 --- a/ui/src/console/detail/account/AccountDetail.js +++ b/ui/src/console/detail/account/AccountDetail.js @@ -5,7 +5,7 @@ import {Tab, Tabs} from "react-bootstrap"; import SecretToggle from "../../SecretToggle"; import React, {useEffect, useState} from "react"; import * as metadata from "../../../api/metadata"; -import {buildMetrics} from "../../metrics"; +import {buildMetrics} from "../../metrics/util"; import MetricsView from "../../metrics/MetricsView"; const AccountDetail = (props) => { diff --git a/ui/src/console/detail/environment/MetricsTab.js b/ui/src/console/detail/environment/MetricsTab.js index 23e6c1de..66e0463a 100644 --- a/ui/src/console/detail/environment/MetricsTab.js +++ b/ui/src/console/detail/environment/MetricsTab.js @@ -1,5 +1,5 @@ import React, {useEffect, useState} from "react"; -import {buildMetrics} from "../../metrics"; +import {buildMetrics} from "../../metrics/util"; import * as metadata from "../../../api/metadata"; import MetricsView from "../../metrics/MetricsView"; diff --git a/ui/src/console/detail/share/MetricsTab.js b/ui/src/console/detail/share/MetricsTab.js index bf6f2454..51143ffe 100644 --- a/ui/src/console/detail/share/MetricsTab.js +++ b/ui/src/console/detail/share/MetricsTab.js @@ -1,5 +1,5 @@ import React, {useEffect, useState} from "react"; -import {buildMetrics} from "../../metrics"; +import {buildMetrics} from "../../metrics/util"; import * as metadata from "../../../api/metadata"; import MetricsView from "../../metrics/MetricsView"; diff --git a/ui/src/console/metrics/MetricsView.js b/ui/src/console/metrics/MetricsView.js index 9b71b5ac..95b26f0c 100644 --- a/ui/src/console/metrics/MetricsView.js +++ b/ui/src/console/metrics/MetricsView.js @@ -1,5 +1,5 @@ import {Col, Container, Row, Tooltip} from "react-bootstrap"; -import {bytesToSize} from "../metrics"; +import {bytesToSize} from "./util"; import {Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis} from "recharts"; import moment from "moment/moment"; import React from "react"; diff --git a/ui/src/console/metrics.js b/ui/src/console/metrics/util.js similarity index 100% rename from ui/src/console/metrics.js rename to ui/src/console/metrics/util.js From bb2b7c3da7e3ff6a3b47159beb934a61a7a5f902 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 11 May 2023 15:21:10 -0400 Subject: [PATCH 23/49] new sparkline implementation (#325) --- controller/environmentDetail.go | 11 ++- controller/shareDetail.go | 11 ++- controller/sparkData.go | 56 +++++++------- rest_model_zrok/share.go | 28 +++---- rest_model_zrok/share_metrics.go | 50 ++++++++++++- rest_model_zrok/share_metrics_sample.go | 56 ++++++++++++++ rest_model_zrok/spark_data.go | 73 +++++++++++++++++++ rest_model_zrok/spark_data_sample.go | 53 ++++++++++++++ rest_server_zrok/embedded_spec.go | 58 ++++++++++----- specs/zrok.yml | 16 +++- ui/src/api/types.js | 10 ++- .../console/detail/environment/SharesTab.js | 5 +- ui/src/console/detail/share/ShareDetail.js | 7 +- 13 files changed, 358 insertions(+), 76 deletions(-) create mode 100644 rest_model_zrok/share_metrics_sample.go create mode 100644 rest_model_zrok/spark_data.go create mode 100644 rest_model_zrok/spark_data_sample.go diff --git a/controller/environmentDetail.go b/controller/environmentDetail.go index 145947c4..604eea41 100644 --- a/controller/environmentDetail.go +++ b/controller/environmentDetail.go @@ -40,9 +40,10 @@ func (h *environmentDetailHandler) Handle(params metadata.GetEnvironmentDetailPa logrus.Errorf("error finding shares for environment '%v' for user '%v': %v", senv.ZId, principal.Email, err) return metadata.NewGetEnvironmentDetailInternalServerError() } - var sparkData map[string][]int64 + sparkRx := make(map[string][]int64) + sparkTx := make(map[string][]int64) if cfg.Metrics != nil && cfg.Metrics.Influx != nil { - sparkData, err = sparkDataForShares(shrs) + sparkRx, sparkTx, err = sparkDataForShares(shrs) if err != nil { logrus.Errorf("error querying spark data for shares for user '%v': %v", principal.Email, err) } @@ -62,6 +63,10 @@ func (h *environmentDetailHandler) Handle(params metadata.GetEnvironmentDetailPa if shr.BackendProxyEndpoint != nil { beProxyEndpoint = *shr.BackendProxyEndpoint } + var sparkData []*rest_model_zrok.SparkDataSample + for i := 0; i < len(sparkRx[shr.Token]) && i < len(sparkTx[shr.Token]); i++ { + sparkData = append(sparkData, &rest_model_zrok.SparkDataSample{Rx: float64(sparkRx[shr.Token][i]), Tx: float64(sparkTx[shr.Token][i])}) + } es.Shares = append(es.Shares, &rest_model_zrok.Share{ Token: shr.Token, ZID: shr.ZId, @@ -71,7 +76,7 @@ func (h *environmentDetailHandler) Handle(params metadata.GetEnvironmentDetailPa FrontendEndpoint: feEndpoint, BackendProxyEndpoint: beProxyEndpoint, Reserved: shr.Reserved, - Metrics: sparkData[shr.Token], + SparkData: sparkData, CreatedAt: shr.CreatedAt.UnixMilli(), UpdatedAt: shr.UpdatedAt.UnixMilli(), }) diff --git a/controller/shareDetail.go b/controller/shareDetail.go index cc018d1f..43b0020b 100644 --- a/controller/shareDetail.go +++ b/controller/shareDetail.go @@ -42,9 +42,10 @@ func (h *shareDetailHandler) Handle(params metadata.GetShareDetailParams, princi logrus.Errorf("environment not matched for share '%v' for account '%v'", params.ShrToken, principal.Email) return metadata.NewGetShareDetailNotFound() } - var sparkData map[string][]int64 + sparkRx := make(map[string][]int64) + sparkTx := make(map[string][]int64) if cfg.Metrics != nil && cfg.Metrics.Influx != nil { - sparkData, err = sparkDataForShares([]*store.Share{shr}) + sparkRx, sparkTx, err = sparkDataForShares([]*store.Share{shr}) if err != nil { logrus.Errorf("error querying spark data for share: %v", err) } @@ -63,6 +64,10 @@ func (h *shareDetailHandler) Handle(params metadata.GetShareDetailParams, princi if shr.BackendProxyEndpoint != nil { beProxyEndpoint = *shr.BackendProxyEndpoint } + var sparkData []*rest_model_zrok.SparkDataSample + for i := 0; i < len(sparkRx[shr.Token]) && i < len(sparkTx[shr.Token]); i++ { + sparkData = append(sparkData, &rest_model_zrok.SparkDataSample{Rx: float64(sparkRx[shr.Token][i]), Tx: float64(sparkTx[shr.Token][i])}) + } return metadata.NewGetShareDetailOK().WithPayload(&rest_model_zrok.Share{ Token: shr.Token, ZID: shr.ZId, @@ -72,7 +77,7 @@ func (h *shareDetailHandler) Handle(params metadata.GetShareDetailParams, princi FrontendEndpoint: feEndpoint, BackendProxyEndpoint: beProxyEndpoint, Reserved: shr.Reserved, - Metrics: sparkData[shr.Token], + SparkData: sparkData, CreatedAt: shr.CreatedAt.UnixMilli(), UpdatedAt: shr.UpdatedAt.UnixMilli(), }) diff --git a/controller/sparkData.go b/controller/sparkData.go index 02b8b35d..c68107e9 100644 --- a/controller/sparkData.go +++ b/controller/sparkData.go @@ -6,37 +6,43 @@ import ( "github.com/openziti/zrok/controller/store" ) -func sparkDataForShares(shrs []*store.Share) (map[string][]int64, error) { - out := make(map[string][]int64) - +func sparkDataForShares(shrs []*store.Share) (rx, tx map[string][]int64, err error) { + rx = make(map[string][]int64) + tx = make(map[string][]int64) if len(shrs) > 0 { qapi := idb.QueryAPI(cfg.Metrics.Influx.Org) - result, err := qapi.Query(context.Background(), sparkFluxQuery(shrs)) + query := sparkFluxQuery(shrs, cfg.Metrics.Influx.Bucket) + result, err := qapi.Query(context.Background(), query) if err != nil { - return nil, err + return nil, nil, err } for result.Next() { - combinedRate := int64(0) - readRate := result.Record().ValueByKey("tx") - if readRate != nil { - combinedRate += readRate.(int64) - } - writeRate := result.Record().ValueByKey("tx") - if writeRate != nil { - combinedRate += writeRate.(int64) - } shrToken := result.Record().ValueByKey("share").(string) - shrMetrics := out[shrToken] - shrMetrics = append(shrMetrics, combinedRate) - out[shrToken] = shrMetrics + switch result.Record().Field() { + case "rx": + rxV := int64(0) + if v, ok := result.Record().Value().(int64); ok { + rxV = v + } + rxData := append(rx[shrToken], rxV) + rx[shrToken] = rxData + + case "tx": + txV := int64(0) + if v, ok := result.Record().Value().(int64); ok { + txV = v + } + txData := append(tx[shrToken], txV) + tx[shrToken] = txData + } } } - return out, nil + return rx, tx, nil } -func sparkFluxQuery(shrs []*store.Share) string { +func sparkFluxQuery(shrs []*store.Share, bucket string) string { shrFilter := "|> filter(fn: (r) =>" for i, shr := range shrs { if i > 0 { @@ -45,14 +51,12 @@ func sparkFluxQuery(shrs []*store.Share) string { shrFilter += fmt.Sprintf(" r[\"share\"] == \"%v\"", shr.Token) } shrFilter += ")" - query := "read = from(bucket: \"zrok\")" + - "|> range(start: -5m)" + - "|> filter(fn: (r) => r[\"_measurement\"] == \"xfer\")" + - "|> filter(fn: (r) => r[\"_field\"] == \"rx\" or r[\"_field\"] == \"tx\")" + + query := fmt.Sprintf("from(bucket: \"%v\")\n", bucket) + + "|> range(start: -5m)\n" + + "|> filter(fn: (r) => r[\"_measurement\"] == \"xfer\")\n" + + "|> filter(fn: (r) => r[\"_field\"] == \"rx\" or r[\"_field\"] == \"tx\")\n" + "|> filter(fn: (r) => r[\"namespace\"] == \"backend\")" + shrFilter + - "|> aggregateWindow(every: 5s, fn: sum, createEmpty: true)\n" + - "|> pivot(rowKey:[\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")" + - "|> yield(name: \"last\")" + "|> aggregateWindow(every: 10s, fn: sum, createEmpty: true)\n" return query } diff --git a/rest_model_zrok/share.go b/rest_model_zrok/share.go index 7156fbb2..16563a1f 100644 --- a/rest_model_zrok/share.go +++ b/rest_model_zrok/share.go @@ -33,15 +33,15 @@ type Share struct { // frontend selection FrontendSelection string `json:"frontendSelection,omitempty"` - // metrics - Metrics ShareMetrics `json:"metrics,omitempty"` - // reserved Reserved bool `json:"reserved,omitempty"` // share mode ShareMode string `json:"shareMode,omitempty"` + // spark data + SparkData SparkData `json:"sparkData,omitempty"` + // token Token string `json:"token,omitempty"` @@ -56,7 +56,7 @@ type Share struct { func (m *Share) Validate(formats strfmt.Registry) error { var res []error - if err := m.validateMetrics(formats); err != nil { + if err := m.validateSparkData(formats); err != nil { res = append(res, err) } @@ -66,16 +66,16 @@ func (m *Share) Validate(formats strfmt.Registry) error { return nil } -func (m *Share) validateMetrics(formats strfmt.Registry) error { - if swag.IsZero(m.Metrics) { // not required +func (m *Share) validateSparkData(formats strfmt.Registry) error { + if swag.IsZero(m.SparkData) { // not required return nil } - if err := m.Metrics.Validate(formats); err != nil { + if err := m.SparkData.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("metrics") + return ve.ValidateName("sparkData") } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("metrics") + return ce.ValidateName("sparkData") } return err } @@ -87,7 +87,7 @@ func (m *Share) validateMetrics(formats strfmt.Registry) error { func (m *Share) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error - if err := m.contextValidateMetrics(ctx, formats); err != nil { + if err := m.contextValidateSparkData(ctx, formats); err != nil { res = append(res, err) } @@ -97,13 +97,13 @@ func (m *Share) ContextValidate(ctx context.Context, formats strfmt.Registry) er return nil } -func (m *Share) contextValidateMetrics(ctx context.Context, formats strfmt.Registry) error { +func (m *Share) contextValidateSparkData(ctx context.Context, formats strfmt.Registry) error { - if err := m.Metrics.ContextValidate(ctx, formats); err != nil { + if err := m.SparkData.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("metrics") + return ve.ValidateName("sparkData") } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("metrics") + return ce.ValidateName("sparkData") } return err } diff --git a/rest_model_zrok/share_metrics.go b/rest_model_zrok/share_metrics.go index 7fd5aafb..e7a109eb 100644 --- a/rest_model_zrok/share_metrics.go +++ b/rest_model_zrok/share_metrics.go @@ -7,21 +7,67 @@ package rest_model_zrok import ( "context" + "strconv" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) // ShareMetrics share metrics // // swagger:model shareMetrics -type ShareMetrics []int64 +type ShareMetrics []*ShareMetricsSample // Validate validates this share metrics func (m ShareMetrics) Validate(formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + if swag.IsZero(m[i]) { // not required + continue + } + + if m[i] != nil { + if err := m[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } return nil } -// ContextValidate validates this share metrics based on context it is used +// ContextValidate validate this share metrics based on the context it is used func (m ShareMetrics) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } return nil } diff --git a/rest_model_zrok/share_metrics_sample.go b/rest_model_zrok/share_metrics_sample.go new file mode 100644 index 00000000..bc725aad --- /dev/null +++ b/rest_model_zrok/share_metrics_sample.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package rest_model_zrok + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// ShareMetricsSample share metrics sample +// +// swagger:model shareMetricsSample +type ShareMetricsSample struct { + + // rx + Rx float64 `json:"rx,omitempty"` + + // timestamp + Timestamp float64 `json:"timestamp,omitempty"` + + // tx + Tx float64 `json:"tx,omitempty"` +} + +// Validate validates this share metrics sample +func (m *ShareMetricsSample) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this share metrics sample based on context it is used +func (m *ShareMetricsSample) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *ShareMetricsSample) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ShareMetricsSample) UnmarshalBinary(b []byte) error { + var res ShareMetricsSample + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/rest_model_zrok/spark_data.go b/rest_model_zrok/spark_data.go new file mode 100644 index 00000000..1d0f6635 --- /dev/null +++ b/rest_model_zrok/spark_data.go @@ -0,0 +1,73 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package rest_model_zrok + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// SparkData spark data +// +// swagger:model sparkData +type SparkData []*SparkDataSample + +// Validate validates this spark data +func (m SparkData) Validate(formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + if swag.IsZero(m[i]) { // not required + continue + } + + if m[i] != nil { + if err := m[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this spark data based on the context it is used +func (m SparkData) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_model_zrok/spark_data_sample.go b/rest_model_zrok/spark_data_sample.go new file mode 100644 index 00000000..75ba458d --- /dev/null +++ b/rest_model_zrok/spark_data_sample.go @@ -0,0 +1,53 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package rest_model_zrok + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// SparkDataSample spark data sample +// +// swagger:model sparkDataSample +type SparkDataSample struct { + + // rx + Rx float64 `json:"rx,omitempty"` + + // tx + Tx float64 `json:"tx,omitempty"` +} + +// Validate validates this spark data sample +func (m *SparkDataSample) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this spark data sample based on context it is used +func (m *SparkDataSample) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *SparkDataSample) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *SparkDataSample) UnmarshalBinary(b []byte) error { + var res SparkDataSample + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index fc9e8858..30b8845f 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -1281,15 +1281,15 @@ func init() { "frontendSelection": { "type": "string" }, - "metrics": { - "$ref": "#/definitions/shareMetrics" - }, "reserved": { "type": "boolean" }, "shareMode": { "type": "string" }, + "sparkData": { + "$ref": "#/definitions/sparkData" + }, "token": { "type": "string" }, @@ -1301,12 +1301,6 @@ func init() { } } }, - "shareMetrics": { - "type": "array", - "items": { - "type": "integer" - } - }, "shareRequest": { "type": "object", "properties": { @@ -1372,6 +1366,23 @@ func init() { "$ref": "#/definitions/share" } }, + "sparkData": { + "type": "array", + "items": { + "$ref": "#/definitions/sparkDataSample" + } + }, + "sparkDataSample": { + "type": "object", + "properties": { + "rx": { + "type": "number" + }, + "tx": { + "type": "number" + } + } + }, "unaccessRequest": { "type": "object", "properties": { @@ -2717,15 +2728,15 @@ func init() { "frontendSelection": { "type": "string" }, - "metrics": { - "$ref": "#/definitions/shareMetrics" - }, "reserved": { "type": "boolean" }, "shareMode": { "type": "string" }, + "sparkData": { + "$ref": "#/definitions/sparkData" + }, "token": { "type": "string" }, @@ -2737,12 +2748,6 @@ func init() { } } }, - "shareMetrics": { - "type": "array", - "items": { - "type": "integer" - } - }, "shareRequest": { "type": "object", "properties": { @@ -2808,6 +2813,23 @@ func init() { "$ref": "#/definitions/share" } }, + "sparkData": { + "type": "array", + "items": { + "$ref": "#/definitions/sparkDataSample" + } + }, + "sparkDataSample": { + "type": "object", + "properties": { + "rx": { + "type": "number" + }, + "tx": { + "type": "number" + } + } + }, "unaccessRequest": { "type": "object", "properties": { diff --git a/specs/zrok.yml b/specs/zrok.yml index d83417e0..a8df51fe 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -844,8 +844,8 @@ definitions: type: string reserved: type: boolean - metrics: - $ref: "#/definitions/shareMetrics" + sparkData: + $ref: "#/definitions/sparkData" createdAt: type: integer updatedAt: @@ -856,10 +856,18 @@ definitions: items: $ref: "#/definitions/share" - shareMetrics: + sparkData: type: array items: - type: integer + $ref: "#/definitions/sparkDataSample" + + sparkDataSample: + type: object + properties: + rx: + type: number + tx: + type: number shareRequest: type: object diff --git a/ui/src/api/types.js b/ui/src/api/types.js index 01c83b83..50548bb4 100644 --- a/ui/src/api/types.js +++ b/ui/src/api/types.js @@ -200,11 +200,19 @@ * @property {string} frontendEndpoint * @property {string} backendProxyEndpoint * @property {boolean} reserved - * @property {module:types.shareMetrics} metrics + * @property {module:types.sparkData} sparkData * @property {number} createdAt * @property {number} updatedAt */ +/** + * @typedef sparkDataSample + * @memberof module:types + * + * @property {number} rx + * @property {number} tx + */ + /** * @typedef shareRequest * @memberof module:types diff --git a/ui/src/console/detail/environment/SharesTab.js b/ui/src/console/detail/environment/SharesTab.js index 6fb01439..5fabe9d8 100644 --- a/ui/src/console/detail/environment/SharesTab.js +++ b/ui/src/console/detail/environment/SharesTab.js @@ -45,8 +45,9 @@ const SharesTab = (props) => { name: "Activity", cell: row => { return <ResponsiveContainer width={"100%"} height={"100%"}> - <AreaChart data={row.metrics}> - <Area type="basis" dataKey={(v) => v} stroke={"#777"} fillOpacity={0.5} fill={"#04adef"} isAnimationActive={false} dot={false} /> + <AreaChart data={row.sparkData}> + <Area type={"linear"} dataKey={(v) => v.rx ? v.rx : 0} stroke={"#231069"} fill={"#04adef"} isAnimationActive={false} dot={false} /> + <Area type={"linear"} dataKey={(v) => v.tx ? v.tx * -1 : 0} stroke={"#231069"} fill={"#9BF316"} isAnimationActive={false} dot={false} /> </AreaChart> </ResponsiveContainer> } diff --git a/ui/src/console/detail/share/ShareDetail.js b/ui/src/console/detail/share/ShareDetail.js index c841af7f..c82bdaff 100644 --- a/ui/src/console/detail/share/ShareDetail.js +++ b/ui/src/console/detail/share/ShareDetail.js @@ -6,7 +6,7 @@ import PropertyTable from "../../PropertyTable"; import {Tab, Tabs} from "react-bootstrap"; import ActionsTab from "./ActionsTab"; import SecretToggle from "../../SecretToggle"; -import {Area, AreaChart, Line, LineChart, ResponsiveContainer, XAxis} from "recharts"; +import {Area, AreaChart, ResponsiveContainer} from "recharts"; import MetricsTab from "./MetricsTab"; const ShareDetail = (props) => { @@ -40,10 +40,11 @@ const ShareDetail = (props) => { }, [props.selection]); const customProperties = { - metrics: row => ( + sparkData: row => ( <ResponsiveContainer width={"100%"} height={"100%"}> <AreaChart data={row.value}> - <Area type="basis" dataKey={(v) => v} stroke={"#777"} fillOpacity={0.5} fill={"#04adef"} isAnimationActive={false} dot={false} /> + <Area type={"basis"} dataKey={(v) => v.rx ? v.rx : 0} stroke={"#231069"} fill={"#04adef"} isAnimationActive={false} dot={false} /> + <Area type={"basis"} dataKey={(v) => v.tx ? v.tx * -1 : 0} stroke={"#231069"} fill={"#9BF316"} isAnimationActive={false} dot={false} /> </AreaChart> </ResponsiveContainer> ), From 4341f60ce61e827ee62a9a5feeb40780d36cceb8 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 11 May 2023 15:22:21 -0400 Subject: [PATCH 24/49] basis, oops (#325) --- ui/src/console/detail/environment/SharesTab.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/console/detail/environment/SharesTab.js b/ui/src/console/detail/environment/SharesTab.js index 5fabe9d8..21c9c48a 100644 --- a/ui/src/console/detail/environment/SharesTab.js +++ b/ui/src/console/detail/environment/SharesTab.js @@ -46,8 +46,8 @@ const SharesTab = (props) => { cell: row => { return <ResponsiveContainer width={"100%"} height={"100%"}> <AreaChart data={row.sparkData}> - <Area type={"linear"} dataKey={(v) => v.rx ? v.rx : 0} stroke={"#231069"} fill={"#04adef"} isAnimationActive={false} dot={false} /> - <Area type={"linear"} dataKey={(v) => v.tx ? v.tx * -1 : 0} stroke={"#231069"} fill={"#9BF316"} isAnimationActive={false} dot={false} /> + <Area type={"basis"} dataKey={(v) => v.rx ? v.rx : 0} stroke={"#231069"} fill={"#04adef"} isAnimationActive={false} dot={false} /> + <Area type={"basis"} dataKey={(v) => v.tx ? v.tx * -1 : 0} stroke={"#231069"} fill={"#9BF316"} isAnimationActive={false} dot={false} /> </AreaChart> </ResponsiveContainer> } From 7f4517963bcda6e485fcd342b10e5ac5dce7b349 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 11 May 2023 15:25:01 -0400 Subject: [PATCH 25/49] metrics tab; not the default (#324) --- ui/src/console/detail/account/AccountDetail.js | 8 ++++---- ui/src/console/detail/environment/EnvironmentDetail.js | 6 +++--- ui/src/console/detail/share/ShareDetail.js | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ui/src/console/detail/account/AccountDetail.js b/ui/src/console/detail/account/AccountDetail.js index df96c801..169984ed 100644 --- a/ui/src/console/detail/account/AccountDetail.js +++ b/ui/src/console/detail/account/AccountDetail.js @@ -16,13 +16,13 @@ const AccountDetail = (props) => { return ( <div> <h2><Icon path={mdiAccountBox} size={2} />{" "}{props.user.email}</h2> - <Tabs defaultActiveKey={"metrics"}> - <Tab eventKey={"metrics"} title={"Metrics"}> - <MetricsTab /> - </Tab> + <Tabs defaultActiveKey={"detail"}> <Tab eventKey={"detail"} title={"Detail"}> <PropertyTable object={props.user} custom={customProperties}/> </Tab> + <Tab eventKey={"metrics"} title={"Metrics"}> + <MetricsTab /> + </Tab> </Tabs> </div> ); diff --git a/ui/src/console/detail/environment/EnvironmentDetail.js b/ui/src/console/detail/environment/EnvironmentDetail.js index e3861f53..25526520 100644 --- a/ui/src/console/detail/environment/EnvironmentDetail.js +++ b/ui/src/console/detail/environment/EnvironmentDetail.js @@ -26,12 +26,12 @@ const EnvironmentDetail = (props) => { <Tab eventKey={"shares"} title={"Shares"}> <SharesTab selection={props.selection} /> </Tab> - <Tab eventKey={"metrics"} title={"Metrics"}> - <MetricsTab selection={props.selection} /> - </Tab> <Tab eventKey={"detail"} title={"Detail"}> <DetailTab environment={detail.environment} /> </Tab> + <Tab eventKey={"metrics"} title={"Metrics"}> + <MetricsTab selection={props.selection} /> + </Tab> <Tab eventKey={"actions"} title={"Actions"}> <ActionsTab environment={detail.environment} /> </Tab> diff --git a/ui/src/console/detail/share/ShareDetail.js b/ui/src/console/detail/share/ShareDetail.js index c82bdaff..743418a6 100644 --- a/ui/src/console/detail/share/ShareDetail.js +++ b/ui/src/console/detail/share/ShareDetail.js @@ -64,13 +64,13 @@ const ShareDetail = (props) => { return ( <div> <h2><Icon path={mdiShareVariant} size={2} />{" "}{detail.backendProxyEndpoint}</h2> - <Tabs defaultActiveKey={"metrics"}> - <Tab eventKey={"metrics"} title={"Metrics"}> - <MetricsTab share={detail} /> - </Tab> + <Tabs defaultActiveKey={"detail"}> <Tab eventKey={"detail"} title={"Detail"}> <PropertyTable object={detail} custom={customProperties} /> </Tab> + <Tab eventKey={"metrics"} title={"Metrics"}> + <MetricsTab share={detail} /> + </Tab> <Tab eventKey={"actions"} title={"Actions"}> <ActionsTab share={detail} /> </Tab> From d0a565d76985d5af483a70d8b53fb307db765740 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 11 May 2023 15:38:18 -0400 Subject: [PATCH 26/49] better environment/shares tab responsive sizing (#234) --- ui/src/console/detail/environment/SharesTab.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/src/console/detail/environment/SharesTab.js b/ui/src/console/detail/environment/SharesTab.js index 21c9c48a..d7293eef 100644 --- a/ui/src/console/detail/environment/SharesTab.js +++ b/ui/src/console/detail/environment/SharesTab.js @@ -37,12 +37,14 @@ const SharesTab = (props) => { }, { name: "Backend", + grow: 0.5, selector: row => row.backendProxyEndpoint, sortable: true, - hide: "md" + hide: "lg" }, { name: "Activity", + grow: 0.5, cell: row => { return <ResponsiveContainer width={"100%"} height={"100%"}> <AreaChart data={row.sparkData}> From d1688c450dc07c24866299817adb904f24e93462 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 11 May 2023 15:41:21 -0400 Subject: [PATCH 27/49] 'activity' (#234) --- controller/environmentDetail.go | 2 +- controller/shareDetail.go | 2 +- rest_model_zrok/share.go | 28 +++++++++---------- rest_server_zrok/embedded_spec.go | 12 ++++---- specs/zrok.yml | 2 +- ui/src/api/types.js | 2 +- .../console/detail/environment/SharesTab.js | 2 +- ui/src/console/detail/share/ShareDetail.js | 2 +- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/controller/environmentDetail.go b/controller/environmentDetail.go index 604eea41..7b31a33d 100644 --- a/controller/environmentDetail.go +++ b/controller/environmentDetail.go @@ -76,7 +76,7 @@ func (h *environmentDetailHandler) Handle(params metadata.GetEnvironmentDetailPa FrontendEndpoint: feEndpoint, BackendProxyEndpoint: beProxyEndpoint, Reserved: shr.Reserved, - SparkData: sparkData, + Activity: sparkData, CreatedAt: shr.CreatedAt.UnixMilli(), UpdatedAt: shr.UpdatedAt.UnixMilli(), }) diff --git a/controller/shareDetail.go b/controller/shareDetail.go index 43b0020b..096f67f3 100644 --- a/controller/shareDetail.go +++ b/controller/shareDetail.go @@ -77,7 +77,7 @@ func (h *shareDetailHandler) Handle(params metadata.GetShareDetailParams, princi FrontendEndpoint: feEndpoint, BackendProxyEndpoint: beProxyEndpoint, Reserved: shr.Reserved, - SparkData: sparkData, + Activity: sparkData, CreatedAt: shr.CreatedAt.UnixMilli(), UpdatedAt: shr.UpdatedAt.UnixMilli(), }) diff --git a/rest_model_zrok/share.go b/rest_model_zrok/share.go index 16563a1f..983c11c6 100644 --- a/rest_model_zrok/share.go +++ b/rest_model_zrok/share.go @@ -18,6 +18,9 @@ import ( // swagger:model share type Share struct { + // activity + Activity SparkData `json:"activity,omitempty"` + // backend mode BackendMode string `json:"backendMode,omitempty"` @@ -39,9 +42,6 @@ type Share struct { // share mode ShareMode string `json:"shareMode,omitempty"` - // spark data - SparkData SparkData `json:"sparkData,omitempty"` - // token Token string `json:"token,omitempty"` @@ -56,7 +56,7 @@ type Share struct { func (m *Share) Validate(formats strfmt.Registry) error { var res []error - if err := m.validateSparkData(formats); err != nil { + if err := m.validateActivity(formats); err != nil { res = append(res, err) } @@ -66,16 +66,16 @@ func (m *Share) Validate(formats strfmt.Registry) error { return nil } -func (m *Share) validateSparkData(formats strfmt.Registry) error { - if swag.IsZero(m.SparkData) { // not required +func (m *Share) validateActivity(formats strfmt.Registry) error { + if swag.IsZero(m.Activity) { // not required return nil } - if err := m.SparkData.Validate(formats); err != nil { + if err := m.Activity.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("sparkData") + return ve.ValidateName("activity") } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("sparkData") + return ce.ValidateName("activity") } return err } @@ -87,7 +87,7 @@ func (m *Share) validateSparkData(formats strfmt.Registry) error { func (m *Share) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error - if err := m.contextValidateSparkData(ctx, formats); err != nil { + if err := m.contextValidateActivity(ctx, formats); err != nil { res = append(res, err) } @@ -97,13 +97,13 @@ func (m *Share) ContextValidate(ctx context.Context, formats strfmt.Registry) er return nil } -func (m *Share) contextValidateSparkData(ctx context.Context, formats strfmt.Registry) error { +func (m *Share) contextValidateActivity(ctx context.Context, formats strfmt.Registry) error { - if err := m.SparkData.ContextValidate(ctx, formats); err != nil { + if err := m.Activity.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("sparkData") + return ve.ValidateName("activity") } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("sparkData") + return ce.ValidateName("activity") } return err } diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 30b8845f..d41988ff 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -1266,6 +1266,9 @@ func init() { "share": { "type": "object", "properties": { + "activity": { + "$ref": "#/definitions/sparkData" + }, "backendMode": { "type": "string" }, @@ -1287,9 +1290,6 @@ func init() { "shareMode": { "type": "string" }, - "sparkData": { - "$ref": "#/definitions/sparkData" - }, "token": { "type": "string" }, @@ -2713,6 +2713,9 @@ func init() { "share": { "type": "object", "properties": { + "activity": { + "$ref": "#/definitions/sparkData" + }, "backendMode": { "type": "string" }, @@ -2734,9 +2737,6 @@ func init() { "shareMode": { "type": "string" }, - "sparkData": { - "$ref": "#/definitions/sparkData" - }, "token": { "type": "string" }, diff --git a/specs/zrok.yml b/specs/zrok.yml index a8df51fe..39fe9033 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -844,7 +844,7 @@ definitions: type: string reserved: type: boolean - sparkData: + activity: $ref: "#/definitions/sparkData" createdAt: type: integer diff --git a/ui/src/api/types.js b/ui/src/api/types.js index 50548bb4..09093d0e 100644 --- a/ui/src/api/types.js +++ b/ui/src/api/types.js @@ -200,7 +200,7 @@ * @property {string} frontendEndpoint * @property {string} backendProxyEndpoint * @property {boolean} reserved - * @property {module:types.sparkData} sparkData + * @property {module:types.sparkData} activity * @property {number} createdAt * @property {number} updatedAt */ diff --git a/ui/src/console/detail/environment/SharesTab.js b/ui/src/console/detail/environment/SharesTab.js index d7293eef..a0926bce 100644 --- a/ui/src/console/detail/environment/SharesTab.js +++ b/ui/src/console/detail/environment/SharesTab.js @@ -47,7 +47,7 @@ const SharesTab = (props) => { grow: 0.5, cell: row => { return <ResponsiveContainer width={"100%"} height={"100%"}> - <AreaChart data={row.sparkData}> + <AreaChart data={row.activity}> <Area type={"basis"} dataKey={(v) => v.rx ? v.rx : 0} stroke={"#231069"} fill={"#04adef"} isAnimationActive={false} dot={false} /> <Area type={"basis"} dataKey={(v) => v.tx ? v.tx * -1 : 0} stroke={"#231069"} fill={"#9BF316"} isAnimationActive={false} dot={false} /> </AreaChart> diff --git a/ui/src/console/detail/share/ShareDetail.js b/ui/src/console/detail/share/ShareDetail.js index 743418a6..b942cd3e 100644 --- a/ui/src/console/detail/share/ShareDetail.js +++ b/ui/src/console/detail/share/ShareDetail.js @@ -40,7 +40,7 @@ const ShareDetail = (props) => { }, [props.selection]); const customProperties = { - sparkData: row => ( + activity: row => ( <ResponsiveContainer width={"100%"} height={"100%"}> <AreaChart data={row.value}> <Area type={"basis"} dataKey={(v) => v.rx ? v.rx : 0} stroke={"#231069"} fill={"#04adef"} isAnimationActive={false} dot={false} /> From ce4eac8e4c55937d6f1dadfa5b1388a2423dcc8a Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 11 May 2023 15:56:29 -0400 Subject: [PATCH 28/49] metrics improvements (#234) --- controller/metrics.go | 25 ++++++++++++++++--------- ui/src/console/metrics/MetricsView.js | 4 ++-- ui/src/console/metrics/util.js | 5 +++-- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/controller/metrics.go b/controller/metrics.go index 81780e57..f31f4657 100644 --- a/controller/metrics.go +++ b/controller/metrics.go @@ -227,14 +227,21 @@ func runFluxForRxTxArray(query string, queryApi api.QueryAPI) (rx, tx, timestamp return nil, nil, nil, err } for result.Next() { - if v, ok := result.Record().Value().(int64); ok { - switch result.Record().Field() { - case "rx": - rx = append(rx, float64(v)) - timestamps = append(timestamps, float64(result.Record().Time().UnixMilli())) - case "tx": - tx = append(tx, float64(v)) + switch result.Record().Field() { + case "rx": + rxV := int64(0) + if v, ok := result.Record().Value().(int64); ok { + rxV = v } + rx = append(rx, float64(rxV)) + timestamps = append(timestamps, float64(result.Record().Time().UnixMilli())) + + case "tx": + txV := int64(0) + if v, ok := result.Record().Value().(int64); ok { + txV = v + } + tx = append(tx, float64(txV)) } } return rx, tx, timestamps, nil @@ -245,9 +252,9 @@ func sliceSize(duration time.Duration) time.Duration { case 30 * 24 * time.Hour: return 24 * time.Hour case 7 * 24 * time.Hour: - return 20 * time.Minute + return 4 * time.Hour case 24 * time.Hour: - return 5 * time.Minute + return 30 * time.Minute default: return duration } diff --git a/ui/src/console/metrics/MetricsView.js b/ui/src/console/metrics/MetricsView.js index 95b26f0c..a34b56f2 100644 --- a/ui/src/console/metrics/MetricsView.js +++ b/ui/src/console/metrics/MetricsView.js @@ -50,8 +50,8 @@ const MetricsGraph = (props) => { <CartesianGrid strokeDasharay={"3 3"} /> <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} /> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} /> + <Bar stroke={"#231069"} fill={"#04adef"} dataKey={(v) => v.rx ? v.rx : 0} /> + <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={(v) => v.tx ? v.tx : 0} /> <Tooltip /> </BarChart> </ResponsiveContainer> diff --git a/ui/src/console/metrics/util.js b/ui/src/console/metrics/util.js index b316a14a..5c4e292f 100644 --- a/ui/src/console/metrics/util.js +++ b/ui/src/console/metrics/util.js @@ -1,4 +1,5 @@ export const buildMetrics = (m) => { + console.log("build", m); let metrics = { data: m.samples, rx: 0, @@ -6,8 +7,8 @@ export const buildMetrics = (m) => { } if(m.samples) { m.samples.forEach(s => { - metrics.rx += s.rx; - metrics.tx += s.tx; + metrics.rx += s.rx ? s.rx : 0; + metrics.tx += s.tx ? s.tx : 0; }); } return metrics; From 714b0b11d13ec42224d4fbc494759b90645196d2 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 11 May 2023 16:18:22 -0400 Subject: [PATCH 29/49] lint --- ui/src/console/detail/environment/MetricsTab.js | 4 ++-- ui/src/console/metrics/MetricsView.js | 13 ++++++------- ui/src/console/visualizer/Network.js | 2 ++ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ui/src/console/detail/environment/MetricsTab.js b/ui/src/console/detail/environment/MetricsTab.js index 66e0463a..37e447a0 100644 --- a/ui/src/console/detail/environment/MetricsTab.js +++ b/ui/src/console/detail/environment/MetricsTab.js @@ -21,7 +21,7 @@ const MetricsTab = (props) => { .then(resp => { setMetrics1(buildMetrics(resp.data)); }); - }, []); + }, [props.selection.id]); useEffect(() => { let mounted = true; @@ -45,7 +45,7 @@ const MetricsTab = (props) => { mounted = false; clearInterval(interval); } - }, []); + }, [props.selection.id]); return ( <MetricsView metrics30={metrics30} metrics7={metrics7} metrics1={metrics1} /> diff --git a/ui/src/console/metrics/MetricsView.js b/ui/src/console/metrics/MetricsView.js index a34b56f2..5085f878 100644 --- a/ui/src/console/metrics/MetricsView.js +++ b/ui/src/console/metrics/MetricsView.js @@ -1,6 +1,6 @@ -import {Col, Container, Row, Tooltip} from "react-bootstrap"; +import {Col, Container, Row} from "react-bootstrap"; import {bytesToSize} from "./util"; -import {Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis} from "recharts"; +import {Area, AreaChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis} from "recharts"; import moment from "moment/moment"; import React from "react"; @@ -46,14 +46,13 @@ const MetricsGraph = (props) => { <Row> <Col> <ResponsiveContainer width={"100%"} height={150}> - <BarChart data={props.metrics.data}> + <AreaChart data={props.metrics.data}> <CartesianGrid strokeDasharay={"3 3"} /> <XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/> <YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/> - <Bar stroke={"#231069"} fill={"#04adef"} dataKey={(v) => v.rx ? v.rx : 0} /> - <Bar stroke={"#231069"} fill={"#9BF316"} dataKey={(v) => v.tx ? v.tx : 0} /> - <Tooltip /> - </BarChart> + <Area type={"basis"} stroke={"#231069"} fill={"#9BF316"} dataKey={(v) => v.tx ? v.tx : 0} stackId={"1"} /> + <Area type={"basis"} stroke={"#231069"} fill={"#04adef"} dataKey={(v) => v.rx ? v.rx : 0} stackId={"1"} /> + </AreaChart> </ResponsiveContainer> </Col> </Row> diff --git a/ui/src/console/visualizer/Network.js b/ui/src/console/visualizer/Network.js index c75421c4..8e81ca8d 100644 --- a/ui/src/console/visualizer/Network.js +++ b/ui/src/console/visualizer/Network.js @@ -48,6 +48,8 @@ const Network = (props) => { case "account": nodeIcon.addPath(accountIcon, xform); break; + default: + break; } ctx.fill(nodeIcon); From 6259b62a8a9f7c89a3db2c2fe5f4a526717f96a0 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Fri, 12 May 2023 11:10:29 -0400 Subject: [PATCH 30/49] separating out tabs in account detail (#327) --- .../console/detail/account/AccountDetail.js | 53 +----------------- ui/src/console/detail/account/MetricsTab.js | 55 +++++++++++++++++++ 2 files changed, 57 insertions(+), 51 deletions(-) create mode 100644 ui/src/console/detail/account/MetricsTab.js diff --git a/ui/src/console/detail/account/AccountDetail.js b/ui/src/console/detail/account/AccountDetail.js index 169984ed..23b8d679 100644 --- a/ui/src/console/detail/account/AccountDetail.js +++ b/ui/src/console/detail/account/AccountDetail.js @@ -3,10 +3,8 @@ import Icon from "@mdi/react"; import PropertyTable from "../../PropertyTable"; import {Tab, Tabs} from "react-bootstrap"; import SecretToggle from "../../SecretToggle"; -import React, {useEffect, useState} from "react"; -import * as metadata from "../../../api/metadata"; -import {buildMetrics} from "../../metrics/util"; -import MetricsView from "../../metrics/MetricsView"; +import React from "react"; +import MetricsTab from "./MetricsTab"; const AccountDetail = (props) => { const customProperties = { @@ -28,53 +26,6 @@ const AccountDetail = (props) => { ); } -const MetricsTab = () => { - const [metrics30, setMetrics30] = useState(buildMetrics([])); - const [metrics7, setMetrics7] = useState(buildMetrics([])); - const [metrics1, setMetrics1] = useState(buildMetrics([])); - useEffect(() => { - metadata.getAccountMetrics() - .then(resp => { - setMetrics30(buildMetrics(resp.data)); - }); - metadata.getAccountMetrics({duration: "168h"}) - .then(resp => { - setMetrics7(buildMetrics(resp.data)); - }); - metadata.getAccountMetrics({duration: "24h"}) - .then(resp => { - setMetrics1(buildMetrics(resp.data)); - }); - }, []); - - useEffect(() => { - let mounted = true; - let interval = setInterval(() => { - metadata.getAccountMetrics() - .then(resp => { - if(mounted) { - setMetrics30(buildMetrics(resp.data)); - } - }); - metadata.getAccountMetrics({duration: "168h"}) - .then(resp => { - setMetrics7(buildMetrics(resp.data)); - }); - metadata.getAccountMetrics({duration: "24h"}) - .then(resp => { - setMetrics1(buildMetrics(resp.data)); - }); - }, 5000); - return () => { - mounted = false; - clearInterval(interval); - } - }, []); - - return ( - <MetricsView metrics30={metrics30} metrics7={metrics7} metrics1={metrics1} /> - ); -} export default AccountDetail; \ No newline at end of file diff --git a/ui/src/console/detail/account/MetricsTab.js b/ui/src/console/detail/account/MetricsTab.js new file mode 100644 index 00000000..fdcbab7d --- /dev/null +++ b/ui/src/console/detail/account/MetricsTab.js @@ -0,0 +1,55 @@ +import React, {useEffect, useState} from "react"; +import {buildMetrics} from "../../metrics/util"; +import * as metadata from "../../../api/metadata"; +import MetricsView from "../../metrics/MetricsView"; + +const MetricsTab = () => { + const [metrics30, setMetrics30] = useState(buildMetrics([])); + const [metrics7, setMetrics7] = useState(buildMetrics([])); + const [metrics1, setMetrics1] = useState(buildMetrics([])); + + useEffect(() => { + metadata.getAccountMetrics() + .then(resp => { + setMetrics30(buildMetrics(resp.data)); + }); + metadata.getAccountMetrics({duration: "168h"}) + .then(resp => { + setMetrics7(buildMetrics(resp.data)); + }); + metadata.getAccountMetrics({duration: "24h"}) + .then(resp => { + setMetrics1(buildMetrics(resp.data)); + }); + }, []); + + useEffect(() => { + let mounted = true; + let interval = setInterval(() => { + metadata.getAccountMetrics() + .then(resp => { + if(mounted) { + setMetrics30(buildMetrics(resp.data)); + } + }); + metadata.getAccountMetrics({duration: "168h"}) + .then(resp => { + setMetrics7(buildMetrics(resp.data)); + }); + metadata.getAccountMetrics({duration: "24h"}) + .then(resp => { + setMetrics1(buildMetrics(resp.data)); + }); + }, 5000); + return () => { + mounted = false; + clearInterval(interval); + } + }, []); + + return ( + <MetricsView metrics30={metrics30} metrics7={metrics7} metrics1={metrics1} /> + ); +} + +export default MetricsTab; \ No newline at end of file From 2655eaefc01726ffbb747db855a994b7babc4cb2 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Fri, 12 May 2023 11:57:34 -0400 Subject: [PATCH 31/49] roughed in environment sparklines backend (#327) --- controller/accountDetail.go | 55 +++++++ controller/controller.go | 1 + controller/sparkData.go | 87 +++++++--- .../metadata/get_account_detail_parameters.go | 128 +++++++++++++++ .../metadata/get_account_detail_responses.go | 153 ++++++++++++++++++ rest_client_zrok/metadata/metadata_client.go | 41 +++++ rest_model_zrok/environment.go | 56 ++++++- rest_server_zrok/embedded_spec.go | 56 ++++++- .../operations/metadata/get_account_detail.go | 71 ++++++++ .../metadata/get_account_detail_parameters.go | 46 ++++++ .../metadata/get_account_detail_responses.go | 87 ++++++++++ .../metadata/get_account_detail_urlbuilder.go | 87 ++++++++++ rest_server_zrok/operations/zrok_api.go | 12 ++ specs/zrok.yml | 45 ++++-- ui/src/api/metadata.js | 16 ++ ui/src/api/types.js | 18 +-- 16 files changed, 908 insertions(+), 51 deletions(-) create mode 100644 controller/accountDetail.go create mode 100644 rest_client_zrok/metadata/get_account_detail_parameters.go create mode 100644 rest_client_zrok/metadata/get_account_detail_responses.go create mode 100644 rest_server_zrok/operations/metadata/get_account_detail.go create mode 100644 rest_server_zrok/operations/metadata/get_account_detail_parameters.go create mode 100644 rest_server_zrok/operations/metadata/get_account_detail_responses.go create mode 100644 rest_server_zrok/operations/metadata/get_account_detail_urlbuilder.go diff --git a/controller/accountDetail.go b/controller/accountDetail.go new file mode 100644 index 00000000..6bf4194b --- /dev/null +++ b/controller/accountDetail.go @@ -0,0 +1,55 @@ +package controller + +import ( + "github.com/go-openapi/runtime/middleware" + "github.com/openziti/zrok/rest_model_zrok" + "github.com/openziti/zrok/rest_server_zrok/operations/metadata" + "github.com/sirupsen/logrus" +) + +type accountDetailHandler struct{} + +func newAccountDetailHandler() *accountDetailHandler { + return &accountDetailHandler{} +} + +func (h *accountDetailHandler) Handle(params metadata.GetAccountDetailParams, principal *rest_model_zrok.Principal) middleware.Responder { + trx, err := str.Begin() + if err != nil { + logrus.Errorf("error stasrting transaction for '%v': %v", principal.Email, err) + return metadata.NewGetAccountDetailInternalServerError() + } + defer func() { _ = trx.Rollback() }() + envs, err := str.FindEnvironmentsForAccount(int(principal.ID), trx) + if err != nil { + logrus.Errorf("error retrieving environments for '%v': %v", principal.Email, err) + return metadata.NewGetAccountDetailInternalServerError() + } + sparkRx := make(map[int][]int64) + sparkTx := make(map[int][]int64) + if cfg.Metrics != nil && cfg.Metrics.Influx != nil { + sparkRx, sparkTx, err = sparkDataForEnvironments(envs) + if err != nil { + logrus.Errorf("error querying spark data for environments for '%v': %v", principal.Email, err) + } + } else { + logrus.Debug("skipping spark data for environments; no influx configuration") + } + var payload []*rest_model_zrok.Environment + for _, env := range envs { + var sparkData []*rest_model_zrok.SparkDataSample + for i := 0; i < len(sparkRx[env.Id]) && i < len(sparkTx[env.Id]); i++ { + sparkData = append(sparkData, &rest_model_zrok.SparkDataSample{Rx: float64(sparkRx[env.Id][i]), Tx: float64(sparkTx[env.Id][i])}) + } + payload = append(payload, &rest_model_zrok.Environment{ + Activity: sparkData, + Address: env.Address, + CreatedAt: env.CreatedAt.UnixMilli(), + Description: env.Description, + Host: env.Host, + UpdatedAt: env.UpdatedAt.UnixMilli(), + ZID: env.ZId, + }) + } + return metadata.NewGetAccountDetailOK().WithPayload(payload) +} diff --git a/controller/controller.go b/controller/controller.go index 706bf16f..18f189b7 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -46,6 +46,7 @@ func Run(inCfg *config.Config) error { api.AdminUpdateFrontendHandler = newUpdateFrontendHandler() api.EnvironmentEnableHandler = newEnableHandler() api.EnvironmentDisableHandler = newDisableHandler() + api.MetadataGetAccountDetailHandler = newAccountDetailHandler() api.MetadataConfigurationHandler = newConfigurationHandler(cfg) if cfg.Metrics != nil && cfg.Metrics.Influx != nil { api.MetadataGetAccountMetricsHandler = newGetAccountMetricsHandler(cfg.Metrics.Influx) diff --git a/controller/sparkData.go b/controller/sparkData.go index c68107e9..d8156c8d 100644 --- a/controller/sparkData.go +++ b/controller/sparkData.go @@ -6,13 +6,79 @@ import ( "github.com/openziti/zrok/controller/store" ) +func sparkDataForEnvironments(envs []*store.Environment) (rx, tx map[int][]int64, err error) { + rx = make(map[int][]int64) + tx = make(map[int][]int64) + if len(envs) > 0 { + qapi := idb.QueryAPI(cfg.Metrics.Influx.Org) + + envFilter := "|> filter(fn: (r) =>" + for i, env := range envs { + if i > 0 { + envFilter += " or" + } + envFilter += fmt.Sprintf(" r[\"envId\"] == \"%d\"", env.Id) + } + envFilter += ")" + query := fmt.Sprintf("from(bucket: \"%v\")\n", cfg.Metrics.Influx.Bucket) + + "|> range(start: -5m)\n" + + "|> filter(fn: (r) => r[\"_measurement\"] == \"xfer\")\n" + + "|> filter(fn: (r) => r[\"_field\"] == \"rx\" or r[\"_field\"] == \"tx\")\n" + + "|> filter(fn: (r) => r[\"namespace\"] == \"backend\")\n" + + envFilter + + "|> aggregateWindow(every: 10s, fn: sum, createEmpty: true)\n" + + result, err := qapi.Query(context.Background(), query) + if err != nil { + return nil, nil, err + } + + for result.Next() { + envId := result.Record().ValueByKey("envId").(int64) + switch result.Record().Field() { + case "rx": + rxV := int64(0) + if v, ok := result.Record().Value().(int64); ok { + rxV = v + } + rxData := append(rx[int(envId)], rxV) + rx[int(envId)] = rxData + + case "tx": + txV := int64(0) + if v, ok := result.Record().Value().(int64); ok { + txV = v + } + txData := append(tx[int(envId)], txV) + tx[int(envId)] = txData + } + } + } + return rx, tx, nil +} + func sparkDataForShares(shrs []*store.Share) (rx, tx map[string][]int64, err error) { rx = make(map[string][]int64) tx = make(map[string][]int64) if len(shrs) > 0 { qapi := idb.QueryAPI(cfg.Metrics.Influx.Org) - query := sparkFluxQuery(shrs, cfg.Metrics.Influx.Bucket) + shrFilter := "|> filter(fn: (r) =>" + for i, shr := range shrs { + if i > 0 { + shrFilter += " or" + } + shrFilter += fmt.Sprintf(" r[\"share\"] == \"%v\"", shr.Token) + } + shrFilter += ")" + query := fmt.Sprintf("from(bucket: \"%v\")\n", cfg.Metrics.Influx.Bucket) + + "|> range(start: -5m)\n" + + "|> filter(fn: (r) => r[\"_measurement\"] == \"xfer\")\n" + + "|> filter(fn: (r) => r[\"_field\"] == \"rx\" or r[\"_field\"] == \"tx\")\n" + + "|> filter(fn: (r) => r[\"namespace\"] == \"backend\")\n" + + shrFilter + + "|> aggregateWindow(every: 10s, fn: sum, createEmpty: true)\n" + result, err := qapi.Query(context.Background(), query) if err != nil { return nil, nil, err @@ -41,22 +107,3 @@ func sparkDataForShares(shrs []*store.Share) (rx, tx map[string][]int64, err err } return rx, tx, nil } - -func sparkFluxQuery(shrs []*store.Share, bucket string) string { - shrFilter := "|> filter(fn: (r) =>" - for i, shr := range shrs { - if i > 0 { - shrFilter += " or" - } - shrFilter += fmt.Sprintf(" r[\"share\"] == \"%v\"", shr.Token) - } - shrFilter += ")" - query := fmt.Sprintf("from(bucket: \"%v\")\n", bucket) + - "|> range(start: -5m)\n" + - "|> filter(fn: (r) => r[\"_measurement\"] == \"xfer\")\n" + - "|> filter(fn: (r) => r[\"_field\"] == \"rx\" or r[\"_field\"] == \"tx\")\n" + - "|> filter(fn: (r) => r[\"namespace\"] == \"backend\")" + - shrFilter + - "|> aggregateWindow(every: 10s, fn: sum, createEmpty: true)\n" - return query -} diff --git a/rest_client_zrok/metadata/get_account_detail_parameters.go b/rest_client_zrok/metadata/get_account_detail_parameters.go new file mode 100644 index 00000000..290957e7 --- /dev/null +++ b/rest_client_zrok/metadata/get_account_detail_parameters.go @@ -0,0 +1,128 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewGetAccountDetailParams creates a new GetAccountDetailParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetAccountDetailParams() *GetAccountDetailParams { + return &GetAccountDetailParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetAccountDetailParamsWithTimeout creates a new GetAccountDetailParams object +// with the ability to set a timeout on a request. +func NewGetAccountDetailParamsWithTimeout(timeout time.Duration) *GetAccountDetailParams { + return &GetAccountDetailParams{ + timeout: timeout, + } +} + +// NewGetAccountDetailParamsWithContext creates a new GetAccountDetailParams object +// with the ability to set a context for a request. +func NewGetAccountDetailParamsWithContext(ctx context.Context) *GetAccountDetailParams { + return &GetAccountDetailParams{ + Context: ctx, + } +} + +// NewGetAccountDetailParamsWithHTTPClient creates a new GetAccountDetailParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetAccountDetailParamsWithHTTPClient(client *http.Client) *GetAccountDetailParams { + return &GetAccountDetailParams{ + HTTPClient: client, + } +} + +/* +GetAccountDetailParams contains all the parameters to send to the API endpoint + + for the get account detail operation. + + Typically these are written to a http.Request. +*/ +type GetAccountDetailParams struct { + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get account detail params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetAccountDetailParams) WithDefaults() *GetAccountDetailParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get account detail params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetAccountDetailParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get account detail params +func (o *GetAccountDetailParams) WithTimeout(timeout time.Duration) *GetAccountDetailParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get account detail params +func (o *GetAccountDetailParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get account detail params +func (o *GetAccountDetailParams) WithContext(ctx context.Context) *GetAccountDetailParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get account detail params +func (o *GetAccountDetailParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get account detail params +func (o *GetAccountDetailParams) WithHTTPClient(client *http.Client) *GetAccountDetailParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get account detail params +func (o *GetAccountDetailParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WriteToRequest writes these params to a swagger request +func (o *GetAccountDetailParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_client_zrok/metadata/get_account_detail_responses.go b/rest_client_zrok/metadata/get_account_detail_responses.go new file mode 100644 index 00000000..2d51df0d --- /dev/null +++ b/rest_client_zrok/metadata/get_account_detail_responses.go @@ -0,0 +1,153 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetAccountDetailReader is a Reader for the GetAccountDetail structure. +type GetAccountDetailReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetAccountDetailReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetAccountDetailOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 500: + result := NewGetAccountDetailInternalServerError() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewGetAccountDetailOK creates a GetAccountDetailOK with default headers values +func NewGetAccountDetailOK() *GetAccountDetailOK { + return &GetAccountDetailOK{} +} + +/* +GetAccountDetailOK describes a response with status code 200, with default header values. + +ok +*/ +type GetAccountDetailOK struct { + Payload rest_model_zrok.Environments +} + +// IsSuccess returns true when this get account detail o k response has a 2xx status code +func (o *GetAccountDetailOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get account detail o k response has a 3xx status code +func (o *GetAccountDetailOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get account detail o k response has a 4xx status code +func (o *GetAccountDetailOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get account detail o k response has a 5xx status code +func (o *GetAccountDetailOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get account detail o k response a status code equal to that given +func (o *GetAccountDetailOK) IsCode(code int) bool { + return code == 200 +} + +func (o *GetAccountDetailOK) Error() string { + return fmt.Sprintf("[GET /detail/account][%d] getAccountDetailOK %+v", 200, o.Payload) +} + +func (o *GetAccountDetailOK) String() string { + return fmt.Sprintf("[GET /detail/account][%d] getAccountDetailOK %+v", 200, o.Payload) +} + +func (o *GetAccountDetailOK) GetPayload() rest_model_zrok.Environments { + return o.Payload +} + +func (o *GetAccountDetailOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetAccountDetailInternalServerError creates a GetAccountDetailInternalServerError with default headers values +func NewGetAccountDetailInternalServerError() *GetAccountDetailInternalServerError { + return &GetAccountDetailInternalServerError{} +} + +/* +GetAccountDetailInternalServerError describes a response with status code 500, with default header values. + +internal server error +*/ +type GetAccountDetailInternalServerError struct { +} + +// IsSuccess returns true when this get account detail internal server error response has a 2xx status code +func (o *GetAccountDetailInternalServerError) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get account detail internal server error response has a 3xx status code +func (o *GetAccountDetailInternalServerError) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get account detail internal server error response has a 4xx status code +func (o *GetAccountDetailInternalServerError) IsClientError() bool { + return false +} + +// IsServerError returns true when this get account detail internal server error response has a 5xx status code +func (o *GetAccountDetailInternalServerError) IsServerError() bool { + return true +} + +// IsCode returns true when this get account detail internal server error response a status code equal to that given +func (o *GetAccountDetailInternalServerError) IsCode(code int) bool { + return code == 500 +} + +func (o *GetAccountDetailInternalServerError) Error() string { + return fmt.Sprintf("[GET /detail/account][%d] getAccountDetailInternalServerError ", 500) +} + +func (o *GetAccountDetailInternalServerError) String() string { + return fmt.Sprintf("[GET /detail/account][%d] getAccountDetailInternalServerError ", 500) +} + +func (o *GetAccountDetailInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} diff --git a/rest_client_zrok/metadata/metadata_client.go b/rest_client_zrok/metadata/metadata_client.go index 11e3a1ca..764216b3 100644 --- a/rest_client_zrok/metadata/metadata_client.go +++ b/rest_client_zrok/metadata/metadata_client.go @@ -32,6 +32,8 @@ type ClientOption func(*runtime.ClientOperation) type ClientService interface { Configuration(params *ConfigurationParams, opts ...ClientOption) (*ConfigurationOK, error) + GetAccountDetail(params *GetAccountDetailParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetAccountDetailOK, error) + GetAccountMetrics(params *GetAccountMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetAccountMetricsOK, error) GetEnvironmentDetail(params *GetEnvironmentDetailParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetEnvironmentDetailOK, error) @@ -87,6 +89,45 @@ func (a *Client) Configuration(params *ConfigurationParams, opts ...ClientOption panic(msg) } +/* +GetAccountDetail get account detail API +*/ +func (a *Client) GetAccountDetail(params *GetAccountDetailParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetAccountDetailOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetAccountDetailParams() + } + op := &runtime.ClientOperation{ + ID: "getAccountDetail", + Method: "GET", + PathPattern: "/detail/account", + ProducesMediaTypes: []string{"application/zrok.v1+json"}, + ConsumesMediaTypes: []string{"application/zrok.v1+json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetAccountDetailReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetAccountDetailOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for getAccountDetail: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + /* GetAccountMetrics get account metrics API */ diff --git a/rest_model_zrok/environment.go b/rest_model_zrok/environment.go index a6041314..19a354de 100644 --- a/rest_model_zrok/environment.go +++ b/rest_model_zrok/environment.go @@ -8,6 +8,7 @@ package rest_model_zrok import ( "context" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" ) @@ -17,8 +18,8 @@ import ( // swagger:model environment type Environment struct { - // active - Active bool `json:"active,omitempty"` + // activity + Activity SparkData `json:"activity,omitempty"` // address Address string `json:"address,omitempty"` @@ -41,11 +42,60 @@ type Environment struct { // Validate validates this environment func (m *Environment) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateActivity(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } return nil } -// ContextValidate validates this environment based on context it is used +func (m *Environment) validateActivity(formats strfmt.Registry) error { + if swag.IsZero(m.Activity) { // not required + return nil + } + + if err := m.Activity.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("activity") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("activity") + } + return err + } + + return nil +} + +// ContextValidate validate this environment based on the context it is used func (m *Environment) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateActivity(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Environment) contextValidateActivity(ctx context.Context, formats strfmt.Registry) error { + + if err := m.Activity.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("activity") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("activity") + } + return err + } + return nil } diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index d41988ff..03a22b60 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -90,6 +90,30 @@ func init() { } } }, + "/detail/account": { + "get": { + "security": [ + { + "key": [] + } + ], + "tags": [ + "metadata" + ], + "operationId": "getAccountDetail", + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/environments" + } + }, + "500": { + "description": "internal server error" + } + } + } + }, "/detail/environment/{envZId}": { "get": { "security": [ @@ -1065,8 +1089,8 @@ func init() { "environment": { "type": "object", "properties": { - "active": { - "type": "boolean" + "activity": { + "$ref": "#/definitions/sparkData" }, "address": { "type": "string" @@ -1537,6 +1561,30 @@ func init() { } } }, + "/detail/account": { + "get": { + "security": [ + { + "key": [] + } + ], + "tags": [ + "metadata" + ], + "operationId": "getAccountDetail", + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/environments" + } + }, + "500": { + "description": "internal server error" + } + } + } + }, "/detail/environment/{envZId}": { "get": { "security": [ @@ -2512,8 +2560,8 @@ func init() { "environment": { "type": "object", "properties": { - "active": { - "type": "boolean" + "activity": { + "$ref": "#/definitions/sparkData" }, "address": { "type": "string" diff --git a/rest_server_zrok/operations/metadata/get_account_detail.go b/rest_server_zrok/operations/metadata/get_account_detail.go new file mode 100644 index 00000000..3df45d07 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_account_detail.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetAccountDetailHandlerFunc turns a function with the right signature into a get account detail handler +type GetAccountDetailHandlerFunc func(GetAccountDetailParams, *rest_model_zrok.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetAccountDetailHandlerFunc) Handle(params GetAccountDetailParams, principal *rest_model_zrok.Principal) middleware.Responder { + return fn(params, principal) +} + +// GetAccountDetailHandler interface for that can handle valid get account detail params +type GetAccountDetailHandler interface { + Handle(GetAccountDetailParams, *rest_model_zrok.Principal) middleware.Responder +} + +// NewGetAccountDetail creates a new http.Handler for the get account detail operation +func NewGetAccountDetail(ctx *middleware.Context, handler GetAccountDetailHandler) *GetAccountDetail { + return &GetAccountDetail{Context: ctx, Handler: handler} +} + +/* + GetAccountDetail swagger:route GET /detail/account metadata getAccountDetail + +GetAccountDetail get account detail API +*/ +type GetAccountDetail struct { + Context *middleware.Context + Handler GetAccountDetailHandler +} + +func (o *GetAccountDetail) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetAccountDetailParams() + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + *r = *aCtx + } + var principal *rest_model_zrok.Principal + if uprinc != nil { + principal = uprinc.(*rest_model_zrok.Principal) // this is really a rest_model_zrok.Principal, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/rest_server_zrok/operations/metadata/get_account_detail_parameters.go b/rest_server_zrok/operations/metadata/get_account_detail_parameters.go new file mode 100644 index 00000000..2702d68e --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_account_detail_parameters.go @@ -0,0 +1,46 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" +) + +// NewGetAccountDetailParams creates a new GetAccountDetailParams object +// +// There are no default values defined in the spec. +func NewGetAccountDetailParams() GetAccountDetailParams { + + return GetAccountDetailParams{} +} + +// GetAccountDetailParams contains all the bound params for the get account detail operation +// typically these are obtained from a http.Request +// +// swagger:parameters getAccountDetail +type GetAccountDetailParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetAccountDetailParams() beforehand. +func (o *GetAccountDetailParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_server_zrok/operations/metadata/get_account_detail_responses.go b/rest_server_zrok/operations/metadata/get_account_detail_responses.go new file mode 100644 index 00000000..78b271c3 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_account_detail_responses.go @@ -0,0 +1,87 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetAccountDetailOKCode is the HTTP code returned for type GetAccountDetailOK +const GetAccountDetailOKCode int = 200 + +/* +GetAccountDetailOK ok + +swagger:response getAccountDetailOK +*/ +type GetAccountDetailOK struct { + + /* + In: Body + */ + Payload rest_model_zrok.Environments `json:"body,omitempty"` +} + +// NewGetAccountDetailOK creates GetAccountDetailOK with default headers values +func NewGetAccountDetailOK() *GetAccountDetailOK { + + return &GetAccountDetailOK{} +} + +// WithPayload adds the payload to the get account detail o k response +func (o *GetAccountDetailOK) WithPayload(payload rest_model_zrok.Environments) *GetAccountDetailOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get account detail o k response +func (o *GetAccountDetailOK) SetPayload(payload rest_model_zrok.Environments) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetAccountDetailOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + payload := o.Payload + if payload == nil { + // return empty array + payload = rest_model_zrok.Environments{} + } + + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// GetAccountDetailInternalServerErrorCode is the HTTP code returned for type GetAccountDetailInternalServerError +const GetAccountDetailInternalServerErrorCode int = 500 + +/* +GetAccountDetailInternalServerError internal server error + +swagger:response getAccountDetailInternalServerError +*/ +type GetAccountDetailInternalServerError struct { +} + +// NewGetAccountDetailInternalServerError creates GetAccountDetailInternalServerError with default headers values +func NewGetAccountDetailInternalServerError() *GetAccountDetailInternalServerError { + + return &GetAccountDetailInternalServerError{} +} + +// WriteResponse to the client +func (o *GetAccountDetailInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(500) +} diff --git a/rest_server_zrok/operations/metadata/get_account_detail_urlbuilder.go b/rest_server_zrok/operations/metadata/get_account_detail_urlbuilder.go new file mode 100644 index 00000000..b7eda126 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_account_detail_urlbuilder.go @@ -0,0 +1,87 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// GetAccountDetailURL generates an URL for the get account detail operation +type GetAccountDetailURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetAccountDetailURL) WithBasePath(bp string) *GetAccountDetailURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetAccountDetailURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetAccountDetailURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/detail/account" + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetAccountDetailURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetAccountDetailURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetAccountDetailURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetAccountDetailURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetAccountDetailURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetAccountDetailURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/rest_server_zrok/operations/zrok_api.go b/rest_server_zrok/operations/zrok_api.go index 61b9bad1..ed5c7721 100644 --- a/rest_server_zrok/operations/zrok_api.go +++ b/rest_server_zrok/operations/zrok_api.go @@ -70,6 +70,9 @@ func NewZrokAPI(spec *loads.Document) *ZrokAPI { EnvironmentEnableHandler: environment.EnableHandlerFunc(func(params environment.EnableParams, principal *rest_model_zrok.Principal) middleware.Responder { return middleware.NotImplemented("operation environment.Enable has not yet been implemented") }), + MetadataGetAccountDetailHandler: metadata.GetAccountDetailHandlerFunc(func(params metadata.GetAccountDetailParams, principal *rest_model_zrok.Principal) middleware.Responder { + return middleware.NotImplemented("operation metadata.GetAccountDetail has not yet been implemented") + }), MetadataGetAccountMetricsHandler: metadata.GetAccountMetricsHandlerFunc(func(params metadata.GetAccountMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { return middleware.NotImplemented("operation metadata.GetAccountMetrics has not yet been implemented") }), @@ -194,6 +197,8 @@ type ZrokAPI struct { EnvironmentDisableHandler environment.DisableHandler // EnvironmentEnableHandler sets the operation handler for the enable operation EnvironmentEnableHandler environment.EnableHandler + // MetadataGetAccountDetailHandler sets the operation handler for the get account detail operation + MetadataGetAccountDetailHandler metadata.GetAccountDetailHandler // MetadataGetAccountMetricsHandler sets the operation handler for the get account metrics operation MetadataGetAccountMetricsHandler metadata.GetAccountMetricsHandler // MetadataGetEnvironmentDetailHandler sets the operation handler for the get environment detail operation @@ -336,6 +341,9 @@ func (o *ZrokAPI) Validate() error { if o.EnvironmentEnableHandler == nil { unregistered = append(unregistered, "environment.EnableHandler") } + if o.MetadataGetAccountDetailHandler == nil { + unregistered = append(unregistered, "metadata.GetAccountDetailHandler") + } if o.MetadataGetAccountMetricsHandler == nil { unregistered = append(unregistered, "metadata.GetAccountMetricsHandler") } @@ -526,6 +534,10 @@ func (o *ZrokAPI) initHandlerCache() { if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } + o.handlers["GET"]["/detail/account"] = metadata.NewGetAccountDetail(o.context, o.MetadataGetAccountDetailHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } o.handlers["GET"]["/metrics/account"] = metadata.NewGetAccountMetrics(o.context, o.MetadataGetAccountMetricsHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) diff --git a/specs/zrok.yml b/specs/zrok.yml index 39fe9033..38897e48 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -329,6 +329,21 @@ paths: schema: $ref: "#/definitions/configuration" + /detail/account: + get: + tags: + - metadata + security: + - key: [] + operationId: getAccountDetail + responses: + 200: + description: ok + schema: + $ref: "#/definitions/environments" + 500: + description: internal server error + /detail/environment/{envZId}: get: tags: @@ -689,8 +704,8 @@ definitions: type: string zId: type: string - active: - type: boolean + activity: + $ref: "#/definitions/sparkData" createdAt: type: integer updatedAt: @@ -856,19 +871,6 @@ definitions: items: $ref: "#/definitions/share" - sparkData: - type: array - items: - $ref: "#/definitions/sparkDataSample" - - sparkDataSample: - type: object - properties: - rx: - type: number - tx: - type: number - shareRequest: type: object properties: @@ -905,6 +907,19 @@ definitions: shrToken: type: string + sparkData: + type: array + items: + $ref: "#/definitions/sparkDataSample" + + sparkDataSample: + type: object + properties: + rx: + type: number + tx: + type: number + unaccessRequest: type: object properties: diff --git a/ui/src/api/metadata.js b/ui/src/api/metadata.js index 7b8af17a..1b50438f 100644 --- a/ui/src/api/metadata.js +++ b/ui/src/api/metadata.js @@ -8,6 +8,12 @@ export function configuration() { return gateway.request(configurationOperation) } +/** + */ +export function getAccountDetail() { + return gateway.request(getAccountDetailOperation) +} + /** * @param {string} envZId * @return {Promise<module:types.environmentShares>} ok @@ -104,6 +110,16 @@ const configurationOperation = { method: 'get' } +const getAccountDetailOperation = { + path: '/detail/account', + method: 'get', + security: [ + { + id: 'key' + } + ] +} + const getEnvironmentDetailOperation = { path: '/detail/environment/{envZId}', method: 'get', diff --git a/ui/src/api/types.js b/ui/src/api/types.js index 09093d0e..4d5a07cb 100644 --- a/ui/src/api/types.js +++ b/ui/src/api/types.js @@ -87,7 +87,7 @@ * @property {string} host * @property {string} address * @property {string} zId - * @property {boolean} active + * @property {module:types.sparkData} activity * @property {number} createdAt * @property {number} updatedAt */ @@ -205,14 +205,6 @@ * @property {number} updatedAt */ -/** - * @typedef sparkDataSample - * @memberof module:types - * - * @property {number} rx - * @property {number} tx - */ - /** * @typedef shareRequest * @memberof module:types @@ -235,6 +227,14 @@ * @property {string} shrToken */ +/** + * @typedef sparkDataSample + * @memberof module:types + * + * @property {number} rx + * @property {number} tx + */ + /** * @typedef unaccessRequest * @memberof module:types From 8bf2173c49d16c4a823dbc5e67a634ddebff96cb Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Fri, 12 May 2023 13:24:29 -0400 Subject: [PATCH 32/49] tweaks to get environment sparklines running (#327) --- controller/sparkData.go | 10 ++- go.mod | 4 +- .../console/detail/account/AccountDetail.js | 6 +- .../console/detail/account/EnvironmentsTab.js | 71 +++++++++++++++++++ .../console/detail/environment/SharesTab.js | 2 +- ui/src/console/metrics/util.js | 1 - 6 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 ui/src/console/detail/account/EnvironmentsTab.js diff --git a/controller/sparkData.go b/controller/sparkData.go index d8156c8d..632baf24 100644 --- a/controller/sparkData.go +++ b/controller/sparkData.go @@ -4,6 +4,8 @@ import ( "context" "fmt" "github.com/openziti/zrok/controller/store" + "github.com/sirupsen/logrus" + "strconv" ) func sparkDataForEnvironments(envs []*store.Environment) (rx, tx map[int][]int64, err error) { @@ -26,6 +28,7 @@ func sparkDataForEnvironments(envs []*store.Environment) (rx, tx map[int][]int64 "|> filter(fn: (r) => r[\"_field\"] == \"rx\" or r[\"_field\"] == \"tx\")\n" + "|> filter(fn: (r) => r[\"namespace\"] == \"backend\")\n" + envFilter + + "|> drop(columns: [\"share\", \"acctId\"])\n" + "|> aggregateWindow(every: 10s, fn: sum, createEmpty: true)\n" result, err := qapi.Query(context.Background(), query) @@ -34,7 +37,12 @@ func sparkDataForEnvironments(envs []*store.Environment) (rx, tx map[int][]int64 } for result.Next() { - envId := result.Record().ValueByKey("envId").(int64) + envIdS := result.Record().ValueByKey("envId").(string) + envId, err := strconv.ParseInt(envIdS, 10, 32) + if err != nil { + logrus.Errorf("error parsing '%v': %v", envIdS, err) + continue + } switch result.Record().Field() { case "rx": rxV := int64(0) diff --git a/go.mod b/go.mod index 7a4cf38c..9f5a34f5 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/openziti/zrok -go 1.19 +go 1.20 require ( github.com/charmbracelet/bubbles v0.14.0 @@ -31,6 +31,7 @@ require ( github.com/openziti/fabric v0.22.59 github.com/openziti/identity v1.0.37 github.com/openziti/sdk-golang v0.18.61 + github.com/openziti/transport/v2 v2.0.63 github.com/pkg/errors v0.9.1 github.com/rabbitmq/amqp091-go v1.7.0 github.com/rubenv/sql-migrate v1.1.2 @@ -91,7 +92,6 @@ require ( github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/openziti/foundation/v2 v2.0.17 // indirect github.com/openziti/metrics v1.2.10 // indirect - github.com/openziti/transport/v2 v2.0.63 // indirect github.com/orcaman/concurrent-map/v2 v2.0.1 // indirect github.com/parallaxsecond/parsec-client-go v0.0.0-20221025095442-f0a77d263cf9 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/ui/src/console/detail/account/AccountDetail.js b/ui/src/console/detail/account/AccountDetail.js index 23b8d679..5ca50391 100644 --- a/ui/src/console/detail/account/AccountDetail.js +++ b/ui/src/console/detail/account/AccountDetail.js @@ -5,6 +5,7 @@ import {Tab, Tabs} from "react-bootstrap"; import SecretToggle from "../../SecretToggle"; import React from "react"; import MetricsTab from "./MetricsTab"; +import EnvironmentsTab from "./EnvironmentsTab"; const AccountDetail = (props) => { const customProperties = { @@ -14,7 +15,10 @@ const AccountDetail = (props) => { return ( <div> <h2><Icon path={mdiAccountBox} size={2} />{" "}{props.user.email}</h2> - <Tabs defaultActiveKey={"detail"}> + <Tabs defaultActiveKey={"environments"}> + <Tab eventKey={"environments"} title={"Environments"}> + <EnvironmentsTab /> + </Tab> <Tab eventKey={"detail"} title={"Detail"}> <PropertyTable object={props.user} custom={customProperties}/> </Tab> diff --git a/ui/src/console/detail/account/EnvironmentsTab.js b/ui/src/console/detail/account/EnvironmentsTab.js new file mode 100644 index 00000000..9da48ce1 --- /dev/null +++ b/ui/src/console/detail/account/EnvironmentsTab.js @@ -0,0 +1,71 @@ +import React, {useEffect, useState} from "react"; +import * as metadata from "../../../api/metadata"; +import {Area, AreaChart, ResponsiveContainer} from "recharts"; +import DataTable from "react-data-table-component"; + +const EnvironmentsTab = (props) => { + const [detail, setDetail] = useState([]); + + useEffect(() => { + metadata.getAccountDetail() + .then(resp => { + setDetail(resp.data); + }); + }, [props.selection]); + + useEffect(() => { + let mounted = true; + let interval = setInterval(() => { + metadata.getAccountDetail() + .then(resp => { + if(mounted) { + setDetail(resp.data); + } + }); + }, 5000); + return () => { + mounted = false; + clearInterval(interval); + } + }, [props.selection]); + + const columns = [ + { + name: "Description", + selector: row => row.description, + sortable: true + }, + { + name: "Address", + grow: 0.5, + selector: row => row.address, + sortable: true + }, + { + name: "Activity", + grow: 0.5, + cell: row => { + return <ResponsiveContainer width={"100%"} height={"100%"}> + <AreaChart data={row.activity}> + <Area type={"basis"} dataKey={(v) => v.rx ? v.rx : 0} stroke={"#231069"} fill={"#04adef"} isAnimationActive={false} dot={false} /> + <Area type={"basis"} dataKey={(v) => v.tx ? v.tx * -1 : 0} stroke={"#231069"} fill={"#9BF316"} isAnimationActive={false} dot={false} /> + </AreaChart> + </ResponsiveContainer> + } + } + ]; + + return ( + <div className={"zrok-datatable"}> + <DataTable + className={"zrok-datatable"} + data={detail} + columns={columns} + defaultSortField={1} + noDataComponent={<p>No environments in account</p>} + /> + </div> + ); +} + +export default EnvironmentsTab; \ No newline at end of file diff --git a/ui/src/console/detail/environment/SharesTab.js b/ui/src/console/detail/environment/SharesTab.js index a0926bce..7a297221 100644 --- a/ui/src/console/detail/environment/SharesTab.js +++ b/ui/src/console/detail/environment/SharesTab.js @@ -22,7 +22,7 @@ const SharesTab = (props) => { setDetail(resp.data); } }); - }, 1000); + }, 5000); return () => { mounted = false; clearInterval(interval); diff --git a/ui/src/console/metrics/util.js b/ui/src/console/metrics/util.js index 5c4e292f..d29de073 100644 --- a/ui/src/console/metrics/util.js +++ b/ui/src/console/metrics/util.js @@ -1,5 +1,4 @@ export const buildMetrics = (m) => { - console.log("build", m); let metrics = { data: m.samples, rx: 0, From ebcbeeb9008c6c3d390e1798bc99a17749b190ea Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Fri, 12 May 2023 13:30:14 -0400 Subject: [PATCH 33/49] visual lint --- ui/src/console/visualizer/Network.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/console/visualizer/Network.js b/ui/src/console/visualizer/Network.js index 8e81ca8d..a36b1283 100644 --- a/ui/src/console/visualizer/Network.js +++ b/ui/src/console/visualizer/Network.js @@ -22,7 +22,7 @@ const Network = (props) => { const paintNode = (node, ctx) => { let nodeColor = node.selected ? "#9BF316" : "#04adef"; - let textColor = node.selected ? "black" : "white"; + let textColor = "black"; ctx.textBaseline = "middle"; ctx.textAlign = "center"; From 7d48683df74418d0109791b1df9bb505f70be919 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Mon, 15 May 2023 14:14:52 -0400 Subject: [PATCH 34/49] support for displaying limited shares in red in the visualizer (#320) --- controller/overview.go | 25 +++++++++++++++++--- controller/store/shareLimitJournal.go | 28 +++++++++++++++++++++++ rest_model_zrok/environment.go | 3 +++ rest_model_zrok/share.go | 3 +++ rest_server_zrok/embedded_spec.go | 12 ++++++++++ specs/zrok.yml | 4 ++++ ui/src/api/types.js | 2 ++ ui/src/console/detail/share/MetricsTab.js | 2 -- ui/src/console/visualizer/Network.js | 2 +- ui/src/console/visualizer/graph.js | 10 ++++---- 10 files changed, 81 insertions(+), 10 deletions(-) diff --git a/controller/overview.go b/controller/overview.go index c85e2418..ee313ba6 100644 --- a/controller/overview.go +++ b/controller/overview.go @@ -2,6 +2,7 @@ package controller import ( "github.com/go-openapi/runtime/middleware" + "github.com/openziti/zrok/controller/store" "github.com/openziti/zrok/rest_model_zrok" "github.com/openziti/zrok/rest_server_zrok/operations/metadata" "github.com/sirupsen/logrus" @@ -36,7 +37,19 @@ func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Princ ZID: env.ZId, }, } - + var shrIds []int + for i := range shrs { + shrIds = append(shrIds, shrs[i].Id) + } + shrsLimited, err := str.FindSelectedLatestShareLimitjournal(shrIds, tx) + if err != nil { + logrus.Errorf("error finding limited shares for environment '%v': %v", env.ZId, err) + return metadata.NewOverviewInternalServerError() + } + shrsLimitedMap := make(map[int]store.LimitJournalAction) + for i := range shrsLimited { + shrsLimitedMap[shrsLimited[i].ShareId] = shrsLimited[i].Action + } for _, shr := range shrs { feEndpoint := "" if shr.FrontendEndpoint != nil { @@ -50,7 +63,7 @@ func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Princ if shr.BackendProxyEndpoint != nil { beProxyEndpoint = *shr.BackendProxyEndpoint } - es.Shares = append(es.Shares, &rest_model_zrok.Share{ + oshr := &rest_model_zrok.Share{ Token: shr.Token, ZID: shr.ZId, ShareMode: shr.ShareMode, @@ -61,7 +74,13 @@ func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Princ Reserved: shr.Reserved, CreatedAt: shr.CreatedAt.UnixMilli(), UpdatedAt: shr.UpdatedAt.UnixMilli(), - }) + } + if action, found := shrsLimitedMap[shr.Id]; found { + if action == store.LimitAction { + oshr.Limited = true + } + } + es.Shares = append(es.Shares, oshr) } out = append(out, es) } diff --git a/controller/store/shareLimitJournal.go b/controller/store/shareLimitJournal.go index 7dcc351f..2b935cc1 100644 --- a/controller/store/shareLimitJournal.go +++ b/controller/store/shareLimitJournal.go @@ -1,6 +1,7 @@ package store import ( + "fmt" "github.com/jmoiron/sqlx" "github.com/pkg/errors" ) @@ -41,6 +42,33 @@ func (str *Store) FindLatestShareLimitJournal(shrId int, trx *sqlx.Tx) (*ShareLi return j, nil } +func (str *Store) FindSelectedLatestShareLimitjournal(shrIds []int, trx *sqlx.Tx) ([]*ShareLimitJournal, error) { + if len(shrIds) < 1 { + return nil, nil + } + in := "(" + for i := range shrIds { + if i > 0 { + in += ", " + } + in += fmt.Sprintf("%d", shrIds[i]) + } + in += ")" + rows, err := trx.Queryx("select id, share_id, rx_bytes, tx_bytes, action, created_at, updated_at from share_limit_journal where id in (select max(id) as id from share_limit_journal group by share_id) and share_id in " + in) + if err != nil { + return nil, errors.Wrap(err, "error selecting all latest share_limit_journal") + } + var sljs []*ShareLimitJournal + for rows.Next() { + slj := &ShareLimitJournal{} + if err := rows.StructScan(slj); err != nil { + return nil, errors.Wrap(err, "error scanning share_limit_journal") + } + sljs = append(sljs, slj) + } + return sljs, nil +} + func (str *Store) FindAllLatestShareLimitJournal(trx *sqlx.Tx) ([]*ShareLimitJournal, error) { rows, err := trx.Queryx("select id, share_id, rx_bytes, tx_bytes, action, created_at, updated_at from share_limit_journal where id in (select max(id) as id from share_limit_journal group by share_id)") if err != nil { diff --git a/rest_model_zrok/environment.go b/rest_model_zrok/environment.go index 19a354de..17376949 100644 --- a/rest_model_zrok/environment.go +++ b/rest_model_zrok/environment.go @@ -33,6 +33,9 @@ type Environment struct { // host Host string `json:"host,omitempty"` + // limited + Limited bool `json:"limited,omitempty"` + // updated at UpdatedAt int64 `json:"updatedAt,omitempty"` diff --git a/rest_model_zrok/share.go b/rest_model_zrok/share.go index 983c11c6..998744b4 100644 --- a/rest_model_zrok/share.go +++ b/rest_model_zrok/share.go @@ -36,6 +36,9 @@ type Share struct { // frontend selection FrontendSelection string `json:"frontendSelection,omitempty"` + // limited + Limited bool `json:"limited,omitempty"` + // reserved Reserved bool `json:"reserved,omitempty"` diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 03a22b60..1d9d36a4 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -1104,6 +1104,9 @@ func init() { "host": { "type": "string" }, + "limited": { + "type": "boolean" + }, "updatedAt": { "type": "integer" }, @@ -1308,6 +1311,9 @@ func init() { "frontendSelection": { "type": "string" }, + "limited": { + "type": "boolean" + }, "reserved": { "type": "boolean" }, @@ -2575,6 +2581,9 @@ func init() { "host": { "type": "string" }, + "limited": { + "type": "boolean" + }, "updatedAt": { "type": "integer" }, @@ -2779,6 +2788,9 @@ func init() { "frontendSelection": { "type": "string" }, + "limited": { + "type": "boolean" + }, "reserved": { "type": "boolean" }, diff --git a/specs/zrok.yml b/specs/zrok.yml index 38897e48..2394046f 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -706,6 +706,8 @@ definitions: type: string activity: $ref: "#/definitions/sparkData" + limited: + type: boolean createdAt: type: integer updatedAt: @@ -861,6 +863,8 @@ definitions: type: boolean activity: $ref: "#/definitions/sparkData" + limited: + type: boolean createdAt: type: integer updatedAt: diff --git a/ui/src/api/types.js b/ui/src/api/types.js index 4d5a07cb..0d2fb901 100644 --- a/ui/src/api/types.js +++ b/ui/src/api/types.js @@ -88,6 +88,7 @@ * @property {string} address * @property {string} zId * @property {module:types.sparkData} activity + * @property {boolean} limited * @property {number} createdAt * @property {number} updatedAt */ @@ -201,6 +202,7 @@ * @property {string} backendProxyEndpoint * @property {boolean} reserved * @property {module:types.sparkData} activity + * @property {boolean} limited * @property {number} createdAt * @property {number} updatedAt */ diff --git a/ui/src/console/detail/share/MetricsTab.js b/ui/src/console/detail/share/MetricsTab.js index 51143ffe..d73bff6f 100644 --- a/ui/src/console/detail/share/MetricsTab.js +++ b/ui/src/console/detail/share/MetricsTab.js @@ -9,7 +9,6 @@ const MetricsTab = (props) => { const [metrics1, setMetrics1] = useState(buildMetrics([])); useEffect(() => { - console.log("token", props.share.token); metadata.getShareMetrics(props.share.token) .then(resp => { setMetrics30(buildMetrics(resp.data)); @@ -27,7 +26,6 @@ const MetricsTab = (props) => { useEffect(() => { let mounted = true; let interval = setInterval(() => { - console.log("token", props.share.token); metadata.getShareMetrics(props.share.token) .then(resp => { if(mounted) { diff --git a/ui/src/console/visualizer/Network.js b/ui/src/console/visualizer/Network.js index a36b1283..9e8fda5f 100644 --- a/ui/src/console/visualizer/Network.js +++ b/ui/src/console/visualizer/Network.js @@ -21,7 +21,7 @@ const Network = (props) => { }, []); const paintNode = (node, ctx) => { - let nodeColor = node.selected ? "#9BF316" : "#04adef"; + let nodeColor = node.selected ? "#9BF316" : node.limited ? "#f00": "#04adef"; let textColor = "black"; ctx.textBaseline = "middle"; diff --git a/ui/src/console/visualizer/graph.js b/ui/src/console/visualizer/graph.js index c10d0ab3..f8d0006e 100644 --- a/ui/src/console/visualizer/graph.js +++ b/ui/src/console/visualizer/graph.js @@ -12,7 +12,7 @@ const sortNodes = (nodes) => { const nodesEqual = (a, b) => { if(a.length !== b.length) return false; - return a.every((e, i) => e.id === b[i].id); + return a.every((e, i) => e.id === b[i].id && e.limited === b[i].limited); } export const mergeGraph = (oldGraph, user, newOverview) => { @@ -34,7 +34,8 @@ export const mergeGraph = (oldGraph, user, newOverview) => { id: env.environment.zId, label: env.environment.description, type: "environment", - val: 50 + val: 50, + limited: env.limited }; newGraph.nodes.push(envNode); newGraph.links.push({ @@ -53,6 +54,7 @@ export const mergeGraph = (oldGraph, user, newOverview) => { envZId: env.environment.zId, label: shrLabel, type: "share", + limited: !!shr.limited, val: 50 }; newGraph.nodes.push(shrNode); @@ -75,11 +77,11 @@ export const mergeGraph = (oldGraph, user, newOverview) => { // we're going to need to recompute a new graph... but we want to maintain the instances that already exist... // we want to preserve nodes that exist in the new graph, and remove those that don't. - let outputNodes = oldGraph.nodes.filter(oldNode => newGraph.nodes.find(newNode => newNode.id === oldNode.id)); + let outputNodes = oldGraph.nodes.filter(oldNode => newGraph.nodes.find(newNode => newNode.id === oldNode.id && newNode.limited === oldNode.limited)); let outputLinks = oldGraph.nodes.filter(oldLink => newGraph.links.find(newLink => newLink.target === oldLink.target && newLink.source === oldLink.source)); // and then do the opposite; add any nodes that are in newGraph that are missing from oldGraph. - outputNodes.push(...newGraph.nodes.filter(newNode => !outputNodes.find(oldNode => oldNode.id === newNode.id))); + outputNodes.push(...newGraph.nodes.filter(newNode => !outputNodes.find(oldNode => oldNode.id === newNode.id && oldNode.limited === newNode.limited))); outputLinks.push(...newGraph.links.filter(newLink => !outputLinks.find(oldLink => oldLink.target === newLink.target && oldLink.source === newLink.source))); return { From d718e8c9ffc0aba17b4c63e45ef5e09cb2e81b3b Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Mon, 15 May 2023 14:27:39 -0400 Subject: [PATCH 35/49] the icon overlay remains 'limited red', even when the node is selected (#320) --- ui/src/console/visualizer/Network.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/src/console/visualizer/Network.js b/ui/src/console/visualizer/Network.js index 9e8fda5f..eeee9af2 100644 --- a/ui/src/console/visualizer/Network.js +++ b/ui/src/console/visualizer/Network.js @@ -36,6 +36,9 @@ const Network = (props) => { const nodeIcon = new Path2D(); let xform = new DOMMatrix(); + if(node.limited) { + ctx.fillStyle = "#f00"; + } xform.translateSelf(node.x - (nodeWidth / 2) - 6, node.y - 13); xform.scaleSelf(0.5, 0.5); switch(node.type) { @@ -51,7 +54,6 @@ const Network = (props) => { default: break; } - ctx.fill(nodeIcon); ctx.strokeStyle = "black"; ctx.lineWidth = 0.5; From 9591f5150e3d66a411eee201a18ddac5b18b6054 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Tue, 16 May 2023 11:51:03 -0400 Subject: [PATCH 36/49] new overview response for '/overview' endpoint (#320) --- controller/overview.go | 17 +-- .../metadata/overview_responses.go | 8 +- rest_model_zrok/overview.go | 103 ++++++++++++++++++ rest_server_zrok/embedded_spec.go | 26 ++++- .../operations/metadata/overview_responses.go | 19 ++-- specs/zrok.yml | 10 +- ui/src/api/types.js | 8 ++ ui/src/console/Console.js | 4 +- ui/src/console/visualizer/Visualizer.js | 3 +- ui/src/console/visualizer/graph.js | 74 +++++++------ 10 files changed, 210 insertions(+), 62 deletions(-) create mode 100644 rest_model_zrok/overview.go diff --git a/controller/overview.go b/controller/overview.go index ee313ba6..d7fabb82 100644 --- a/controller/overview.go +++ b/controller/overview.go @@ -20,14 +20,14 @@ func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Princ logrus.Errorf("error finding environments for '%v': %v", principal.Email, err) return metadata.NewOverviewInternalServerError() } - var out rest_model_zrok.EnvironmentSharesList + var envShrsList rest_model_zrok.EnvironmentSharesList for _, env := range envs { shrs, err := str.FindSharesForEnvironment(env.Id, tx) if err != nil { logrus.Errorf("error finding shares for environment '%v': %v", env.ZId, err) return metadata.NewOverviewInternalServerError() } - es := &rest_model_zrok.EnvironmentShares{ + envShrs := &rest_model_zrok.EnvironmentShares{ Environment: &rest_model_zrok.Environment{ Address: env.Address, CreatedAt: env.CreatedAt.UnixMilli(), @@ -63,7 +63,7 @@ func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Princ if shr.BackendProxyEndpoint != nil { beProxyEndpoint = *shr.BackendProxyEndpoint } - oshr := &rest_model_zrok.Share{ + envShr := &rest_model_zrok.Share{ Token: shr.Token, ZID: shr.ZId, ShareMode: shr.ShareMode, @@ -77,12 +77,15 @@ func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Princ } if action, found := shrsLimitedMap[shr.Id]; found { if action == store.LimitAction { - oshr.Limited = true + envShr.Limited = true } } - es.Shares = append(es.Shares, oshr) + envShrs.Shares = append(envShrs.Shares, envShr) } - out = append(out, es) + envShrsList = append(envShrsList, envShrs) } - return metadata.NewOverviewOK().WithPayload(out) + return metadata.NewOverviewOK().WithPayload(&rest_model_zrok.Overview{ + AccountLimited: false, + Environments: envShrsList, + }) } diff --git a/rest_client_zrok/metadata/overview_responses.go b/rest_client_zrok/metadata/overview_responses.go index af006875..c16b2a21 100644 --- a/rest_client_zrok/metadata/overview_responses.go +++ b/rest_client_zrok/metadata/overview_responses.go @@ -51,7 +51,7 @@ OverviewOK describes a response with status code 200, with default header values overview returned */ type OverviewOK struct { - Payload rest_model_zrok.EnvironmentSharesList + Payload *rest_model_zrok.Overview } // IsSuccess returns true when this overview o k response has a 2xx status code @@ -87,14 +87,16 @@ func (o *OverviewOK) String() string { return fmt.Sprintf("[GET /overview][%d] overviewOK %+v", 200, o.Payload) } -func (o *OverviewOK) GetPayload() rest_model_zrok.EnvironmentSharesList { +func (o *OverviewOK) GetPayload() *rest_model_zrok.Overview { return o.Payload } func (o *OverviewOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + o.Payload = new(rest_model_zrok.Overview) + // response payload - if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } diff --git a/rest_model_zrok/overview.go b/rest_model_zrok/overview.go new file mode 100644 index 00000000..b4b7453e --- /dev/null +++ b/rest_model_zrok/overview.go @@ -0,0 +1,103 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package rest_model_zrok + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// Overview overview +// +// swagger:model overview +type Overview struct { + + // account limited + AccountLimited bool `json:"accountLimited,omitempty"` + + // environments + Environments EnvironmentSharesList `json:"environments,omitempty"` +} + +// Validate validates this overview +func (m *Overview) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateEnvironments(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Overview) validateEnvironments(formats strfmt.Registry) error { + if swag.IsZero(m.Environments) { // not required + return nil + } + + if err := m.Environments.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("environments") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("environments") + } + return err + } + + return nil +} + +// ContextValidate validate this overview based on the context it is used +func (m *Overview) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateEnvironments(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Overview) contextValidateEnvironments(ctx context.Context, formats strfmt.Registry) error { + + if err := m.Environments.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("environments") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("environments") + } + return err + } + + return nil +} + +// MarshalBinary interface implementation +func (m *Overview) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Overview) UnmarshalBinary(b []byte) error { + var res Overview + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 1d9d36a4..7d78648b 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -679,7 +679,7 @@ func init() { "200": { "description": "overview returned", "schema": { - "$ref": "#/definitions/environmentSharesList" + "$ref": "#/definitions/overview" } }, "500": { @@ -1211,6 +1211,17 @@ func init() { } } }, + "overview": { + "type": "object", + "properties": { + "accountLimited": { + "type": "boolean" + }, + "environments": { + "$ref": "#/definitions/environmentSharesList" + } + } + }, "principal": { "type": "object", "properties": { @@ -2156,7 +2167,7 @@ func init() { "200": { "description": "overview returned", "schema": { - "$ref": "#/definitions/environmentSharesList" + "$ref": "#/definitions/overview" } }, "500": { @@ -2688,6 +2699,17 @@ func init() { } } }, + "overview": { + "type": "object", + "properties": { + "accountLimited": { + "type": "boolean" + }, + "environments": { + "$ref": "#/definitions/environmentSharesList" + } + } + }, "principal": { "type": "object", "properties": { diff --git a/rest_server_zrok/operations/metadata/overview_responses.go b/rest_server_zrok/operations/metadata/overview_responses.go index f64c45d9..57cd555d 100644 --- a/rest_server_zrok/operations/metadata/overview_responses.go +++ b/rest_server_zrok/operations/metadata/overview_responses.go @@ -26,7 +26,7 @@ type OverviewOK struct { /* In: Body */ - Payload rest_model_zrok.EnvironmentSharesList `json:"body,omitempty"` + Payload *rest_model_zrok.Overview `json:"body,omitempty"` } // NewOverviewOK creates OverviewOK with default headers values @@ -36,13 +36,13 @@ func NewOverviewOK() *OverviewOK { } // WithPayload adds the payload to the overview o k response -func (o *OverviewOK) WithPayload(payload rest_model_zrok.EnvironmentSharesList) *OverviewOK { +func (o *OverviewOK) WithPayload(payload *rest_model_zrok.Overview) *OverviewOK { o.Payload = payload return o } // SetPayload sets the payload to the overview o k response -func (o *OverviewOK) SetPayload(payload rest_model_zrok.EnvironmentSharesList) { +func (o *OverviewOK) SetPayload(payload *rest_model_zrok.Overview) { o.Payload = payload } @@ -50,14 +50,11 @@ func (o *OverviewOK) SetPayload(payload rest_model_zrok.EnvironmentSharesList) { func (o *OverviewOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(200) - payload := o.Payload - if payload == nil { - // return empty array - payload = rest_model_zrok.EnvironmentSharesList{} - } - - if err := producer.Produce(rw, payload); err != nil { - panic(err) // let the recovery middleware deal with this + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } } } diff --git a/specs/zrok.yml b/specs/zrok.yml index 2394046f..634a01db 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -403,7 +403,7 @@ paths: 200: description: overview returned schema: - $ref: "#/definitions/environmentSharesList" + $ref: "#/definitions/overview" 500: description: internal server error schema: @@ -785,6 +785,14 @@ definitions: timestamp: type: number + overview: + type: object + properties: + accountLimited: + type: boolean + environments: + $ref: "#/definitions/environmentSharesList" + principal: type: object properties: diff --git a/ui/src/api/types.js b/ui/src/api/types.js index 0d2fb901..3c80956c 100644 --- a/ui/src/api/types.js +++ b/ui/src/api/types.js @@ -143,6 +143,14 @@ * @property {number} timestamp */ +/** + * @typedef overview + * @memberof module:types + * + * @property {boolean} accountLimited + * @property {module:types.environmentSharesList} environments + */ + /** * @typedef principal * @memberof module:types diff --git a/ui/src/console/Console.js b/ui/src/console/Console.js index e615daca..12de5ed3 100644 --- a/ui/src/console/Console.js +++ b/ui/src/console/Console.js @@ -15,12 +15,13 @@ const Console = (props) => { const openVersionModal = () => setShowVersionModal(true); const closeVersionModal = () => setShowVersionModal(false); - const [overview, setOverview] = useState([]); + const [overview, setOverview] = useState({}); useEffect(() => { let mounted = true; metadata.overview().then(resp => { if(mounted) { + console.log("init overview", resp.data); setOverview(resp.data); } }); @@ -31,6 +32,7 @@ const Console = (props) => { let interval = setInterval(() => { metadata.overview().then(resp => { if(mounted) { + console.log("update overview", resp.data); setOverview(resp.data); } }) diff --git a/ui/src/console/visualizer/Visualizer.js b/ui/src/console/visualizer/Visualizer.js index b3952a97..3341ea8f 100644 --- a/ui/src/console/visualizer/Visualizer.js +++ b/ui/src/console/visualizer/Visualizer.js @@ -10,7 +10,8 @@ const Visualizer = (props) => { const [networkGraph, setNetworkGraph] = useState({nodes: [], links: []}); useEffect(() => { - setNetworkGraph(mergeGraph(networkGraph, props.user, props.overview)); + console.log("visualizer overview", props.overview); + setNetworkGraph(mergeGraph(networkGraph, props.user, props.overview.environments)); if(isSelectionGone(networkGraph, props.selection)) { // if the selection is no longer in the network graph... diff --git a/ui/src/console/visualizer/graph.js b/ui/src/console/visualizer/graph.js index f8d0006e..24b2c917 100644 --- a/ui/src/console/visualizer/graph.js +++ b/ui/src/console/visualizer/graph.js @@ -29,43 +29,45 @@ export const mergeGraph = (oldGraph, user, newOverview) => { } newGraph.nodes.push(accountNode); - newOverview.forEach(env => { - let envNode = { - id: env.environment.zId, - label: env.environment.description, - type: "environment", - val: 50, - limited: env.limited - }; - newGraph.nodes.push(envNode); - newGraph.links.push({ - target: accountNode.id, - source: envNode.id, - color: "#04adef" - }); - if(env.shares) { - env.shares.forEach(shr => { - let shrLabel = shr.token; - if(shr.backendProxyEndpoint !== "") { - shrLabel = shr.backendProxyEndpoint; - } - let shrNode = { - id: shr.token, - envZId: env.environment.zId, - label: shrLabel, - type: "share", - limited: !!shr.limited, - val: 50 - }; - newGraph.nodes.push(shrNode); - newGraph.links.push({ - target: envNode.id, - source: shrNode.id, - color: "#04adef" - }); + if(newOverview) { + newOverview.forEach(env => { + let envNode = { + id: env.environment.zId, + label: env.environment.description, + type: "environment", + val: 50, + limited: env.limited + }; + newGraph.nodes.push(envNode); + newGraph.links.push({ + target: accountNode.id, + source: envNode.id, + color: "#04adef" }); - } - }); + if(env.shares) { + env.shares.forEach(shr => { + let shrLabel = shr.token; + if(shr.backendProxyEndpoint !== "") { + shrLabel = shr.backendProxyEndpoint; + } + let shrNode = { + id: shr.token, + envZId: env.environment.zId, + label: shrLabel, + type: "share", + limited: !!shr.limited, + val: 50 + }; + newGraph.nodes.push(shrNode); + newGraph.links.push({ + target: envNode.id, + source: shrNode.id, + color: "#04adef" + }); + }); + } + }); + } newGraph.nodes = sortNodes(newGraph.nodes); if(nodesEqual(oldGraph.nodes, newGraph.nodes)) { From d0cedaf6e55619f24c33965426a9e36004e1adba Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Tue, 16 May 2023 12:05:30 -0400 Subject: [PATCH 37/49] infrastructure for detecting limited accounts (#320) --- controller/overview.go | 14 +++++++++++++- ui/src/console/visualizer/Visualizer.js | 2 +- ui/src/console/visualizer/graph.js | 7 ++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/controller/overview.go b/controller/overview.go index d7fabb82..f557bc0d 100644 --- a/controller/overview.go +++ b/controller/overview.go @@ -84,8 +84,20 @@ func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Princ } envShrsList = append(envShrsList, envShrs) } + var alj *store.AccountLimitJournal + aljEmpty, err := str.IsAccountLimitJournalEmpty(int(principal.ID), tx) + if err != nil { + logrus.Errorf("error checking account limit journal for '%v': %v", principal.Email, err) + } + if !aljEmpty { + alj, err = str.FindLatestAccountLimitJournal(int(principal.ID), tx) + if err != nil { + logrus.Errorf("error getting latest account limit journal entry for '%v': %v", principal.Email, err) + return metadata.NewOverviewInternalServerError() + } + } return metadata.NewOverviewOK().WithPayload(&rest_model_zrok.Overview{ - AccountLimited: false, + AccountLimited: alj != nil && alj.Action == store.LimitAction, Environments: envShrsList, }) } diff --git a/ui/src/console/visualizer/Visualizer.js b/ui/src/console/visualizer/Visualizer.js index 3341ea8f..abb15db7 100644 --- a/ui/src/console/visualizer/Visualizer.js +++ b/ui/src/console/visualizer/Visualizer.js @@ -11,7 +11,7 @@ const Visualizer = (props) => { useEffect(() => { console.log("visualizer overview", props.overview); - setNetworkGraph(mergeGraph(networkGraph, props.user, props.overview.environments)); + setNetworkGraph(mergeGraph(networkGraph, props.user, props.overview.accountLimited, props.overview.environments)); if(isSelectionGone(networkGraph, props.selection)) { // if the selection is no longer in the network graph... diff --git a/ui/src/console/visualizer/graph.js b/ui/src/console/visualizer/graph.js index 24b2c917..0ea1b840 100644 --- a/ui/src/console/visualizer/graph.js +++ b/ui/src/console/visualizer/graph.js @@ -15,7 +15,7 @@ const nodesEqual = (a, b) => { return a.every((e, i) => e.id === b[i].id && e.limited === b[i].limited); } -export const mergeGraph = (oldGraph, user, newOverview) => { +export const mergeGraph = (oldGraph, user, accountLimited, newOverview) => { let newGraph = { nodes: [], links: [] @@ -25,6 +25,7 @@ export const mergeGraph = (oldGraph, user, newOverview) => { id: user.token, label: user.email, type: "account", + limited: !!accountLimited, val: 50 } newGraph.nodes.push(accountNode); @@ -36,7 +37,7 @@ export const mergeGraph = (oldGraph, user, newOverview) => { label: env.environment.description, type: "environment", val: 50, - limited: env.limited + limited: !!env.limited || accountNode.limited }; newGraph.nodes.push(envNode); newGraph.links.push({ @@ -55,7 +56,7 @@ export const mergeGraph = (oldGraph, user, newOverview) => { envZId: env.environment.zId, label: shrLabel, type: "share", - limited: !!shr.limited, + limited: !!shr.limited || envNode.limited, val: 50 }; newGraph.nodes.push(shrNode); From 75376969ca8223208bba6897adbc144e6f4c9a99 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Tue, 16 May 2023 13:45:43 -0400 Subject: [PATCH 38/49] limit details on explorer nodes (#320) --- controller/controller.go | 2 +- controller/overview.go | 134 ++++++++++++++------ controller/store/environmentLimitJournal.go | 28 ++++ ui/src/console/Console.js | 2 - ui/src/console/visualizer/Visualizer.js | 1 - ui/src/console/visualizer/graph.js | 5 +- 6 files changed, 130 insertions(+), 42 deletions(-) diff --git a/controller/controller.go b/controller/controller.go index 18f189b7..70af7b81 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -55,7 +55,7 @@ func Run(inCfg *config.Config) error { } api.MetadataGetEnvironmentDetailHandler = newEnvironmentDetailHandler() api.MetadataGetShareDetailHandler = newShareDetailHandler() - api.MetadataOverviewHandler = metadata.OverviewHandlerFunc(overviewHandler) + api.MetadataOverviewHandler = newOverviewHandler() api.MetadataVersionHandler = metadata.VersionHandlerFunc(versionHandler) api.ShareAccessHandler = newAccessHandler() api.ShareShareHandler = newShareHandler() diff --git a/controller/overview.go b/controller/overview.go index f557bc0d..2615451c 100644 --- a/controller/overview.go +++ b/controller/overview.go @@ -2,54 +2,59 @@ package controller import ( "github.com/go-openapi/runtime/middleware" + "github.com/jmoiron/sqlx" "github.com/openziti/zrok/controller/store" "github.com/openziti/zrok/rest_model_zrok" "github.com/openziti/zrok/rest_server_zrok/operations/metadata" "github.com/sirupsen/logrus" ) -func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Principal) middleware.Responder { - tx, err := str.Begin() +type overviewHandler struct{} + +func newOverviewHandler() *overviewHandler { + return &overviewHandler{} +} + +func (h *overviewHandler) Handle(_ metadata.OverviewParams, principal *rest_model_zrok.Principal) middleware.Responder { + trx, err := str.Begin() if err != nil { logrus.Errorf("error starting transaction: %v", err) return metadata.NewOverviewInternalServerError() } - defer func() { _ = tx.Rollback() }() - envs, err := str.FindEnvironmentsForAccount(int(principal.ID), tx) + defer func() { _ = trx.Rollback() }() + envs, err := str.FindEnvironmentsForAccount(int(principal.ID), trx) if err != nil { logrus.Errorf("error finding environments for '%v': %v", principal.Email, err) return metadata.NewOverviewInternalServerError() } + elm, err := newEnvironmentsLimitedMap(envs, trx) + if err != nil { + logrus.Errorf("error finding limited environments for '%v': %v", principal.Email, err) + return metadata.NewOverviewInternalServerError() + } var envShrsList rest_model_zrok.EnvironmentSharesList for _, env := range envs { - shrs, err := str.FindSharesForEnvironment(env.Id, tx) + shrs, err := str.FindSharesForEnvironment(env.Id, trx) if err != nil { logrus.Errorf("error finding shares for environment '%v': %v", env.ZId, err) return metadata.NewOverviewInternalServerError() } + slm, err := newSharesLimitedMap(shrs, trx) + if err != nil { + logrus.Errorf("error finding limited shares for environment '%v': %v", env.ZId, err) + return metadata.NewOverviewInternalServerError() + } envShrs := &rest_model_zrok.EnvironmentShares{ Environment: &rest_model_zrok.Environment{ Address: env.Address, - CreatedAt: env.CreatedAt.UnixMilli(), Description: env.Description, Host: env.Host, - UpdatedAt: env.UpdatedAt.UnixMilli(), ZID: env.ZId, + Limited: elm.isLimited(env), + CreatedAt: env.CreatedAt.UnixMilli(), + UpdatedAt: env.UpdatedAt.UnixMilli(), }, } - var shrIds []int - for i := range shrs { - shrIds = append(shrIds, shrs[i].Id) - } - shrsLimited, err := str.FindSelectedLatestShareLimitjournal(shrIds, tx) - if err != nil { - logrus.Errorf("error finding limited shares for environment '%v': %v", env.ZId, err) - return metadata.NewOverviewInternalServerError() - } - shrsLimitedMap := make(map[int]store.LimitJournalAction) - for i := range shrsLimited { - shrsLimitedMap[shrsLimited[i].ShareId] = shrsLimited[i].Action - } for _, shr := range shrs { feEndpoint := "" if shr.FrontendEndpoint != nil { @@ -72,32 +77,89 @@ func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Princ FrontendEndpoint: feEndpoint, BackendProxyEndpoint: beProxyEndpoint, Reserved: shr.Reserved, + Limited: slm.isLimited(shr), CreatedAt: shr.CreatedAt.UnixMilli(), UpdatedAt: shr.UpdatedAt.UnixMilli(), } - if action, found := shrsLimitedMap[shr.Id]; found { - if action == store.LimitAction { - envShr.Limited = true - } - } envShrs.Shares = append(envShrs.Shares, envShr) } envShrsList = append(envShrsList, envShrs) } - var alj *store.AccountLimitJournal - aljEmpty, err := str.IsAccountLimitJournalEmpty(int(principal.ID), tx) + accountLimited, err := h.isAccountLimited(principal, trx) if err != nil { - logrus.Errorf("error checking account limit journal for '%v': %v", principal.Email, err) - } - if !aljEmpty { - alj, err = str.FindLatestAccountLimitJournal(int(principal.ID), tx) - if err != nil { - logrus.Errorf("error getting latest account limit journal entry for '%v': %v", principal.Email, err) - return metadata.NewOverviewInternalServerError() - } + logrus.Errorf("error checking account limited for '%v': %v", principal.Email, err) } return metadata.NewOverviewOK().WithPayload(&rest_model_zrok.Overview{ - AccountLimited: alj != nil && alj.Action == store.LimitAction, + AccountLimited: accountLimited, Environments: envShrsList, }) } + +func (h *overviewHandler) isAccountLimited(principal *rest_model_zrok.Principal, trx *sqlx.Tx) (bool, error) { + var alj *store.AccountLimitJournal + aljEmpty, err := str.IsAccountLimitJournalEmpty(int(principal.ID), trx) + if err != nil { + return false, err + } + if !aljEmpty { + alj, err = str.FindLatestAccountLimitJournal(int(principal.ID), trx) + if err != nil { + return false, err + } + } + return alj != nil && alj.Action == store.LimitAction, nil +} + +type sharesLimitedMap struct { + v map[int]struct{} +} + +func newSharesLimitedMap(shrs []*store.Share, trx *sqlx.Tx) (*sharesLimitedMap, error) { + var shrIds []int + for i := range shrs { + shrIds = append(shrIds, shrs[i].Id) + } + shrsLimited, err := str.FindSelectedLatestShareLimitjournal(shrIds, trx) + if err != nil { + return nil, err + } + slm := &sharesLimitedMap{v: make(map[int]struct{})} + for i := range shrsLimited { + if shrsLimited[i].Action == store.LimitAction { + slm.v[shrsLimited[i].ShareId] = struct{}{} + } + } + return slm, nil +} + +func (m *sharesLimitedMap) isLimited(shr *store.Share) bool { + _, limited := m.v[shr.Id] + return limited +} + +type environmentsLimitedMap struct { + v map[int]struct{} +} + +func newEnvironmentsLimitedMap(envs []*store.Environment, trx *sqlx.Tx) (*environmentsLimitedMap, error) { + var envIds []int + for i := range envs { + envIds = append(envIds, envs[i].Id) + } + envsLimited, err := str.FindSelectedLatestEnvironmentLimitJournal(envIds, trx) + if err != nil { + return nil, err + } + elm := &environmentsLimitedMap{v: make(map[int]struct{})} + for i := range envsLimited { + if envsLimited[i].Action == store.LimitAction { + elm.v[envsLimited[i].EnvironmentId] = struct{}{} + } + } + return elm, nil +} + +func (m *environmentsLimitedMap) isLimited(env *store.Environment) bool { + _, limited := m.v[env.Id] + return limited +} diff --git a/controller/store/environmentLimitJournal.go b/controller/store/environmentLimitJournal.go index 5a7a2963..b3b3d3cc 100644 --- a/controller/store/environmentLimitJournal.go +++ b/controller/store/environmentLimitJournal.go @@ -1,6 +1,7 @@ package store import ( + "fmt" "github.com/jmoiron/sqlx" "github.com/pkg/errors" ) @@ -41,6 +42,33 @@ func (str *Store) FindLatestEnvironmentLimitJournal(envId int, trx *sqlx.Tx) (*E return j, nil } +func (str *Store) FindSelectedLatestEnvironmentLimitJournal(envIds []int, trx *sqlx.Tx) ([]*EnvironmentLimitJournal, error) { + if len(envIds) < 1 { + return nil, nil + } + in := "(" + for i := range envIds { + if i > 0 { + in += ", " + } + in += fmt.Sprintf("%d", envIds[i]) + } + in += ")" + rows, err := trx.Queryx("select id, environment_id, rx_bytes, tx_bytes, action, created_at, updated_at from environment_limit_journal where id in (select max(id) as id from environment_limit_journal group by environment_id) and environment_id in " + in) + if err != nil { + return nil, errors.Wrap(err, "error selecting all latest environment_limit_journal") + } + var eljs []*EnvironmentLimitJournal + for rows.Next() { + elj := &EnvironmentLimitJournal{} + if err := rows.StructScan(elj); err != nil { + return nil, errors.Wrap(err, "error scanning environment_limit_journal") + } + eljs = append(eljs, elj) + } + return eljs, nil +} + func (str *Store) FindAllLatestEnvironmentLimitJournal(trx *sqlx.Tx) ([]*EnvironmentLimitJournal, error) { rows, err := trx.Queryx("select id, environment_id, rx_bytes, tx_bytes, action, created_at, updated_at from environment_limit_journal where id in (select max(id) as id from environment_limit_journal group by environment_id)") if err != nil { diff --git a/ui/src/console/Console.js b/ui/src/console/Console.js index 12de5ed3..fc36ae1c 100644 --- a/ui/src/console/Console.js +++ b/ui/src/console/Console.js @@ -21,7 +21,6 @@ const Console = (props) => { let mounted = true; metadata.overview().then(resp => { if(mounted) { - console.log("init overview", resp.data); setOverview(resp.data); } }); @@ -32,7 +31,6 @@ const Console = (props) => { let interval = setInterval(() => { metadata.overview().then(resp => { if(mounted) { - console.log("update overview", resp.data); setOverview(resp.data); } }) diff --git a/ui/src/console/visualizer/Visualizer.js b/ui/src/console/visualizer/Visualizer.js index abb15db7..54182ae2 100644 --- a/ui/src/console/visualizer/Visualizer.js +++ b/ui/src/console/visualizer/Visualizer.js @@ -10,7 +10,6 @@ const Visualizer = (props) => { const [networkGraph, setNetworkGraph] = useState({nodes: [], links: []}); useEffect(() => { - console.log("visualizer overview", props.overview); setNetworkGraph(mergeGraph(networkGraph, props.user, props.overview.accountLimited, props.overview.environments)); if(isSelectionGone(networkGraph, props.selection)) { diff --git a/ui/src/console/visualizer/graph.js b/ui/src/console/visualizer/graph.js index 0ea1b840..92625c89 100644 --- a/ui/src/console/visualizer/graph.js +++ b/ui/src/console/visualizer/graph.js @@ -32,12 +32,13 @@ export const mergeGraph = (oldGraph, user, accountLimited, newOverview) => { if(newOverview) { newOverview.forEach(env => { + let limited = !!env.limited; let envNode = { id: env.environment.zId, label: env.environment.description, type: "environment", - val: 50, - limited: !!env.limited || accountNode.limited + limited: !!env.environment.limited || accountNode.limited, + val: 50 }; newGraph.nodes.push(envNode); newGraph.links.push({ From 8a9e02e46439ca44cac5a69342a0d444792fece2 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 17 May 2023 11:23:16 -0400 Subject: [PATCH 39/49] env+shrs -> env+res; making space to return frontends (#323) --- controller/environmentDetail.go | 2 +- controller/overview.go | 21 +- .../get_environment_detail_responses.go | 6 +- rest_model_zrok/environment_and_resources.go | 188 ++++++++++++++++++ rest_model_zrok/frontend.go | 59 ++++++ rest_model_zrok/frontends.go | 73 +++++++ rest_model_zrok/overview.go | 42 ++-- rest_server_zrok/embedded_spec.go | 82 ++++++-- .../get_environment_detail_responses.go | 6 +- specs/zrok.yml | 32 ++- ui/src/api/metadata.js | 2 +- ui/src/api/types.js | 15 +- 12 files changed, 467 insertions(+), 61 deletions(-) create mode 100644 rest_model_zrok/environment_and_resources.go create mode 100644 rest_model_zrok/frontend.go create mode 100644 rest_model_zrok/frontends.go diff --git a/controller/environmentDetail.go b/controller/environmentDetail.go index 7b31a33d..5f401ad2 100644 --- a/controller/environmentDetail.go +++ b/controller/environmentDetail.go @@ -25,7 +25,7 @@ func (h *environmentDetailHandler) Handle(params metadata.GetEnvironmentDetailPa logrus.Errorf("environment '%v' not found for account '%v': %v", params.EnvZID, principal.Email, err) return metadata.NewGetEnvironmentDetailNotFound() } - es := &rest_model_zrok.EnvironmentShares{ + es := &rest_model_zrok.EnvironmentAndResources{ Environment: &rest_model_zrok.Environment{ Address: senv.Address, CreatedAt: senv.CreatedAt.UnixMilli(), diff --git a/controller/overview.go b/controller/overview.go index 2615451c..72870826 100644 --- a/controller/overview.go +++ b/controller/overview.go @@ -32,7 +32,11 @@ func (h *overviewHandler) Handle(_ metadata.OverviewParams, principal *rest_mode logrus.Errorf("error finding limited environments for '%v': %v", principal.Email, err) return metadata.NewOverviewInternalServerError() } - var envShrsList rest_model_zrok.EnvironmentSharesList + accountLimited, err := h.isAccountLimited(principal, trx) + if err != nil { + logrus.Errorf("error checking account limited for '%v': %v", principal.Email, err) + } + ovr := &rest_model_zrok.Overview{AccountLimited: accountLimited} for _, env := range envs { shrs, err := str.FindSharesForEnvironment(env.Id, trx) if err != nil { @@ -44,7 +48,7 @@ func (h *overviewHandler) Handle(_ metadata.OverviewParams, principal *rest_mode logrus.Errorf("error finding limited shares for environment '%v': %v", env.ZId, err) return metadata.NewOverviewInternalServerError() } - envShrs := &rest_model_zrok.EnvironmentShares{ + envRes := &rest_model_zrok.EnvironmentAndResources{ Environment: &rest_model_zrok.Environment{ Address: env.Address, Description: env.Description, @@ -81,18 +85,11 @@ func (h *overviewHandler) Handle(_ metadata.OverviewParams, principal *rest_mode CreatedAt: shr.CreatedAt.UnixMilli(), UpdatedAt: shr.UpdatedAt.UnixMilli(), } - envShrs.Shares = append(envShrs.Shares, envShr) + envRes.Shares = append(envRes.Shares, envShr) } - envShrsList = append(envShrsList, envShrs) + ovr.Environments = append(ovr.Environments, envRes) } - accountLimited, err := h.isAccountLimited(principal, trx) - if err != nil { - logrus.Errorf("error checking account limited for '%v': %v", principal.Email, err) - } - return metadata.NewOverviewOK().WithPayload(&rest_model_zrok.Overview{ - AccountLimited: accountLimited, - Environments: envShrsList, - }) + return metadata.NewOverviewOK().WithPayload(ovr) } func (h *overviewHandler) isAccountLimited(principal *rest_model_zrok.Principal, trx *sqlx.Tx) (bool, error) { diff --git a/rest_client_zrok/metadata/get_environment_detail_responses.go b/rest_client_zrok/metadata/get_environment_detail_responses.go index df5b305d..35d86c4c 100644 --- a/rest_client_zrok/metadata/get_environment_detail_responses.go +++ b/rest_client_zrok/metadata/get_environment_detail_responses.go @@ -63,7 +63,7 @@ GetEnvironmentDetailOK describes a response with status code 200, with default h ok */ type GetEnvironmentDetailOK struct { - Payload *rest_model_zrok.EnvironmentShares + Payload *rest_model_zrok.EnvironmentAndResources } // IsSuccess returns true when this get environment detail o k response has a 2xx status code @@ -99,13 +99,13 @@ func (o *GetEnvironmentDetailOK) String() string { return fmt.Sprintf("[GET /detail/environment/{envZId}][%d] getEnvironmentDetailOK %+v", 200, o.Payload) } -func (o *GetEnvironmentDetailOK) GetPayload() *rest_model_zrok.EnvironmentShares { +func (o *GetEnvironmentDetailOK) GetPayload() *rest_model_zrok.EnvironmentAndResources { return o.Payload } func (o *GetEnvironmentDetailOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { - o.Payload = new(rest_model_zrok.EnvironmentShares) + o.Payload = new(rest_model_zrok.EnvironmentAndResources) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { diff --git a/rest_model_zrok/environment_and_resources.go b/rest_model_zrok/environment_and_resources.go new file mode 100644 index 00000000..493d2818 --- /dev/null +++ b/rest_model_zrok/environment_and_resources.go @@ -0,0 +1,188 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package rest_model_zrok + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// EnvironmentAndResources environment and resources +// +// swagger:model environmentAndResources +type EnvironmentAndResources struct { + + // environment + Environment *Environment `json:"environment,omitempty"` + + // frontends + Frontends Frontends `json:"frontends,omitempty"` + + // shares + Shares Shares `json:"shares,omitempty"` +} + +// Validate validates this environment and resources +func (m *EnvironmentAndResources) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateEnvironment(formats); err != nil { + res = append(res, err) + } + + if err := m.validateFrontends(formats); err != nil { + res = append(res, err) + } + + if err := m.validateShares(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *EnvironmentAndResources) validateEnvironment(formats strfmt.Registry) error { + if swag.IsZero(m.Environment) { // not required + return nil + } + + if m.Environment != nil { + if err := m.Environment.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("environment") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("environment") + } + return err + } + } + + return nil +} + +func (m *EnvironmentAndResources) validateFrontends(formats strfmt.Registry) error { + if swag.IsZero(m.Frontends) { // not required + return nil + } + + if err := m.Frontends.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("frontends") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("frontends") + } + return err + } + + return nil +} + +func (m *EnvironmentAndResources) validateShares(formats strfmt.Registry) error { + if swag.IsZero(m.Shares) { // not required + return nil + } + + if err := m.Shares.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("shares") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("shares") + } + return err + } + + return nil +} + +// ContextValidate validate this environment and resources based on the context it is used +func (m *EnvironmentAndResources) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateEnvironment(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateFrontends(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateShares(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *EnvironmentAndResources) contextValidateEnvironment(ctx context.Context, formats strfmt.Registry) error { + + if m.Environment != nil { + if err := m.Environment.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("environment") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("environment") + } + return err + } + } + + return nil +} + +func (m *EnvironmentAndResources) contextValidateFrontends(ctx context.Context, formats strfmt.Registry) error { + + if err := m.Frontends.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("frontends") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("frontends") + } + return err + } + + return nil +} + +func (m *EnvironmentAndResources) contextValidateShares(ctx context.Context, formats strfmt.Registry) error { + + if err := m.Shares.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("shares") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("shares") + } + return err + } + + return nil +} + +// MarshalBinary interface implementation +func (m *EnvironmentAndResources) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *EnvironmentAndResources) UnmarshalBinary(b []byte) error { + var res EnvironmentAndResources + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/rest_model_zrok/frontend.go b/rest_model_zrok/frontend.go new file mode 100644 index 00000000..65cc237d --- /dev/null +++ b/rest_model_zrok/frontend.go @@ -0,0 +1,59 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package rest_model_zrok + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// Frontend frontend +// +// swagger:model frontend +type Frontend struct { + + // created at + CreatedAt int64 `json:"createdAt,omitempty"` + + // shr token + ShrToken string `json:"shrToken,omitempty"` + + // updated at + UpdatedAt int64 `json:"updatedAt,omitempty"` + + // z Id + ZID string `json:"zId,omitempty"` +} + +// Validate validates this frontend +func (m *Frontend) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this frontend based on context it is used +func (m *Frontend) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *Frontend) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Frontend) UnmarshalBinary(b []byte) error { + var res Frontend + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/rest_model_zrok/frontends.go b/rest_model_zrok/frontends.go new file mode 100644 index 00000000..62f23b2a --- /dev/null +++ b/rest_model_zrok/frontends.go @@ -0,0 +1,73 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package rest_model_zrok + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// Frontends frontends +// +// swagger:model frontends +type Frontends []*Frontend + +// Validate validates this frontends +func (m Frontends) Validate(formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + if swag.IsZero(m[i]) { // not required + continue + } + + if m[i] != nil { + if err := m[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this frontends based on the context it is used +func (m Frontends) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_model_zrok/overview.go b/rest_model_zrok/overview.go index b4b7453e..fc2dbeb0 100644 --- a/rest_model_zrok/overview.go +++ b/rest_model_zrok/overview.go @@ -7,6 +7,7 @@ package rest_model_zrok import ( "context" + "strconv" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" @@ -22,7 +23,7 @@ type Overview struct { AccountLimited bool `json:"accountLimited,omitempty"` // environments - Environments EnvironmentSharesList `json:"environments,omitempty"` + Environments []*EnvironmentAndResources `json:"environments"` } // Validate validates this overview @@ -44,13 +45,22 @@ func (m *Overview) validateEnvironments(formats strfmt.Registry) error { return nil } - if err := m.Environments.Validate(formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("environments") - } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("environments") + for i := 0; i < len(m.Environments); i++ { + if swag.IsZero(m.Environments[i]) { // not required + continue } - return err + + if m.Environments[i] != nil { + if err := m.Environments[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("environments" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("environments" + "." + strconv.Itoa(i)) + } + return err + } + } + } return nil @@ -72,13 +82,19 @@ func (m *Overview) ContextValidate(ctx context.Context, formats strfmt.Registry) func (m *Overview) contextValidateEnvironments(ctx context.Context, formats strfmt.Registry) error { - if err := m.Environments.ContextValidate(ctx, formats); err != nil { - if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("environments") - } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("environments") + for i := 0; i < len(m.Environments); i++ { + + if m.Environments[i] != nil { + if err := m.Environments[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("environments" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("environments" + "." + strconv.Itoa(i)) + } + return err + } } - return err + } return nil diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 7d78648b..0ac1aecc 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -137,7 +137,7 @@ func init() { "200": { "description": "ok", "schema": { - "$ref": "#/definitions/environmentShares" + "$ref": "#/definitions/environmentAndResources" } }, "401": { @@ -1115,23 +1115,20 @@ func init() { } } }, - "environmentShares": { + "environmentAndResources": { "type": "object", "properties": { "environment": { "$ref": "#/definitions/environment" }, + "frontends": { + "$ref": "#/definitions/frontends" + }, "shares": { "$ref": "#/definitions/shares" } } }, - "environmentSharesList": { - "type": "array", - "items": { - "$ref": "#/definitions/environmentShares" - } - }, "environments": { "type": "array", "items": { @@ -1141,6 +1138,29 @@ func init() { "errorMessage": { "type": "string" }, + "frontend": { + "type": "object", + "properties": { + "createdAt": { + "type": "integer" + }, + "shrToken": { + "type": "string" + }, + "updatedAt": { + "type": "integer" + }, + "zId": { + "type": "string" + } + } + }, + "frontends": { + "type": "array", + "items": { + "$ref": "#/definitions/frontend" + } + }, "inviteRequest": { "type": "object", "properties": { @@ -1218,7 +1238,10 @@ func init() { "type": "boolean" }, "environments": { - "$ref": "#/definitions/environmentSharesList" + "type": "array", + "items": { + "$ref": "#/definitions/environmentAndResources" + } } } }, @@ -1625,7 +1648,7 @@ func init() { "200": { "description": "ok", "schema": { - "$ref": "#/definitions/environmentShares" + "$ref": "#/definitions/environmentAndResources" } }, "401": { @@ -2603,23 +2626,20 @@ func init() { } } }, - "environmentShares": { + "environmentAndResources": { "type": "object", "properties": { "environment": { "$ref": "#/definitions/environment" }, + "frontends": { + "$ref": "#/definitions/frontends" + }, "shares": { "$ref": "#/definitions/shares" } } }, - "environmentSharesList": { - "type": "array", - "items": { - "$ref": "#/definitions/environmentShares" - } - }, "environments": { "type": "array", "items": { @@ -2629,6 +2649,29 @@ func init() { "errorMessage": { "type": "string" }, + "frontend": { + "type": "object", + "properties": { + "createdAt": { + "type": "integer" + }, + "shrToken": { + "type": "string" + }, + "updatedAt": { + "type": "integer" + }, + "zId": { + "type": "string" + } + } + }, + "frontends": { + "type": "array", + "items": { + "$ref": "#/definitions/frontend" + } + }, "inviteRequest": { "type": "object", "properties": { @@ -2706,7 +2749,10 @@ func init() { "type": "boolean" }, "environments": { - "$ref": "#/definitions/environmentSharesList" + "type": "array", + "items": { + "$ref": "#/definitions/environmentAndResources" + } } } }, diff --git a/rest_server_zrok/operations/metadata/get_environment_detail_responses.go b/rest_server_zrok/operations/metadata/get_environment_detail_responses.go index 18ad7c1e..ff2623b3 100644 --- a/rest_server_zrok/operations/metadata/get_environment_detail_responses.go +++ b/rest_server_zrok/operations/metadata/get_environment_detail_responses.go @@ -26,7 +26,7 @@ type GetEnvironmentDetailOK struct { /* In: Body */ - Payload *rest_model_zrok.EnvironmentShares `json:"body,omitempty"` + Payload *rest_model_zrok.EnvironmentAndResources `json:"body,omitempty"` } // NewGetEnvironmentDetailOK creates GetEnvironmentDetailOK with default headers values @@ -36,13 +36,13 @@ func NewGetEnvironmentDetailOK() *GetEnvironmentDetailOK { } // WithPayload adds the payload to the get environment detail o k response -func (o *GetEnvironmentDetailOK) WithPayload(payload *rest_model_zrok.EnvironmentShares) *GetEnvironmentDetailOK { +func (o *GetEnvironmentDetailOK) WithPayload(payload *rest_model_zrok.EnvironmentAndResources) *GetEnvironmentDetailOK { o.Payload = payload return o } // SetPayload sets the payload to the get environment detail o k response -func (o *GetEnvironmentDetailOK) SetPayload(payload *rest_model_zrok.EnvironmentShares) { +func (o *GetEnvironmentDetailOK) SetPayload(payload *rest_model_zrok.EnvironmentAndResources) { o.Payload = payload } diff --git a/specs/zrok.yml b/specs/zrok.yml index 634a01db..c8f4e458 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -360,7 +360,7 @@ paths: 200: description: ok schema: - $ref: "#/definitions/environmentShares" + $ref: "#/definitions/environmentAndResources" 401: description: unauthorized 404: @@ -718,22 +718,36 @@ definitions: items: $ref: "#/definitions/environment" - environmentSharesList: - type: array - items: - $ref: "#/definitions/environmentShares" - - environmentShares: + environmentAndResources: type: object properties: environment: $ref: "#/definitions/environment" + frontends: + $ref: "#/definitions/frontends" shares: $ref: "#/definitions/shares" errorMessage: type: string + frontend: + type: object + properties: + shrToken: + type: string + zId: + type: string + createdAt: + type: integer + updatedAt: + type: integer + + frontends: + type: array + items: + $ref: "#/definitions/frontend" + inviteTokenGenerateRequest: type: object properties: @@ -791,7 +805,9 @@ definitions: accountLimited: type: boolean environments: - $ref: "#/definitions/environmentSharesList" + type: array + items: + $ref: "#/definitions/environmentAndResources" principal: type: object diff --git a/ui/src/api/metadata.js b/ui/src/api/metadata.js index 1b50438f..2ff94807 100644 --- a/ui/src/api/metadata.js +++ b/ui/src/api/metadata.js @@ -16,7 +16,7 @@ export function getAccountDetail() { /** * @param {string} envZId - * @return {Promise<module:types.environmentShares>} ok + * @return {Promise<module:types.environmentAndResources>} ok */ export function getEnvironmentDetail(envZId) { const parameters = { diff --git a/ui/src/api/types.js b/ui/src/api/types.js index 3c80956c..ae81ebab 100644 --- a/ui/src/api/types.js +++ b/ui/src/api/types.js @@ -94,13 +94,24 @@ */ /** - * @typedef environmentShares + * @typedef environmentAndResources * @memberof module:types * * @property {module:types.environment} environment + * @property {module:types.frontends} frontends * @property {module:types.shares} shares */ +/** + * @typedef frontend + * @memberof module:types + * + * @property {string} shrToken + * @property {string} zId + * @property {number} createdAt + * @property {number} updatedAt + */ + /** * @typedef inviteTokenGenerateRequest * @memberof module:types @@ -148,7 +159,7 @@ * @memberof module:types * * @property {boolean} accountLimited - * @property {module:types.environmentSharesList} environments + * @property {module:types.environmentAndResources[]} environments */ /** From bdf1a32d6a12cc4406bd460612ae1579c6b155d7 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 17 May 2023 11:39:05 -0400 Subject: [PATCH 40/49] improved limit indicators (#320) --- ui/src/console/visualizer/Network.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/ui/src/console/visualizer/Network.js b/ui/src/console/visualizer/Network.js index eeee9af2..e27929e9 100644 --- a/ui/src/console/visualizer/Network.js +++ b/ui/src/console/visualizer/Network.js @@ -3,10 +3,11 @@ import {useEffect, useRef} from "react"; import {ForceGraph2D} from "react-force-graph"; import * as d3 from "d3-force-3d"; import {roundRect} from "./draw"; -import {mdiShareVariant, mdiConsoleNetwork, mdiAccountBox} from "@mdi/js"; +import {mdiShareVariant, mdiConsoleNetwork, mdiAccountBox, mdiAlertCircle} from "@mdi/js"; const accountIcon = new Path2D(mdiAccountBox); const environmentIcon = new Path2D(mdiConsoleNetwork); +const limitIcon = new Path2D(mdiAlertCircle); const shareIcon = new Path2D(mdiShareVariant); const Network = (props) => { @@ -21,7 +22,7 @@ const Network = (props) => { }, []); const paintNode = (node, ctx) => { - let nodeColor = node.selected ? "#9BF316" : node.limited ? "#f00": "#04adef"; + let nodeColor = node.selected ? "#9BF316" : "#04adef"; let textColor = "black"; ctx.textBaseline = "middle"; @@ -36,9 +37,6 @@ const Network = (props) => { const nodeIcon = new Path2D(); let xform = new DOMMatrix(); - if(node.limited) { - ctx.fillStyle = "#f00"; - } xform.translateSelf(node.x - (nodeWidth / 2) - 6, node.y - 13); xform.scaleSelf(0.5, 0.5); switch(node.type) { @@ -59,6 +57,17 @@ const Network = (props) => { ctx.lineWidth = 0.5; ctx.stroke(nodeIcon); + if(node.limited) { + const nodeLimitIcon = new Path2D(); + let limitXform = new DOMMatrix(); + limitXform.translateSelf(node.x + (nodeWidth / 2) - 6, node.y - 13); + limitXform.scaleSelf(0.5, 0.5); + nodeLimitIcon.addPath(limitIcon, limitXform); + ctx.fillStyle = "red"; + ctx.fill(nodeLimitIcon); + ctx.stroke(nodeLimitIcon); + } + ctx.fillStyle = textColor; ctx.fillText(node.label, node.x, node.y); } From 1aaba521c7472e1171185724a2f80b7e28146e17 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 17 May 2023 11:45:48 -0400 Subject: [PATCH 41/49] fix for bad network requests before component ready --- ui/src/console/detail/share/MetricsTab.js | 48 +++++++++++++---------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/ui/src/console/detail/share/MetricsTab.js b/ui/src/console/detail/share/MetricsTab.js index d73bff6f..b5289eaf 100644 --- a/ui/src/console/detail/share/MetricsTab.js +++ b/ui/src/console/detail/share/MetricsTab.js @@ -9,28 +9,10 @@ const MetricsTab = (props) => { const [metrics1, setMetrics1] = useState(buildMetrics([])); useEffect(() => { - metadata.getShareMetrics(props.share.token) - .then(resp => { - setMetrics30(buildMetrics(resp.data)); - }); - metadata.getShareMetrics(props.share.token, {duration: "168h"}) - .then(resp => { - setMetrics7(buildMetrics(resp.data)); - }); - metadata.getShareMetrics(props.share.token, {duration: "24h"}) - .then(resp => { - setMetrics1(buildMetrics(resp.data)); - }); - }, [props.share]); - - useEffect(() => { - let mounted = true; - let interval = setInterval(() => { + if(props.share.token) { metadata.getShareMetrics(props.share.token) .then(resp => { - if(mounted) { - setMetrics30(buildMetrics(resp.data)); - } + setMetrics30(buildMetrics(resp.data)); }); metadata.getShareMetrics(props.share.token, {duration: "168h"}) .then(resp => { @@ -40,6 +22,32 @@ const MetricsTab = (props) => { .then(resp => { setMetrics1(buildMetrics(resp.data)); }); + } + }, [props.share]); + + useEffect(() => { + let mounted = true; + let interval = setInterval(() => { + if(props.share.token) { + metadata.getShareMetrics(props.share.token) + .then(resp => { + if(mounted) { + setMetrics30(buildMetrics(resp.data)); + } + }); + metadata.getShareMetrics(props.share.token, {duration: "168h"}) + .then(resp => { + if(mounted) { + setMetrics7(buildMetrics(resp.data)); + } + }); + metadata.getShareMetrics(props.share.token, {duration: "24h"}) + .then(resp => { + if(mounted) { + setMetrics1(buildMetrics(resp.data)); + } + }); + } }, 5000); return () => { mounted = false; From 9893f3f87453ca4671c960ed82a94e542dde2911 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 17 May 2023 11:50:10 -0400 Subject: [PATCH 42/49] more alerty (#320) --- ui/src/console/visualizer/Network.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/console/visualizer/Network.js b/ui/src/console/visualizer/Network.js index e27929e9..f05e4b79 100644 --- a/ui/src/console/visualizer/Network.js +++ b/ui/src/console/visualizer/Network.js @@ -3,11 +3,11 @@ import {useEffect, useRef} from "react"; import {ForceGraph2D} from "react-force-graph"; import * as d3 from "d3-force-3d"; import {roundRect} from "./draw"; -import {mdiShareVariant, mdiConsoleNetwork, mdiAccountBox, mdiAlertCircle} from "@mdi/js"; +import {mdiShareVariant, mdiConsoleNetwork, mdiAccountBox, mdiAlertOctagram} from "@mdi/js"; const accountIcon = new Path2D(mdiAccountBox); const environmentIcon = new Path2D(mdiConsoleNetwork); -const limitIcon = new Path2D(mdiAlertCircle); +const limitIcon = new Path2D(mdiAlertOctagram); const shareIcon = new Path2D(mdiShareVariant); const Network = (props) => { From 4119ab4e2bf7f727f7a6c5171c871c723d83815f Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 17 May 2023 12:46:10 -0400 Subject: [PATCH 43/49] icons --- ui/src/console/visualizer/Network.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/src/console/visualizer/Network.js b/ui/src/console/visualizer/Network.js index f05e4b79..86858266 100644 --- a/ui/src/console/visualizer/Network.js +++ b/ui/src/console/visualizer/Network.js @@ -3,10 +3,10 @@ import {useEffect, useRef} from "react"; import {ForceGraph2D} from "react-force-graph"; import * as d3 from "d3-force-3d"; import {roundRect} from "./draw"; -import {mdiShareVariant, mdiConsoleNetwork, mdiAccountBox, mdiAlertOctagram} from "@mdi/js"; +import {mdiShareVariant, mdiConsole, mdiAccount, mdiAlertOctagram} from "@mdi/js"; -const accountIcon = new Path2D(mdiAccountBox); -const environmentIcon = new Path2D(mdiConsoleNetwork); +const accountIcon = new Path2D(mdiAccount); +const environmentIcon = new Path2D(mdiConsole); const limitIcon = new Path2D(mdiAlertOctagram); const shareIcon = new Path2D(mdiShareVariant); From 05b53df6ba3ed4ec640582ff5b8b95c9964af3b0 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 17 May 2023 13:21:01 -0400 Subject: [PATCH 44/49] frontends and data plane connections to owned shares (#323) --- controller/overview.go | 42 +++++++++++++++++++++------- rest_model_zrok/frontend.go | 3 ++ rest_server_zrok/embedded_spec.go | 6 ++++ specs/zrok.yml | 2 ++ ui/src/api/types.js | 1 + ui/src/console/visualizer/Network.js | 9 ++++-- ui/src/console/visualizer/graph.js | 32 +++++++++++++++++++++ 7 files changed, 83 insertions(+), 12 deletions(-) diff --git a/controller/overview.go b/controller/overview.go index 72870826..5c3bb6f8 100644 --- a/controller/overview.go +++ b/controller/overview.go @@ -38,16 +38,6 @@ func (h *overviewHandler) Handle(_ metadata.OverviewParams, principal *rest_mode } ovr := &rest_model_zrok.Overview{AccountLimited: accountLimited} for _, env := range envs { - shrs, err := str.FindSharesForEnvironment(env.Id, trx) - if err != nil { - logrus.Errorf("error finding shares for environment '%v': %v", env.ZId, err) - return metadata.NewOverviewInternalServerError() - } - slm, err := newSharesLimitedMap(shrs, trx) - if err != nil { - logrus.Errorf("error finding limited shares for environment '%v': %v", env.ZId, err) - return metadata.NewOverviewInternalServerError() - } envRes := &rest_model_zrok.EnvironmentAndResources{ Environment: &rest_model_zrok.Environment{ Address: env.Address, @@ -59,6 +49,16 @@ func (h *overviewHandler) Handle(_ metadata.OverviewParams, principal *rest_mode UpdatedAt: env.UpdatedAt.UnixMilli(), }, } + shrs, err := str.FindSharesForEnvironment(env.Id, trx) + if err != nil { + logrus.Errorf("error finding shares for environment '%v': %v", env.ZId, err) + return metadata.NewOverviewInternalServerError() + } + slm, err := newSharesLimitedMap(shrs, trx) + if err != nil { + logrus.Errorf("error finding limited shares for environment '%v': %v", env.ZId, err) + return metadata.NewOverviewInternalServerError() + } for _, shr := range shrs { feEndpoint := "" if shr.FrontendEndpoint != nil { @@ -87,6 +87,28 @@ func (h *overviewHandler) Handle(_ metadata.OverviewParams, principal *rest_mode } envRes.Shares = append(envRes.Shares, envShr) } + fes, err := str.FindFrontendsForEnvironment(env.Id, trx) + if err != nil { + logrus.Errorf("error finding frontends for environment '%v': %v", env.ZId, err) + return metadata.NewOverviewInternalServerError() + } + for _, fe := range fes { + envFe := &rest_model_zrok.Frontend{ + ID: int64(fe.Id), + ZID: fe.ZId, + CreatedAt: fe.CreatedAt.UnixMilli(), + UpdatedAt: fe.UpdatedAt.UnixMilli(), + } + if fe.PrivateShareId != nil { + feShr, err := str.GetShare(*fe.PrivateShareId, trx) + if err != nil { + logrus.Errorf("error getting share for frontend '%v': %v", fe.ZId, err) + return metadata.NewOverviewInternalServerError() + } + envFe.ShrToken = feShr.Token + } + envRes.Frontends = append(envRes.Frontends, envFe) + } ovr.Environments = append(ovr.Environments, envRes) } return metadata.NewOverviewOK().WithPayload(ovr) diff --git a/rest_model_zrok/frontend.go b/rest_model_zrok/frontend.go index 65cc237d..3185dcaf 100644 --- a/rest_model_zrok/frontend.go +++ b/rest_model_zrok/frontend.go @@ -20,6 +20,9 @@ type Frontend struct { // created at CreatedAt int64 `json:"createdAt,omitempty"` + // id + ID int64 `json:"id,omitempty"` + // shr token ShrToken string `json:"shrToken,omitempty"` diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 0ac1aecc..40dd52ea 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -1144,6 +1144,9 @@ func init() { "createdAt": { "type": "integer" }, + "id": { + "type": "integer" + }, "shrToken": { "type": "string" }, @@ -2655,6 +2658,9 @@ func init() { "createdAt": { "type": "integer" }, + "id": { + "type": "integer" + }, "shrToken": { "type": "string" }, diff --git a/specs/zrok.yml b/specs/zrok.yml index c8f4e458..bf0ce90f 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -734,6 +734,8 @@ definitions: frontend: type: object properties: + id: + type: integer shrToken: type: string zId: diff --git a/ui/src/api/types.js b/ui/src/api/types.js index ae81ebab..b55e3d00 100644 --- a/ui/src/api/types.js +++ b/ui/src/api/types.js @@ -106,6 +106,7 @@ * @typedef frontend * @memberof module:types * + * @property {number} id * @property {string} shrToken * @property {string} zId * @property {number} createdAt diff --git a/ui/src/console/visualizer/Network.js b/ui/src/console/visualizer/Network.js index 86858266..2206a185 100644 --- a/ui/src/console/visualizer/Network.js +++ b/ui/src/console/visualizer/Network.js @@ -3,10 +3,11 @@ import {useEffect, useRef} from "react"; import {ForceGraph2D} from "react-force-graph"; import * as d3 from "d3-force-3d"; import {roundRect} from "./draw"; -import {mdiShareVariant, mdiConsole, mdiAccount, mdiAlertOctagram} from "@mdi/js"; +import {mdiShareVariant, mdiConsole, mdiAccount, mdiAlertOctagram, mdiAccessPointNetwork} from "@mdi/js"; const accountIcon = new Path2D(mdiAccount); const environmentIcon = new Path2D(mdiConsole); +const frontendIcon = new Path2D(mdiAccessPointNetwork); const limitIcon = new Path2D(mdiAlertOctagram); const shareIcon = new Path2D(mdiShareVariant); @@ -46,6 +47,9 @@ const Network = (props) => { case "environment": nodeIcon.addPath(environmentIcon, xform); break; + case "frontend": + nodeIcon.addPath(frontendIcon, xform); + break; case "account": nodeIcon.addPath(accountIcon, xform); break; @@ -84,7 +88,8 @@ const Network = (props) => { height={800} onNodeClick={nodeClicked} linkOpacity={.75} - linkWidth={1.5} + linkWidth={(l) => l.type === "data" ? 3.0 : 1.5 } + linkLineDash={(l) => l.type === "data" ? [3, 3] : [] } nodeCanvasObject={paintNode} backgroundColor={"linear-gradient(180deg, #0E0238 0%, #231069 100%);"} cooldownTicks={300} diff --git a/ui/src/console/visualizer/graph.js b/ui/src/console/visualizer/graph.js index 92625c89..afbd1e82 100644 --- a/ui/src/console/visualizer/graph.js +++ b/ui/src/console/visualizer/graph.js @@ -31,6 +31,8 @@ export const mergeGraph = (oldGraph, user, accountLimited, newOverview) => { newGraph.nodes.push(accountNode); if(newOverview) { + let allShares = {}; + let allFrontends = []; newOverview.forEach(env => { let limited = !!env.limited; let envNode = { @@ -60,6 +62,7 @@ export const mergeGraph = (oldGraph, user, accountLimited, newOverview) => { limited: !!shr.limited || envNode.limited, val: 50 }; + allShares[shr.token] = shrNode; newGraph.nodes.push(shrNode); newGraph.links.push({ target: envNode.id, @@ -68,6 +71,35 @@ export const mergeGraph = (oldGraph, user, accountLimited, newOverview) => { }); }); } + if(env.frontends) { + env.frontends.forEach(fe => { + let feNode = { + id: fe.id, + target: fe.shrToken, + label: fe.shrToken, + type: "frontend", + val: 50 + } + allFrontends.push(feNode); + newGraph.nodes.push(feNode); + newGraph.links.push({ + target: envNode.id, + source: feNode.id, + color: "#04adef" + }); + }); + } + }); + allFrontends.forEach(fe => { + let target = allShares[fe.target]; + if(target) { + newGraph.links.push({ + target: target.id, + source: fe.id, + color: "#9BF316", + type: "data", + }); + } }); } newGraph.nodes = sortNodes(newGraph.nodes); From 7963ba83b0e1ba341fea00759fffb8b0a2aa5419 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 17 May 2023 13:53:18 -0400 Subject: [PATCH 45/49] frontend details (#323) --- controller/controller.go | 1 + controller/frontendDetail.go | 60 ++++ .../get_frontend_detail_parameters.go | 149 ++++++++++ .../metadata/get_frontend_detail_responses.go | 269 ++++++++++++++++++ rest_client_zrok/metadata/metadata_client.go | 41 +++ rest_server_zrok/embedded_spec.go | 76 +++++ .../metadata/get_frontend_detail.go | 71 +++++ .../get_frontend_detail_parameters.go | 77 +++++ .../metadata/get_frontend_detail_responses.go | 134 +++++++++ .../get_frontend_detail_urlbuilder.go | 101 +++++++ rest_server_zrok/operations/zrok_api.go | 12 + specs/zrok.yml | 24 ++ ui/src/api/metadata.js | 23 ++ ui/src/console/detail/Detail.js | 6 + ui/src/console/detail/access/AccessDetail.js | 30 ++ ui/src/console/detail/access/DetailTab.js | 14 + 16 files changed, 1088 insertions(+) create mode 100644 controller/frontendDetail.go create mode 100644 rest_client_zrok/metadata/get_frontend_detail_parameters.go create mode 100644 rest_client_zrok/metadata/get_frontend_detail_responses.go create mode 100644 rest_server_zrok/operations/metadata/get_frontend_detail.go create mode 100644 rest_server_zrok/operations/metadata/get_frontend_detail_parameters.go create mode 100644 rest_server_zrok/operations/metadata/get_frontend_detail_responses.go create mode 100644 rest_server_zrok/operations/metadata/get_frontend_detail_urlbuilder.go create mode 100644 ui/src/console/detail/access/AccessDetail.js create mode 100644 ui/src/console/detail/access/DetailTab.js diff --git a/controller/controller.go b/controller/controller.go index 70af7b81..15288e3b 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -54,6 +54,7 @@ func Run(inCfg *config.Config) error { api.MetadataGetShareMetricsHandler = newGetShareMetricsHandler(cfg.Metrics.Influx) } api.MetadataGetEnvironmentDetailHandler = newEnvironmentDetailHandler() + api.MetadataGetFrontendDetailHandler = newGetFrontendDetailHandler() api.MetadataGetShareDetailHandler = newShareDetailHandler() api.MetadataOverviewHandler = newOverviewHandler() api.MetadataVersionHandler = metadata.VersionHandlerFunc(versionHandler) diff --git a/controller/frontendDetail.go b/controller/frontendDetail.go new file mode 100644 index 00000000..aca79322 --- /dev/null +++ b/controller/frontendDetail.go @@ -0,0 +1,60 @@ +package controller + +import ( + "github.com/go-openapi/runtime/middleware" + "github.com/openziti/zrok/rest_model_zrok" + "github.com/openziti/zrok/rest_server_zrok/operations/metadata" + "github.com/sirupsen/logrus" +) + +type getFrontendDetailHandler struct{} + +func newGetFrontendDetailHandler() *getFrontendDetailHandler { + return &getFrontendDetailHandler{} +} + +func (h *getFrontendDetailHandler) Handle(params metadata.GetFrontendDetailParams, principal *rest_model_zrok.Principal) middleware.Responder { + trx, err := str.Begin() + if err != nil { + logrus.Errorf("error starting transaction: %v", err) + return metadata.NewGetFrontendDetailInternalServerError() + } + defer func() { _ = trx.Rollback() }() + fe, err := str.GetFrontend(int(params.FeID), trx) + if err != nil { + logrus.Errorf("error finding share '%d': %v", params.FeID, err) + return metadata.NewGetFrontendDetailNotFound() + } + envs, err := str.FindEnvironmentsForAccount(int(principal.ID), trx) + if err != nil { + logrus.Errorf("error finding environments for account '%v': %v", principal.Email, err) + return metadata.NewGetFrontendDetailInternalServerError() + } + found := false + if fe.EnvironmentId == nil { + logrus.Errorf("non owned environment '%d' for '%v'", fe.Id, principal.Email) + return metadata.NewGetFrontendDetailNotFound() + } + for _, env := range envs { + if *fe.EnvironmentId == env.Id { + found = true + break + } + } + if !found { + logrus.Errorf("environment not matched for frontend '%d' for account '%v'", fe.Id, principal.Email) + return metadata.NewGetFrontendDetailNotFound() + } + shr, err := str.GetShare(fe.Id, trx) + if err != nil { + logrus.Errorf("error getting share for frontend '%d': %v", fe.Id, err) + return metadata.NewGetFrontendDetailInternalServerError() + } + return metadata.NewGetFrontendDetailOK().WithPayload(&rest_model_zrok.Frontend{ + ID: int64(fe.Id), + ShrToken: shr.Token, + ZID: fe.ZId, + CreatedAt: fe.CreatedAt.UnixMilli(), + UpdatedAt: fe.UpdatedAt.UnixMilli(), + }) +} diff --git a/rest_client_zrok/metadata/get_frontend_detail_parameters.go b/rest_client_zrok/metadata/get_frontend_detail_parameters.go new file mode 100644 index 00000000..4ce868af --- /dev/null +++ b/rest_client_zrok/metadata/get_frontend_detail_parameters.go @@ -0,0 +1,149 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// NewGetFrontendDetailParams creates a new GetFrontendDetailParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetFrontendDetailParams() *GetFrontendDetailParams { + return &GetFrontendDetailParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetFrontendDetailParamsWithTimeout creates a new GetFrontendDetailParams object +// with the ability to set a timeout on a request. +func NewGetFrontendDetailParamsWithTimeout(timeout time.Duration) *GetFrontendDetailParams { + return &GetFrontendDetailParams{ + timeout: timeout, + } +} + +// NewGetFrontendDetailParamsWithContext creates a new GetFrontendDetailParams object +// with the ability to set a context for a request. +func NewGetFrontendDetailParamsWithContext(ctx context.Context) *GetFrontendDetailParams { + return &GetFrontendDetailParams{ + Context: ctx, + } +} + +// NewGetFrontendDetailParamsWithHTTPClient creates a new GetFrontendDetailParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetFrontendDetailParamsWithHTTPClient(client *http.Client) *GetFrontendDetailParams { + return &GetFrontendDetailParams{ + HTTPClient: client, + } +} + +/* +GetFrontendDetailParams contains all the parameters to send to the API endpoint + + for the get frontend detail operation. + + Typically these are written to a http.Request. +*/ +type GetFrontendDetailParams struct { + + // FeID. + FeID int64 + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get frontend detail params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetFrontendDetailParams) WithDefaults() *GetFrontendDetailParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get frontend detail params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetFrontendDetailParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get frontend detail params +func (o *GetFrontendDetailParams) WithTimeout(timeout time.Duration) *GetFrontendDetailParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get frontend detail params +func (o *GetFrontendDetailParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get frontend detail params +func (o *GetFrontendDetailParams) WithContext(ctx context.Context) *GetFrontendDetailParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get frontend detail params +func (o *GetFrontendDetailParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get frontend detail params +func (o *GetFrontendDetailParams) WithHTTPClient(client *http.Client) *GetFrontendDetailParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get frontend detail params +func (o *GetFrontendDetailParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithFeID adds the feID to the get frontend detail params +func (o *GetFrontendDetailParams) WithFeID(feID int64) *GetFrontendDetailParams { + o.SetFeID(feID) + return o +} + +// SetFeID adds the feId to the get frontend detail params +func (o *GetFrontendDetailParams) SetFeID(feID int64) { + o.FeID = feID +} + +// WriteToRequest writes these params to a swagger request +func (o *GetFrontendDetailParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param feId + if err := r.SetPathParam("feId", swag.FormatInt64(o.FeID)); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_client_zrok/metadata/get_frontend_detail_responses.go b/rest_client_zrok/metadata/get_frontend_detail_responses.go new file mode 100644 index 00000000..90058d0b --- /dev/null +++ b/rest_client_zrok/metadata/get_frontend_detail_responses.go @@ -0,0 +1,269 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetFrontendDetailReader is a Reader for the GetFrontendDetail structure. +type GetFrontendDetailReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetFrontendDetailReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetFrontendDetailOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 401: + result := NewGetFrontendDetailUnauthorized() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 404: + result := NewGetFrontendDetailNotFound() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 500: + result := NewGetFrontendDetailInternalServerError() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewGetFrontendDetailOK creates a GetFrontendDetailOK with default headers values +func NewGetFrontendDetailOK() *GetFrontendDetailOK { + return &GetFrontendDetailOK{} +} + +/* +GetFrontendDetailOK describes a response with status code 200, with default header values. + +ok +*/ +type GetFrontendDetailOK struct { + Payload *rest_model_zrok.Frontend +} + +// IsSuccess returns true when this get frontend detail o k response has a 2xx status code +func (o *GetFrontendDetailOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get frontend detail o k response has a 3xx status code +func (o *GetFrontendDetailOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get frontend detail o k response has a 4xx status code +func (o *GetFrontendDetailOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get frontend detail o k response has a 5xx status code +func (o *GetFrontendDetailOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get frontend detail o k response a status code equal to that given +func (o *GetFrontendDetailOK) IsCode(code int) bool { + return code == 200 +} + +func (o *GetFrontendDetailOK) Error() string { + return fmt.Sprintf("[GET /detail/frontend/{feId}][%d] getFrontendDetailOK %+v", 200, o.Payload) +} + +func (o *GetFrontendDetailOK) String() string { + return fmt.Sprintf("[GET /detail/frontend/{feId}][%d] getFrontendDetailOK %+v", 200, o.Payload) +} + +func (o *GetFrontendDetailOK) GetPayload() *rest_model_zrok.Frontend { + return o.Payload +} + +func (o *GetFrontendDetailOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(rest_model_zrok.Frontend) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetFrontendDetailUnauthorized creates a GetFrontendDetailUnauthorized with default headers values +func NewGetFrontendDetailUnauthorized() *GetFrontendDetailUnauthorized { + return &GetFrontendDetailUnauthorized{} +} + +/* +GetFrontendDetailUnauthorized describes a response with status code 401, with default header values. + +unauthorized +*/ +type GetFrontendDetailUnauthorized struct { +} + +// IsSuccess returns true when this get frontend detail unauthorized response has a 2xx status code +func (o *GetFrontendDetailUnauthorized) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get frontend detail unauthorized response has a 3xx status code +func (o *GetFrontendDetailUnauthorized) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get frontend detail unauthorized response has a 4xx status code +func (o *GetFrontendDetailUnauthorized) IsClientError() bool { + return true +} + +// IsServerError returns true when this get frontend detail unauthorized response has a 5xx status code +func (o *GetFrontendDetailUnauthorized) IsServerError() bool { + return false +} + +// IsCode returns true when this get frontend detail unauthorized response a status code equal to that given +func (o *GetFrontendDetailUnauthorized) IsCode(code int) bool { + return code == 401 +} + +func (o *GetFrontendDetailUnauthorized) Error() string { + return fmt.Sprintf("[GET /detail/frontend/{feId}][%d] getFrontendDetailUnauthorized ", 401) +} + +func (o *GetFrontendDetailUnauthorized) String() string { + return fmt.Sprintf("[GET /detail/frontend/{feId}][%d] getFrontendDetailUnauthorized ", 401) +} + +func (o *GetFrontendDetailUnauthorized) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + +// NewGetFrontendDetailNotFound creates a GetFrontendDetailNotFound with default headers values +func NewGetFrontendDetailNotFound() *GetFrontendDetailNotFound { + return &GetFrontendDetailNotFound{} +} + +/* +GetFrontendDetailNotFound describes a response with status code 404, with default header values. + +not found +*/ +type GetFrontendDetailNotFound struct { +} + +// IsSuccess returns true when this get frontend detail not found response has a 2xx status code +func (o *GetFrontendDetailNotFound) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get frontend detail not found response has a 3xx status code +func (o *GetFrontendDetailNotFound) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get frontend detail not found response has a 4xx status code +func (o *GetFrontendDetailNotFound) IsClientError() bool { + return true +} + +// IsServerError returns true when this get frontend detail not found response has a 5xx status code +func (o *GetFrontendDetailNotFound) IsServerError() bool { + return false +} + +// IsCode returns true when this get frontend detail not found response a status code equal to that given +func (o *GetFrontendDetailNotFound) IsCode(code int) bool { + return code == 404 +} + +func (o *GetFrontendDetailNotFound) Error() string { + return fmt.Sprintf("[GET /detail/frontend/{feId}][%d] getFrontendDetailNotFound ", 404) +} + +func (o *GetFrontendDetailNotFound) String() string { + return fmt.Sprintf("[GET /detail/frontend/{feId}][%d] getFrontendDetailNotFound ", 404) +} + +func (o *GetFrontendDetailNotFound) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + +// NewGetFrontendDetailInternalServerError creates a GetFrontendDetailInternalServerError with default headers values +func NewGetFrontendDetailInternalServerError() *GetFrontendDetailInternalServerError { + return &GetFrontendDetailInternalServerError{} +} + +/* +GetFrontendDetailInternalServerError describes a response with status code 500, with default header values. + +internal server error +*/ +type GetFrontendDetailInternalServerError struct { +} + +// IsSuccess returns true when this get frontend detail internal server error response has a 2xx status code +func (o *GetFrontendDetailInternalServerError) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get frontend detail internal server error response has a 3xx status code +func (o *GetFrontendDetailInternalServerError) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get frontend detail internal server error response has a 4xx status code +func (o *GetFrontendDetailInternalServerError) IsClientError() bool { + return false +} + +// IsServerError returns true when this get frontend detail internal server error response has a 5xx status code +func (o *GetFrontendDetailInternalServerError) IsServerError() bool { + return true +} + +// IsCode returns true when this get frontend detail internal server error response a status code equal to that given +func (o *GetFrontendDetailInternalServerError) IsCode(code int) bool { + return code == 500 +} + +func (o *GetFrontendDetailInternalServerError) Error() string { + return fmt.Sprintf("[GET /detail/frontend/{feId}][%d] getFrontendDetailInternalServerError ", 500) +} + +func (o *GetFrontendDetailInternalServerError) String() string { + return fmt.Sprintf("[GET /detail/frontend/{feId}][%d] getFrontendDetailInternalServerError ", 500) +} + +func (o *GetFrontendDetailInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} diff --git a/rest_client_zrok/metadata/metadata_client.go b/rest_client_zrok/metadata/metadata_client.go index 764216b3..194736ee 100644 --- a/rest_client_zrok/metadata/metadata_client.go +++ b/rest_client_zrok/metadata/metadata_client.go @@ -40,6 +40,8 @@ type ClientService interface { GetEnvironmentMetrics(params *GetEnvironmentMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetEnvironmentMetricsOK, error) + GetFrontendDetail(params *GetFrontendDetailParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetFrontendDetailOK, error) + GetShareDetail(params *GetShareDetailParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetShareDetailOK, error) GetShareMetrics(params *GetShareMetricsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetShareMetricsOK, error) @@ -245,6 +247,45 @@ func (a *Client) GetEnvironmentMetrics(params *GetEnvironmentMetricsParams, auth panic(msg) } +/* +GetFrontendDetail get frontend detail API +*/ +func (a *Client) GetFrontendDetail(params *GetFrontendDetailParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetFrontendDetailOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetFrontendDetailParams() + } + op := &runtime.ClientOperation{ + ID: "getFrontendDetail", + Method: "GET", + PathPattern: "/detail/frontend/{feId}", + ProducesMediaTypes: []string{"application/zrok.v1+json"}, + ConsumesMediaTypes: []string{"application/zrok.v1+json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetFrontendDetailReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetFrontendDetailOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for getFrontendDetail: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + /* GetShareDetail get share detail API */ diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 40dd52ea..bb04abd4 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -152,6 +152,44 @@ func init() { } } }, + "/detail/frontend/{feId}": { + "get": { + "security": [ + { + "key": [] + } + ], + "tags": [ + "metadata" + ], + "operationId": "getFrontendDetail", + "parameters": [ + { + "type": "integer", + "name": "feId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/frontend" + } + }, + "401": { + "description": "unauthorized" + }, + "404": { + "description": "not found" + }, + "500": { + "description": "internal server error" + } + } + } + }, "/detail/share/{shrToken}": { "get": { "security": [ @@ -1666,6 +1704,44 @@ func init() { } } }, + "/detail/frontend/{feId}": { + "get": { + "security": [ + { + "key": [] + } + ], + "tags": [ + "metadata" + ], + "operationId": "getFrontendDetail", + "parameters": [ + { + "type": "integer", + "name": "feId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/frontend" + } + }, + "401": { + "description": "unauthorized" + }, + "404": { + "description": "not found" + }, + "500": { + "description": "internal server error" + } + } + } + }, "/detail/share/{shrToken}": { "get": { "security": [ diff --git a/rest_server_zrok/operations/metadata/get_frontend_detail.go b/rest_server_zrok/operations/metadata/get_frontend_detail.go new file mode 100644 index 00000000..c532c8f1 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_frontend_detail.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetFrontendDetailHandlerFunc turns a function with the right signature into a get frontend detail handler +type GetFrontendDetailHandlerFunc func(GetFrontendDetailParams, *rest_model_zrok.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetFrontendDetailHandlerFunc) Handle(params GetFrontendDetailParams, principal *rest_model_zrok.Principal) middleware.Responder { + return fn(params, principal) +} + +// GetFrontendDetailHandler interface for that can handle valid get frontend detail params +type GetFrontendDetailHandler interface { + Handle(GetFrontendDetailParams, *rest_model_zrok.Principal) middleware.Responder +} + +// NewGetFrontendDetail creates a new http.Handler for the get frontend detail operation +func NewGetFrontendDetail(ctx *middleware.Context, handler GetFrontendDetailHandler) *GetFrontendDetail { + return &GetFrontendDetail{Context: ctx, Handler: handler} +} + +/* + GetFrontendDetail swagger:route GET /detail/frontend/{feId} metadata getFrontendDetail + +GetFrontendDetail get frontend detail API +*/ +type GetFrontendDetail struct { + Context *middleware.Context + Handler GetFrontendDetailHandler +} + +func (o *GetFrontendDetail) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetFrontendDetailParams() + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + *r = *aCtx + } + var principal *rest_model_zrok.Principal + if uprinc != nil { + principal = uprinc.(*rest_model_zrok.Principal) // this is really a rest_model_zrok.Principal, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/rest_server_zrok/operations/metadata/get_frontend_detail_parameters.go b/rest_server_zrok/operations/metadata/get_frontend_detail_parameters.go new file mode 100644 index 00000000..14263c9c --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_frontend_detail_parameters.go @@ -0,0 +1,77 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// NewGetFrontendDetailParams creates a new GetFrontendDetailParams object +// +// There are no default values defined in the spec. +func NewGetFrontendDetailParams() GetFrontendDetailParams { + + return GetFrontendDetailParams{} +} + +// GetFrontendDetailParams contains all the bound params for the get frontend detail operation +// typically these are obtained from a http.Request +// +// swagger:parameters getFrontendDetail +type GetFrontendDetailParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: path + */ + FeID int64 +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetFrontendDetailParams() beforehand. +func (o *GetFrontendDetailParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + rFeID, rhkFeID, _ := route.Params.GetOK("feId") + if err := o.bindFeID(rFeID, rhkFeID, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindFeID binds and validates parameter FeID from path. +func (o *GetFrontendDetailParams) bindFeID(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + + value, err := swag.ConvertInt64(raw) + if err != nil { + return errors.InvalidType("feId", "path", "int64", raw) + } + o.FeID = value + + return nil +} diff --git a/rest_server_zrok/operations/metadata/get_frontend_detail_responses.go b/rest_server_zrok/operations/metadata/get_frontend_detail_responses.go new file mode 100644 index 00000000..538cc9bf --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_frontend_detail_responses.go @@ -0,0 +1,134 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/openziti/zrok/rest_model_zrok" +) + +// GetFrontendDetailOKCode is the HTTP code returned for type GetFrontendDetailOK +const GetFrontendDetailOKCode int = 200 + +/* +GetFrontendDetailOK ok + +swagger:response getFrontendDetailOK +*/ +type GetFrontendDetailOK struct { + + /* + In: Body + */ + Payload *rest_model_zrok.Frontend `json:"body,omitempty"` +} + +// NewGetFrontendDetailOK creates GetFrontendDetailOK with default headers values +func NewGetFrontendDetailOK() *GetFrontendDetailOK { + + return &GetFrontendDetailOK{} +} + +// WithPayload adds the payload to the get frontend detail o k response +func (o *GetFrontendDetailOK) WithPayload(payload *rest_model_zrok.Frontend) *GetFrontendDetailOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get frontend detail o k response +func (o *GetFrontendDetailOK) SetPayload(payload *rest_model_zrok.Frontend) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetFrontendDetailOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetFrontendDetailUnauthorizedCode is the HTTP code returned for type GetFrontendDetailUnauthorized +const GetFrontendDetailUnauthorizedCode int = 401 + +/* +GetFrontendDetailUnauthorized unauthorized + +swagger:response getFrontendDetailUnauthorized +*/ +type GetFrontendDetailUnauthorized struct { +} + +// NewGetFrontendDetailUnauthorized creates GetFrontendDetailUnauthorized with default headers values +func NewGetFrontendDetailUnauthorized() *GetFrontendDetailUnauthorized { + + return &GetFrontendDetailUnauthorized{} +} + +// WriteResponse to the client +func (o *GetFrontendDetailUnauthorized) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(401) +} + +// GetFrontendDetailNotFoundCode is the HTTP code returned for type GetFrontendDetailNotFound +const GetFrontendDetailNotFoundCode int = 404 + +/* +GetFrontendDetailNotFound not found + +swagger:response getFrontendDetailNotFound +*/ +type GetFrontendDetailNotFound struct { +} + +// NewGetFrontendDetailNotFound creates GetFrontendDetailNotFound with default headers values +func NewGetFrontendDetailNotFound() *GetFrontendDetailNotFound { + + return &GetFrontendDetailNotFound{} +} + +// WriteResponse to the client +func (o *GetFrontendDetailNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(404) +} + +// GetFrontendDetailInternalServerErrorCode is the HTTP code returned for type GetFrontendDetailInternalServerError +const GetFrontendDetailInternalServerErrorCode int = 500 + +/* +GetFrontendDetailInternalServerError internal server error + +swagger:response getFrontendDetailInternalServerError +*/ +type GetFrontendDetailInternalServerError struct { +} + +// NewGetFrontendDetailInternalServerError creates GetFrontendDetailInternalServerError with default headers values +func NewGetFrontendDetailInternalServerError() *GetFrontendDetailInternalServerError { + + return &GetFrontendDetailInternalServerError{} +} + +// WriteResponse to the client +func (o *GetFrontendDetailInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(500) +} diff --git a/rest_server_zrok/operations/metadata/get_frontend_detail_urlbuilder.go b/rest_server_zrok/operations/metadata/get_frontend_detail_urlbuilder.go new file mode 100644 index 00000000..ec2a9784 --- /dev/null +++ b/rest_server_zrok/operations/metadata/get_frontend_detail_urlbuilder.go @@ -0,0 +1,101 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package metadata + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" + + "github.com/go-openapi/swag" +) + +// GetFrontendDetailURL generates an URL for the get frontend detail operation +type GetFrontendDetailURL struct { + FeID int64 + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetFrontendDetailURL) WithBasePath(bp string) *GetFrontendDetailURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetFrontendDetailURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetFrontendDetailURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/detail/frontend/{feId}" + + feID := swag.FormatInt64(o.FeID) + if feID != "" { + _path = strings.Replace(_path, "{feId}", feID, -1) + } else { + return nil, errors.New("feId is required on GetFrontendDetailURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetFrontendDetailURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetFrontendDetailURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetFrontendDetailURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetFrontendDetailURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetFrontendDetailURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetFrontendDetailURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/rest_server_zrok/operations/zrok_api.go b/rest_server_zrok/operations/zrok_api.go index ed5c7721..158e03c3 100644 --- a/rest_server_zrok/operations/zrok_api.go +++ b/rest_server_zrok/operations/zrok_api.go @@ -82,6 +82,9 @@ func NewZrokAPI(spec *loads.Document) *ZrokAPI { MetadataGetEnvironmentMetricsHandler: metadata.GetEnvironmentMetricsHandlerFunc(func(params metadata.GetEnvironmentMetricsParams, principal *rest_model_zrok.Principal) middleware.Responder { return middleware.NotImplemented("operation metadata.GetEnvironmentMetrics has not yet been implemented") }), + MetadataGetFrontendDetailHandler: metadata.GetFrontendDetailHandlerFunc(func(params metadata.GetFrontendDetailParams, principal *rest_model_zrok.Principal) middleware.Responder { + return middleware.NotImplemented("operation metadata.GetFrontendDetail has not yet been implemented") + }), MetadataGetShareDetailHandler: metadata.GetShareDetailHandlerFunc(func(params metadata.GetShareDetailParams, principal *rest_model_zrok.Principal) middleware.Responder { return middleware.NotImplemented("operation metadata.GetShareDetail has not yet been implemented") }), @@ -205,6 +208,8 @@ type ZrokAPI struct { MetadataGetEnvironmentDetailHandler metadata.GetEnvironmentDetailHandler // MetadataGetEnvironmentMetricsHandler sets the operation handler for the get environment metrics operation MetadataGetEnvironmentMetricsHandler metadata.GetEnvironmentMetricsHandler + // MetadataGetFrontendDetailHandler sets the operation handler for the get frontend detail operation + MetadataGetFrontendDetailHandler metadata.GetFrontendDetailHandler // MetadataGetShareDetailHandler sets the operation handler for the get share detail operation MetadataGetShareDetailHandler metadata.GetShareDetailHandler // MetadataGetShareMetricsHandler sets the operation handler for the get share metrics operation @@ -353,6 +358,9 @@ func (o *ZrokAPI) Validate() error { if o.MetadataGetEnvironmentMetricsHandler == nil { unregistered = append(unregistered, "metadata.GetEnvironmentMetricsHandler") } + if o.MetadataGetFrontendDetailHandler == nil { + unregistered = append(unregistered, "metadata.GetFrontendDetailHandler") + } if o.MetadataGetShareDetailHandler == nil { unregistered = append(unregistered, "metadata.GetShareDetailHandler") } @@ -550,6 +558,10 @@ func (o *ZrokAPI) initHandlerCache() { if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } + o.handlers["GET"]["/detail/frontend/{feId}"] = metadata.NewGetFrontendDetail(o.context, o.MetadataGetFrontendDetailHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } o.handlers["GET"]["/detail/share/{shrToken}"] = metadata.NewGetShareDetail(o.context, o.MetadataGetShareDetailHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) diff --git a/specs/zrok.yml b/specs/zrok.yml index bf0ce90f..78aeb8d4 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -368,6 +368,30 @@ paths: 500: description: internal server error + /detail/frontend/{feId}: + get: + tags: + - metadata + security: + - key: [] + operationId: getFrontendDetail + parameters: + - name: feId + in: path + type: integer + required: true + responses: + 200: + description: ok + schema: + $ref: "#/definitions/frontend" + 401: + description: unauthorized + 404: + description: not found + 500: + description: internal server error + /detail/share/{shrToken}: get: tags: diff --git a/ui/src/api/metadata.js b/ui/src/api/metadata.js index 2ff94807..463128c3 100644 --- a/ui/src/api/metadata.js +++ b/ui/src/api/metadata.js @@ -27,6 +27,19 @@ export function getEnvironmentDetail(envZId) { return gateway.request(getEnvironmentDetailOperation, parameters) } +/** + * @param {number} feId + * @return {Promise<module:types.frontend>} ok + */ +export function getFrontendDetail(feId) { + const parameters = { + path: { + feId + } + } + return gateway.request(getFrontendDetailOperation, parameters) +} + /** * @param {string} shrToken * @return {Promise<module:types.share>} ok @@ -130,6 +143,16 @@ const getEnvironmentDetailOperation = { ] } +const getFrontendDetailOperation = { + path: '/detail/frontend/{feId}', + method: 'get', + security: [ + { + id: 'key' + } + ] +} + const getShareDetailOperation = { path: '/detail/share/{shrToken}', method: 'get', diff --git a/ui/src/console/detail/Detail.js b/ui/src/console/detail/Detail.js index 3c5b1aea..6a92761a 100644 --- a/ui/src/console/detail/Detail.js +++ b/ui/src/console/detail/Detail.js @@ -1,11 +1,17 @@ import AccountDetail from "./account/AccountDetail"; import ShareDetail from "./share/ShareDetail"; import EnvironmentDetail from "./environment/EnvironmentDetail"; +import AccessDetail from "./access/AccessDetail"; const Detail = (props) => { let detailComponent = <h1>{props.selection.id} ({props.selection.type})</h1>; + console.log("selection type", props.selection.type); switch(props.selection.type) { + case "frontend": + detailComponent = <AccessDetail selection={props.selection} />; + break; + case "environment": detailComponent = <EnvironmentDetail selection={props.selection} />; break; diff --git a/ui/src/console/detail/access/AccessDetail.js b/ui/src/console/detail/access/AccessDetail.js new file mode 100644 index 00000000..07299597 --- /dev/null +++ b/ui/src/console/detail/access/AccessDetail.js @@ -0,0 +1,30 @@ +import {mdiAccessPointNetwork} from "@mdi/js"; +import Icon from "@mdi/react"; +import {useEffect, useState} from "react"; +import {getFrontendDetail} from "../../../api/metadata"; +import {Tab, Tabs} from "react-bootstrap"; +import DetailTab from "./DetailTab"; + +const AccessDetail = (props) => { + const [detail, setDetail] = useState({}); + + useEffect(() => { + getFrontendDetail(props.selection.id) + .then(resp => { + setDetail(resp.data); + }); + }, [props.selection]); + + return ( + <div> + <h2><Icon path={mdiAccessPointNetwork} size={2} />{" "}{detail.shrToken} ({detail.id})</h2> + <Tabs defaultActiveKey={"detail"} className={"mb-3"}> + <Tab eventKey={"detail"} title={"Detail"}> + <DetailTab frontend={detail} /> + </Tab> + </Tabs> + </div> + ); +} + +export default AccessDetail; \ No newline at end of file diff --git a/ui/src/console/detail/access/DetailTab.js b/ui/src/console/detail/access/DetailTab.js new file mode 100644 index 00000000..65362c99 --- /dev/null +++ b/ui/src/console/detail/access/DetailTab.js @@ -0,0 +1,14 @@ +import SecretToggle from "../../SecretToggle"; +import PropertyTable from "../../PropertyTable"; + +const DetailTab = (props) => { + const customProperties = { + zId: row => <SecretToggle secret={row.value} /> + } + + return ( + <PropertyTable object={props.frontend} custom={customProperties} /> + ); +}; + +export default DetailTab; \ No newline at end of file From d205405aa089e65227140bbed7effd083da3f945 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Wed, 17 May 2023 20:58:07 -0400 Subject: [PATCH 46/49] use a synthetic 'node.id' for frontend nodes to fix 'undulating visualizer' issue; change detection was breaking (#323) --- ui/src/console/detail/access/AccessDetail.js | 2 +- ui/src/console/visualizer/graph.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/src/console/detail/access/AccessDetail.js b/ui/src/console/detail/access/AccessDetail.js index 07299597..455fa016 100644 --- a/ui/src/console/detail/access/AccessDetail.js +++ b/ui/src/console/detail/access/AccessDetail.js @@ -9,7 +9,7 @@ const AccessDetail = (props) => { const [detail, setDetail] = useState({}); useEffect(() => { - getFrontendDetail(props.selection.id) + getFrontendDetail(props.selection.feId) .then(resp => { setDetail(resp.data); }); diff --git a/ui/src/console/visualizer/graph.js b/ui/src/console/visualizer/graph.js index afbd1e82..cb2efb2a 100644 --- a/ui/src/console/visualizer/graph.js +++ b/ui/src/console/visualizer/graph.js @@ -74,7 +74,8 @@ export const mergeGraph = (oldGraph, user, accountLimited, newOverview) => { if(env.frontends) { env.frontends.forEach(fe => { let feNode = { - id: fe.id, + id: "fe:" + fe.id, + feId: fe.id, target: fe.shrToken, label: fe.shrToken, type: "frontend", From 1b70c6e013bed5c1636350b952931b6b6173126a Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 18 May 2023 13:10:12 -0400 Subject: [PATCH 47/49] synthetic 'selection.id', real identifiers on bespoke properties (#323) --- ui/src/console/detail/Detail.js | 1 - .../console/detail/environment/EnvironmentDetail.js | 2 +- ui/src/console/detail/environment/MetricsTab.js | 12 ++++++------ ui/src/console/detail/environment/SharesTab.js | 4 ++-- ui/src/console/detail/share/ShareDetail.js | 4 ++-- ui/src/console/visualizer/graph.js | 8 +++++--- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/ui/src/console/detail/Detail.js b/ui/src/console/detail/Detail.js index 6a92761a..582d0503 100644 --- a/ui/src/console/detail/Detail.js +++ b/ui/src/console/detail/Detail.js @@ -5,7 +5,6 @@ import AccessDetail from "./access/AccessDetail"; const Detail = (props) => { let detailComponent = <h1>{props.selection.id} ({props.selection.type})</h1>; - console.log("selection type", props.selection.type); switch(props.selection.type) { case "frontend": diff --git a/ui/src/console/detail/environment/EnvironmentDetail.js b/ui/src/console/detail/environment/EnvironmentDetail.js index 25526520..818a4d97 100644 --- a/ui/src/console/detail/environment/EnvironmentDetail.js +++ b/ui/src/console/detail/environment/EnvironmentDetail.js @@ -12,7 +12,7 @@ const EnvironmentDetail = (props) => { const [detail, setDetail] = useState({}); useEffect(() => { - getEnvironmentDetail(props.selection.id) + getEnvironmentDetail(props.selection.envZId) .then(resp => { setDetail(resp.data); }); diff --git a/ui/src/console/detail/environment/MetricsTab.js b/ui/src/console/detail/environment/MetricsTab.js index 37e447a0..bf234580 100644 --- a/ui/src/console/detail/environment/MetricsTab.js +++ b/ui/src/console/detail/environment/MetricsTab.js @@ -9,15 +9,15 @@ const MetricsTab = (props) => { const [metrics1, setMetrics1] = useState(buildMetrics([])); useEffect(() => { - metadata.getEnvironmentMetrics(props.selection.id) + metadata.getEnvironmentMetrics(props.selection.envZId) .then(resp => { setMetrics30(buildMetrics(resp.data)); }); - metadata.getEnvironmentMetrics(props.selection.id, {duration: "168h"}) + metadata.getEnvironmentMetrics(props.selection.envZId, {duration: "168h"}) .then(resp => { setMetrics7(buildMetrics(resp.data)); }); - metadata.getEnvironmentMetrics(props.selection.id, {duration: "24h"}) + metadata.getEnvironmentMetrics(props.selection.envZId, {duration: "24h"}) .then(resp => { setMetrics1(buildMetrics(resp.data)); }); @@ -26,17 +26,17 @@ const MetricsTab = (props) => { useEffect(() => { let mounted = true; let interval = setInterval(() => { - metadata.getEnvironmentMetrics(props.selection.id) + metadata.getEnvironmentMetrics(props.selection.envZId) .then(resp => { if(mounted) { setMetrics30(buildMetrics(resp.data)); } }); - metadata.getEnvironmentMetrics(props.selection.id, {duration: "168h"}) + metadata.getEnvironmentMetrics(props.selection.envZId, {duration: "168h"}) .then(resp => { setMetrics7(buildMetrics(resp.data)); }); - metadata.getEnvironmentMetrics(props.selection.id, {duration: "24h"}) + metadata.getEnvironmentMetrics(props.selection.envZId, {duration: "24h"}) .then(resp => { setMetrics1(buildMetrics(resp.data)); }); diff --git a/ui/src/console/detail/environment/SharesTab.js b/ui/src/console/detail/environment/SharesTab.js index 7a297221..d47c026b 100644 --- a/ui/src/console/detail/environment/SharesTab.js +++ b/ui/src/console/detail/environment/SharesTab.js @@ -7,7 +7,7 @@ const SharesTab = (props) => { const [detail, setDetail] = useState({}); useEffect(() => { - metadata.getEnvironmentDetail(props.selection.id) + metadata.getEnvironmentDetail(props.selection.envZId) .then(resp => { setDetail(resp.data); }); @@ -16,7 +16,7 @@ const SharesTab = (props) => { useEffect(() => { let mounted = true; let interval = setInterval(() => { - metadata.getEnvironmentDetail(props.selection.id) + metadata.getEnvironmentDetail(props.selection.envZId) .then(resp => { if(mounted) { setDetail(resp.data); diff --git a/ui/src/console/detail/share/ShareDetail.js b/ui/src/console/detail/share/ShareDetail.js index b942cd3e..b440db9b 100644 --- a/ui/src/console/detail/share/ShareDetail.js +++ b/ui/src/console/detail/share/ShareDetail.js @@ -13,7 +13,7 @@ const ShareDetail = (props) => { const [detail, setDetail] = useState({}); useEffect(() => { - metadata.getShareDetail(props.selection.id) + metadata.getShareDetail(props.selection.shrToken) .then(resp => { let detail = resp.data; detail.envZId = props.selection.envZId; @@ -24,7 +24,7 @@ const ShareDetail = (props) => { useEffect(() => { let mounted = true; let interval = setInterval(() => { - metadata.getShareDetail(props.selection.id) + metadata.getShareDetail(props.selection.shrToken) .then(resp => { if(mounted) { let detail = resp.data; diff --git a/ui/src/console/visualizer/graph.js b/ui/src/console/visualizer/graph.js index cb2efb2a..3d35d7d4 100644 --- a/ui/src/console/visualizer/graph.js +++ b/ui/src/console/visualizer/graph.js @@ -36,7 +36,8 @@ export const mergeGraph = (oldGraph, user, accountLimited, newOverview) => { newOverview.forEach(env => { let limited = !!env.limited; let envNode = { - id: env.environment.zId, + id: 'env:' + env.environment.zId, + envZId: env.environment.zId, label: env.environment.description, type: "environment", limited: !!env.environment.limited || accountNode.limited, @@ -55,7 +56,8 @@ export const mergeGraph = (oldGraph, user, accountLimited, newOverview) => { shrLabel = shr.backendProxyEndpoint; } let shrNode = { - id: shr.token, + id: 'shr:' + shr.token, + shrToken: shr.token, envZId: env.environment.zId, label: shrLabel, type: "share", @@ -74,7 +76,7 @@ export const mergeGraph = (oldGraph, user, accountLimited, newOverview) => { if(env.frontends) { env.frontends.forEach(fe => { let feNode = { - id: "fe:" + fe.id, + id: 'ac:' + fe.id, feId: fe.id, target: fe.shrToken, label: fe.shrToken, From 871bf2d52874bace74cb052fd075ed7f4431355a Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 18 May 2023 13:19:16 -0400 Subject: [PATCH 48/49] make frontend dial policies for private access names more unique (include frontend token) (#329) --- controller/access.go | 2 +- controller/limits/accountRelaxAction.go | 4 ++-- controller/limits/shareRelaxAction.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/controller/access.go b/controller/access.go index f55ea5e2..b989988e 100644 --- a/controller/access.go +++ b/controller/access.go @@ -76,7 +76,7 @@ func (h *accessHandler) Handle(params share.AccessParams, principal *rest_model_ "zrokFrontendToken": feToken, "zrokShareToken": shrToken, } - if err := zrokEdgeSdk.CreateServicePolicyDial(envZId+"-"+shr.ZId+"-dial", shr.ZId, []string{envZId}, addlTags, edge); err != nil { + if err := zrokEdgeSdk.CreateServicePolicyDial(feToken+"-"+envZId+"-"+shr.ZId+"-dial", shr.ZId, []string{envZId}, addlTags, edge); err != nil { logrus.Errorf("unable to create dial policy for user '%v': %v", principal.Email, err) return share.NewAccessInternalServerError() } diff --git a/controller/limits/accountRelaxAction.go b/controller/limits/accountRelaxAction.go index e73829dd..5e46c35f 100644 --- a/controller/limits/accountRelaxAction.go +++ b/controller/limits/accountRelaxAction.go @@ -35,11 +35,11 @@ func (a *accountRelaxAction) HandleAccount(acct *store.Account, _, _ int64, _ *B switch shr.ShareMode { case "public": if err := relaxPublicShare(a.str, a.edge, shr, trx); err != nil { - return err + return errors.Wrap(err, "error relaxing public share") } case "private": if err := relaxPrivateShare(a.str, a.edge, shr, trx); err != nil { - return err + return errors.Wrap(err, "error relaxing private share") } } } diff --git a/controller/limits/shareRelaxAction.go b/controller/limits/shareRelaxAction.go index 511ec49b..2a5912c6 100644 --- a/controller/limits/shareRelaxAction.go +++ b/controller/limits/shareRelaxAction.go @@ -72,7 +72,7 @@ func relaxPrivateShare(str *store.Store, edge *rest_management_api_client.ZitiEd "zrokFrontendToken": fe.Token, "zrokShareToken": shr.Token, } - if err := zrokEdgeSdk.CreateServicePolicyDial(env.ZId+"-"+shr.ZId+"-dial", shr.ZId, []string{env.ZId}, addlTags, edge); err != nil { + if err := zrokEdgeSdk.CreateServicePolicyDial(fe.Token+"-"+env.ZId+"-"+shr.ZId+"-dial", shr.ZId, []string{env.ZId}, addlTags, edge); err != nil { return errors.Wrapf(err, "unable to create dial policy for frontend '%v'", fe.Token) } From 78ea98626dab21cdacbbbdd8a13238c7199240a4 Mon Sep 17 00:00:00 2001 From: Michael Quigley <michael@quigley.com> Date: Thu, 18 May 2023 14:25:53 -0400 Subject: [PATCH 49/49] support deleting multiple service policies in one shot; bug in limits (#329) --- controller/disable.go | 6 +++--- controller/gc.go | 6 +++--- controller/limits/accountLimitAction.go | 2 +- controller/limits/environmentLimitAction.go | 2 +- controller/limits/shareLimitAction.go | 2 +- controller/unaccess.go | 2 +- controller/unshare.go | 4 ++-- controller/zrokEdgeSdk/sp.go | 22 +++++++++++---------- 8 files changed, 24 insertions(+), 22 deletions(-) diff --git a/controller/disable.go b/controller/disable.go index 672c7f33..af45228e 100644 --- a/controller/disable.go +++ b/controller/disable.go @@ -100,10 +100,10 @@ func (h *disableHandler) removeSharesForEnvironment(envId int, tx *sqlx.Tx, edge if err := zrokEdgeSdk.DeleteServiceEdgeRouterPolicy(env.ZId, shrToken, edge); err != nil { logrus.Error(err) } - if err := zrokEdgeSdk.DeleteServicePolicyDial(env.ZId, shrToken, edge); err != nil { + if err := zrokEdgeSdk.DeleteServicePoliciesDial(env.ZId, shrToken, edge); err != nil { logrus.Error(err) } - if err := zrokEdgeSdk.DeleteServicePolicyBind(env.ZId, shrToken, edge); err != nil { + if err := zrokEdgeSdk.DeleteServicePoliciesBind(env.ZId, shrToken, edge); err != nil { logrus.Error(err) } if err := zrokEdgeSdk.DeleteConfig(env.ZId, shrToken, edge); err != nil { @@ -129,7 +129,7 @@ func (h *disableHandler) removeFrontendsForEnvironment(envId int, tx *sqlx.Tx, e return err } for _, fe := range fes { - if err := zrokEdgeSdk.DeleteServicePolicy(env.ZId, fmt.Sprintf("tags.zrokFrontendToken=\"%v\" and type=1", fe.Token), edge); err != nil { + if err := zrokEdgeSdk.DeleteServicePolicies(env.ZId, fmt.Sprintf("tags.zrokFrontendToken=\"%v\" and type=1", fe.Token), edge); err != nil { logrus.Errorf("error removing frontend access for '%v': %v", fe.Token, err) } } diff --git a/controller/gc.go b/controller/gc.go index e0df53b9..ebeb503f 100644 --- a/controller/gc.go +++ b/controller/gc.go @@ -76,10 +76,10 @@ func gcServices(edge *rest_management_api_client.ZitiEdgeManagement, liveMap map if err := zrokEdgeSdk.DeleteServiceEdgeRouterPolicy("gc", *svc.Name, edge); err != nil { logrus.Errorf("error garbage collecting service edge router policy: %v", err) } - if err := zrokEdgeSdk.DeleteServicePolicyDial("gc", *svc.Name, edge); err != nil { + if err := zrokEdgeSdk.DeleteServicePoliciesDial("gc", *svc.Name, edge); err != nil { logrus.Errorf("error garbage collecting service dial policy: %v", err) } - if err := zrokEdgeSdk.DeleteServicePolicyBind("gc", *svc.Name, edge); err != nil { + if err := zrokEdgeSdk.DeleteServicePoliciesBind("gc", *svc.Name, edge); err != nil { logrus.Errorf("error garbage collecting service bind policy: %v", err) } if err := zrokEdgeSdk.DeleteConfig("gc", *svc.Name, edge); err != nil { @@ -137,7 +137,7 @@ func gcServicePolicies(edge *rest_management_api_client.ZitiEdgeManagement, live if _, found := liveMap[spName]; !found { logrus.Infof("garbage collecting, svcId='%v'", spName) deleteFilter := fmt.Sprintf("id=\"%v\"", *sp.ID) - if err := zrokEdgeSdk.DeleteServicePolicy("gc", deleteFilter, edge); err != nil { + if err := zrokEdgeSdk.DeleteServicePolicies("gc", deleteFilter, edge); err != nil { logrus.Errorf("error garbage collecting service policy: %v", err) } } else { diff --git a/controller/limits/accountLimitAction.go b/controller/limits/accountLimitAction.go index 919166bc..02857f5c 100644 --- a/controller/limits/accountLimitAction.go +++ b/controller/limits/accountLimitAction.go @@ -33,7 +33,7 @@ func (a *accountLimitAction) HandleAccount(acct *store.Account, rxBytes, txBytes } for _, shr := range shrs { - if err := zrokEdgeSdk.DeleteServicePolicyDial(env.ZId, shr.Token, a.edge); err != nil { + if err := zrokEdgeSdk.DeleteServicePoliciesDial(env.ZId, shr.Token, a.edge); err != nil { return errors.Wrapf(err, "error deleting dial service policy for '%v'", shr.Token) } logrus.Infof("removed dial service policy for share '%v' of environment '%v'", shr.Token, env.ZId) diff --git a/controller/limits/environmentLimitAction.go b/controller/limits/environmentLimitAction.go index ce26cafc..58e277dc 100644 --- a/controller/limits/environmentLimitAction.go +++ b/controller/limits/environmentLimitAction.go @@ -27,7 +27,7 @@ func (a *environmentLimitAction) HandleEnvironment(env *store.Environment, _, _ } for _, shr := range shrs { - if err := zrokEdgeSdk.DeleteServicePolicyDial(env.ZId, shr.Token, a.edge); err != nil { + if err := zrokEdgeSdk.DeleteServicePoliciesDial(env.ZId, shr.Token, a.edge); err != nil { return errors.Wrapf(err, "error deleting dial service policy for '%v'", shr.Token) } logrus.Infof("removed dial service policy for share '%v' of environment '%v'", shr.Token, env.ZId) diff --git a/controller/limits/shareLimitAction.go b/controller/limits/shareLimitAction.go index 7eefffb2..0b0e9fd7 100644 --- a/controller/limits/shareLimitAction.go +++ b/controller/limits/shareLimitAction.go @@ -25,7 +25,7 @@ func (a *shareLimitAction) HandleShare(shr *store.Share, _, _ int64, _ *Bandwidt return err } - if err := zrokEdgeSdk.DeleteServicePolicyDial(env.ZId, shr.Token, a.edge); err != nil { + if err := zrokEdgeSdk.DeleteServicePoliciesDial(env.ZId, shr.Token, a.edge); err != nil { return err } logrus.Infof("removed dial service policy for '%v'", shr.Token) diff --git a/controller/unaccess.go b/controller/unaccess.go index a8bcb21b..197c1b7e 100644 --- a/controller/unaccess.go +++ b/controller/unaccess.go @@ -68,7 +68,7 @@ func (h *unaccessHandler) Handle(params share.UnaccessParams, principal *rest_mo return share.NewUnaccessNotFound() } - if err := zrokEdgeSdk.DeleteServicePolicy(envZId, fmt.Sprintf("tags.zrokShareToken=\"%v\" and tags.zrokFrontendToken=\"%v\" and type=1", shrToken, feToken), edge); err != nil { + if err := zrokEdgeSdk.DeleteServicePolicies(envZId, fmt.Sprintf("tags.zrokShareToken=\"%v\" and tags.zrokFrontendToken=\"%v\" and type=1", shrToken, feToken), edge); err != nil { logrus.Errorf("error removing access to '%v' for '%v': %v", shrToken, envZId, err) return share.NewUnaccessInternalServerError() } diff --git a/controller/unshare.go b/controller/unshare.go index baf2052b..4b02021c 100644 --- a/controller/unshare.go +++ b/controller/unshare.go @@ -124,10 +124,10 @@ func (h *unshareHandler) deallocateResources(senv *store.Environment, shrToken, if err := zrokEdgeSdk.DeleteServiceEdgeRouterPolicy(senv.ZId, shrToken, edge); err != nil { return err } - if err := zrokEdgeSdk.DeleteServicePolicyDial(senv.ZId, shrToken, edge); err != nil { + if err := zrokEdgeSdk.DeleteServicePoliciesDial(senv.ZId, shrToken, edge); err != nil { return err } - if err := zrokEdgeSdk.DeleteServicePolicyBind(senv.ZId, shrToken, edge); err != nil { + if err := zrokEdgeSdk.DeleteServicePoliciesBind(senv.ZId, shrToken, edge); err != nil { return err } if err := zrokEdgeSdk.DeleteConfig(senv.ZId, shrToken, edge); err != nil { diff --git a/controller/zrokEdgeSdk/sp.go b/controller/zrokEdgeSdk/sp.go index cd790a42..89a763a7 100644 --- a/controller/zrokEdgeSdk/sp.go +++ b/controller/zrokEdgeSdk/sp.go @@ -78,16 +78,16 @@ func createServicePolicy(name string, semantic rest_model.Semantic, identityRole return resp.Payload.Data.ID, nil } -func DeleteServicePolicyBind(envZId, shrToken string, edge *rest_management_api_client.ZitiEdgeManagement) error { - return DeleteServicePolicy(envZId, fmt.Sprintf("tags.zrokShareToken=\"%v\" and type=%d", shrToken, servicePolicyBind), edge) +func DeleteServicePoliciesBind(envZId, shrToken string, edge *rest_management_api_client.ZitiEdgeManagement) error { + return DeleteServicePolicies(envZId, fmt.Sprintf("tags.zrokShareToken=\"%v\" and type=%d", shrToken, servicePolicyBind), edge) } -func DeleteServicePolicyDial(envZId, shrToken string, edge *rest_management_api_client.ZitiEdgeManagement) error { - return DeleteServicePolicy(envZId, fmt.Sprintf("tags.zrokShareToken=\"%v\" and type=%d", shrToken, servicePolicyDial), edge) +func DeleteServicePoliciesDial(envZId, shrToken string, edge *rest_management_api_client.ZitiEdgeManagement) error { + return DeleteServicePolicies(envZId, fmt.Sprintf("tags.zrokShareToken=\"%v\" and type=%d", shrToken, servicePolicyDial), edge) } -func DeleteServicePolicy(envZId, filter string, edge *rest_management_api_client.ZitiEdgeManagement) error { - limit := int64(1) +func DeleteServicePolicies(envZId, filter string, edge *rest_management_api_client.ZitiEdgeManagement) error { + limit := int64(0) offset := int64(0) listReq := &service_policy.ListServicePoliciesParams{ Filter: &filter, @@ -100,8 +100,9 @@ func DeleteServicePolicy(envZId, filter string, edge *rest_management_api_client if err != nil { return err } - if len(listResp.Payload.Data) == 1 { - spId := *(listResp.Payload.Data[0].ID) + logrus.Infof("found %d service policies to delete for '%v'", len(listResp.Payload.Data), filter) + for i := range listResp.Payload.Data { + spId := *(listResp.Payload.Data[i].ID) req := &service_policy.DeleteServicePolicyParams{ ID: spId, Context: context.Background(), @@ -112,8 +113,9 @@ func DeleteServicePolicy(envZId, filter string, edge *rest_management_api_client return err } logrus.Infof("deleted service policy '%v' for environment '%v'", spId, envZId) - } else { - logrus.Infof("did not find a service policy") + } + if len(listResp.Payload.Data) < 1 { + logrus.Warnf("did not find any service policies to delete for '%v'", filter) } return nil }