diff --git a/dashboard/components/explorer/dependency-graph/dependencygraph.tsx b/dashboard/components/explorer/dependency-graph/dependencygraph.tsx new file mode 100644 index 000000000..42e3cdaf2 --- /dev/null +++ b/dashboard/components/explorer/dependency-graph/dependencygraph.tsx @@ -0,0 +1,26 @@ +import DependencyGraphError from './dependencygrapherror'; +import DependencyGraphSkeleton from './dependencygraphskeleton'; +import DependencyGraphView from './dependencygrapview'; +import { ReactFlowData } from './hooks/useDependencyGraph'; + +export type DashboardDependencyGraphProps = { + loading: boolean; + data: ReactFlowData | undefined; + error: boolean; + fetch: () => void; +}; + +function DependencyGraph({ + loading, + data, + error, + fetch +}: DashboardDependencyGraphProps) { + if (loading) return ; + + if (error) return ; + + return ; +} + +export default DependencyGraph; diff --git a/dashboard/components/explorer/dependency-graph/dependencygrapherror.tsx b/dashboard/components/explorer/dependency-graph/dependencygrapherror.tsx new file mode 100644 index 000000000..1bbe0e785 --- /dev/null +++ b/dashboard/components/explorer/dependency-graph/dependencygrapherror.tsx @@ -0,0 +1,67 @@ +import Button from '../../button/Button'; + +type DashboardDependencyGraphErrorProps = { + fetch: () => void; +}; + +function DependencyGraphError({ fetch }: DashboardDependencyGraphErrorProps) { + return ( + <> +
+
+
+

+ Dependency Graph +

+
+

+ Analyze account resource associations +

+
+
+
+
+
+ + + + +

+ Cannot fetch Relationships +

+
+ +
+
+
+
+ + ); +} + +export default DependencyGraphError; diff --git a/dashboard/components/explorer/dependency-graph/dependencygraphskeleton.tsx b/dashboard/components/explorer/dependency-graph/dependencygraphskeleton.tsx new file mode 100644 index 000000000..dc3ee3531 --- /dev/null +++ b/dashboard/components/explorer/dependency-graph/dependencygraphskeleton.tsx @@ -0,0 +1,24 @@ +function DependencyGraphSkeleton() { + return ( + <> +
+
+
+
+
+
+
+
+
+
+
{/* TODO - Add Skeleton Grpah */}
+
+
+ + ); +} + +export default DependencyGraphSkeleton; diff --git a/dashboard/components/explorer/dependency-graph/dependencygraphwrapper.tsx b/dashboard/components/explorer/dependency-graph/dependencygraphwrapper.tsx new file mode 100644 index 000000000..2412a29c2 --- /dev/null +++ b/dashboard/components/explorer/dependency-graph/dependencygraphwrapper.tsx @@ -0,0 +1,23 @@ +import DependencyGraph from './dependencygraph'; +import useDependencyGraph from './hooks/useDependencyGraph'; + +function DependencyGraphWrapper() { + const { loading, data, error, fetch } = useDependencyGraph(); + return ( + <> +
+

+ Graph View +

+ +
+ + ); +} + +export default DependencyGraphWrapper; diff --git a/dashboard/components/explorer/dependency-graph/dependencygrapview.tsx b/dashboard/components/explorer/dependency-graph/dependencygrapview.tsx new file mode 100644 index 000000000..18497d98a --- /dev/null +++ b/dashboard/components/explorer/dependency-graph/dependencygrapview.tsx @@ -0,0 +1,35 @@ +import 'reactflow/dist/style.css'; +import CustomNode from './nodes/nodes'; +import { ReactFlowData } from './hooks/useDependencyGraph'; + +const nodeTypes = { + customNode: CustomNode +}; + +export type DashboardDependencyGraphViewProps = { + data: ReactFlowData | undefined; +}; + +function DependencyGraphView({ data }: DashboardDependencyGraphViewProps) { + return ( +
+
+
+

+ Dependency Graph +

+
+

+ Analyze account resource associations +

+
+
+
+
+
{/* TODO - Add Graph */}
+
+
+ ); +} + +export default DependencyGraphView; diff --git a/dashboard/components/explorer/dependency-graph/hooks/useDependencyGraph.tsx b/dashboard/components/explorer/dependency-graph/hooks/useDependencyGraph.tsx new file mode 100644 index 000000000..ed2a066cf --- /dev/null +++ b/dashboard/components/explorer/dependency-graph/hooks/useDependencyGraph.tsx @@ -0,0 +1,81 @@ +import { useEffect, useState } from 'react'; +import settingsService from '../../../../services/settingsService'; + +export type ReactFlowData = { + nodes: any[]; + edges: any[]; +}; + +// converting the json object into data that reactflow needs +// TODO - based on selected library +function GetData(res: any) { + const d = { + nodes: [], + edges: [] + } as ReactFlowData; + res.forEach((ele: any) => { + // check if node exist already + if (d.nodes.findIndex(element => element.id === ele.resourceId) === -1) { + const a = { + id: ele.resourceId, + type: 'customNode', + data: { label: ele.service, resource: 'AWS' }, + position: { x: 0, y: 0 } + }; + d.nodes.push(a); + } + ele.relations.forEach((rel: any) => { + // check for other node exists + if (d.nodes.findIndex(element => element.id === rel.ResourceID) === -1) { + const a = { + id: rel.ResourceID, + type: 'customNode', + data: { label: rel.Type, resource: 'AWS' }, // when supporting new provider this could be made dynamic + position: { x: 0, y: 0 } + }; + d.nodes.push(a); + } + const edge = { + id: `${ele.resourceId}-${rel.ResourceID}`, + source: ele.resourceId, + target: rel.ResourceID + }; + d.edges.push(edge); + }); + }); + return d; +} + +function useDependencyGraph() { + const [loading, setLoading] = useState(true); + const [data, setData] = useState(); + const [error, setError] = useState(false); + + function fetch() { + if (!loading) { + setLoading(true); + } + + if (error) { + setError(false); + } + + settingsService.getRelations().then(res => { + if (res === Error) { + setLoading(false); + setError(true); + } else { + setLoading(false); + setData(GetData(res)); + } + }); + } + + useEffect(() => { + fetch(); + }, []); + + return { loading, data, error, fetch }; +} + +export default useDependencyGraph; diff --git a/dashboard/components/explorer/dependency-graph/nodes/nodes.tsx b/dashboard/components/explorer/dependency-graph/nodes/nodes.tsx new file mode 100644 index 000000000..8b5e861ea --- /dev/null +++ b/dashboard/components/explorer/dependency-graph/nodes/nodes.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { Handle, Position } from 'reactflow'; +import Tippy from '@tippyjs/react'; + +type CircleNodeProps = { + data: any; + id: string; +}; + +function getInfo(data: any, id: any) { + return ( + <> +
+
+

Name: {data.label}

+

ResourceId: {id}

+
+
+ + ); +} + +function getLogo(resource: string) { + switch (resource) { + case 'AWS': + return ( + + + + ); + default: + return <>; + } +} + +const css = ` + .c { + border: 1px solid black; + } +`; +const CustomNode: React.FC = ({ data, id }) => ( + <> + + {getInfo(data, id)}}> +
+ + + {getLogo(data.resource)} +
+
+ +); + +export default CustomNode; diff --git a/dashboard/components/navbar/Navbar.tsx b/dashboard/components/navbar/Navbar.tsx index 1de7c18e1..01a71ae0a 100644 --- a/dashboard/components/navbar/Navbar.tsx +++ b/dashboard/components/navbar/Navbar.tsx @@ -9,7 +9,8 @@ function Navbar() { const router = useRouter(); const nav = [ { label: 'Dashboard', href: '/dashboard' }, - { label: 'Inventory', href: '/inventory' } + { label: 'Inventory', href: '/inventory' }, + { label: 'Explorer', href: '/explorer' } ]; return (