Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #507 #506 #484 add export for rw and ro and provide to ro a way to run queries #508

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions app/api/graph/[graph]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,18 @@ export async function GET(request: NextRequest, { params }: { params: { graph: s

const query = request.nextUrl.searchParams.get("query")
const create = request.nextUrl.searchParams.get("create")
const role = request.nextUrl.searchParams.get("role")

if (!query) throw new Error("Missing parameter 'query'")

if (create === "false" && (await client.list()).some((g) => g === graphId))
return NextResponse.json({}, { status: 200 })

const graph = client.selectGraph(graphId)
const result = await graph.query(query)

const result = role === "Read-Only"
? await graph.roQuery(query)
: await graph.query(query)

if (!result) throw new Error("something went wrong")

Expand Down
4 changes: 2 additions & 2 deletions app/api/user/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface CreateUser {
export const ROLE = new Map<string, string[]>(
[
["Admin", ["on", "~*", "&*", "+@all"]],
["Read-Write", ["on", "~*", "resetchannels", "-@all", "+graph.explain", "+graph.list", "+ping", "+graph.ro_query", "+info", "+graph.delete", "+graph.query", "+graph.profile"]],
["Read-Only", ["on", "~*", "resetchannels", "-@all", "+graph.explain", "+graph.list", "+ping", "+graph.ro_query", "+info"]]
["Read-Write", ["on", "~*", "resetchannels", "-@all", "+graph.explain", "+graph.list", "+ping", "+graph.ro_query", "+info", "+dump", "+graph.delete", "+graph.query", "+graph.profile"]],
["Read-Only", ["on", "~*", "resetchannels", "-@all", "+graph.explain", "+graph.list", "+ping", "+graph.ro_query", "+info", "+dump"]]
]
)
12 changes: 7 additions & 5 deletions app/components/EditorComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useEffect, useRef, useState } from "react"
import * as monaco from "monaco-editor";
import { prepareArg, securedFetch } from "@/lib/utils";
import { Maximize2 } from "lucide-react";
import { Session } from "next-auth";
import { Graph } from "../api/graph/model";
import Button from "./ui/Button";

Expand All @@ -25,6 +26,7 @@ interface Props {
runQuery: (query: string) => void
graph: Graph
isCollapsed: boolean
data: Session | null
}

const monacoOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
Expand Down Expand Up @@ -196,7 +198,7 @@ const getEmptySuggestions = (): Suggestions => ({
functions: []
})

export default function EditorComponent({ currentQuery, historyQueries, setCurrentQuery, maximize, runQuery, graph, isCollapsed }: Props) {
export default function EditorComponent({ currentQuery, historyQueries, setCurrentQuery, maximize, runQuery, graph, isCollapsed, data }: Props) {

const [query, setQuery] = useState(currentQuery)
const [monacoInstance, setMonacoInstance] = useState<Monaco>()
Expand Down Expand Up @@ -272,7 +274,7 @@ export default function EditorComponent({ currentQuery, historyQueries, setCurre
const addLabelsSuggestions = async (sug: monaco.languages.CompletionItem[]) => {
const labelsQuery = `CALL db.labels()`

await securedFetch(`api/graph/${prepareArg(graph.Id)}/?query=${prepareArg(labelsQuery)}`, {
await securedFetch(`api/graph/${prepareArg(graph.Id)}/?query=${prepareArg(labelsQuery)}&role=${data?.user.role}`, {
method: "GET"
}).then((res) => res.json()).then((json) => {
json.result.data.forEach(({ label }: { label: string }) => {
Expand All @@ -290,7 +292,7 @@ export default function EditorComponent({ currentQuery, historyQueries, setCurre
const addRelationshipTypesSuggestions = async (sug: monaco.languages.CompletionItem[]) => {
const relationshipTypeQuery = `CALL db.relationshipTypes()`

await securedFetch(`api/graph/${prepareArg(graph.Id)}/?query=${prepareArg(relationshipTypeQuery)}`, {
await securedFetch(`api/graph/${prepareArg(graph.Id)}/?query=${prepareArg(relationshipTypeQuery)}&role=${data?.user.role}`, {
method: "GET"
}).then((res) => res.json()).then((json) => {
json.result.data.forEach(({ relationshipType }: { relationshipType: string }) => {
Expand All @@ -308,7 +310,7 @@ export default function EditorComponent({ currentQuery, historyQueries, setCurre
const addPropertyKeysSuggestions = async (sug: monaco.languages.CompletionItem[]) => {
const propertyKeysQuery = `CALL db.propertyKeys()`

await securedFetch(`api/graph/${prepareArg(graph.Id)}/?query=${prepareArg(propertyKeysQuery)}`, {
await securedFetch(`api/graph/${prepareArg(graph.Id)}/?query=${prepareArg(propertyKeysQuery)}&role=${data?.user.role}`, {
method: "GET"
}).then((res) => res.json()).then((json) => {
json.result.data.forEach(({ propertyKey }: { propertyKey: string }) => {
Expand All @@ -325,7 +327,7 @@ export default function EditorComponent({ currentQuery, historyQueries, setCurre

const addFunctionsSuggestions = async (functions: monaco.languages.CompletionItem[]) => {
const proceduresQuery = `CALL dbms.procedures() YIELD name`
await securedFetch(`api/graph/${prepareArg(graph.Id)}/?query=${prepareArg(proceduresQuery)}`, {
await securedFetch(`api/graph/${prepareArg(graph.Id)}/?query=${prepareArg(proceduresQuery)}&role=${data?.user.role}`, {
method: "GET"
}).then((res) => res.json()).then((json) => {
[...json.result.data.map(({ name }: { name: string }) => name), ...FUNCTIONS].forEach((name: string) => {
Expand Down
17 changes: 11 additions & 6 deletions app/graph/GraphDataPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { ElementDataDefinition, prepareArg, securedFetch, Toast } from "@/lib/utils";
import { Dispatch, SetStateAction, useEffect, useState } from "react";
import { ChevronRight, PlusCircle, Trash2 } from "lucide-react";
import { Session } from "next-auth";
import Button from "../components/ui/Button";
import Input from "../components/ui/Input";
import { Graph } from "../api/graph/model";
Expand All @@ -16,6 +17,7 @@ interface Props {
onExpand: () => void;
graph: Graph;
onDeleteElement?: () => Promise<void>;
data: Session | null;
}

const excludedProperties = new Set([
Expand All @@ -26,9 +28,10 @@ const excludedProperties = new Set([
"label",
"target",
"source",
"name",
]);

export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement, graph }: Props) {
export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement, graph, data }: Props) {

const [attributes, setAttributes] = useState<string[]>([]);
const [editable, setEditable] = useState<string>("");
Expand All @@ -40,7 +43,7 @@ export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement,
const type = !("source" in obj)

useEffect(() => {
setAttributes(Object.keys(obj).filter((key) => !excludedProperties.has(key)));
setAttributes(Object.keys(obj).filter((key) => !excludedProperties.has(key) || (key === "name" && obj.name !== obj.id)));
setLabel(type ? obj.category : obj.label);
}, [obj, type]);

Expand All @@ -52,9 +55,9 @@ export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement,
})).ok

if (success) {
graph.Elements.forEach(({ data }) => {
if (data.id !== id) return
data[key] = val
graph.Elements.forEach(({ data: d }) => {
if (d.id !== id) return
d[key] = val
})
setObj((prev) => ({ ...prev, [key]: val }))
setNewVal("")
Expand Down Expand Up @@ -116,7 +119,7 @@ export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement,
</div>
<div className="w-1 grow flex gap-2">
{
editable === key ?
editable === key && data?.user.role !== "Read-Only" ?
<Input
ref={(ref) => ref?.focus()}
variant="Small"
Expand Down Expand Up @@ -202,6 +205,7 @@ export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement,
label="Add Value"
icon={<PlusCircle />}
onClick={() => setIsAddValue(true)}
disabled={data?.user.role === "Read-Only"}
/>
</div>
</ul>
Expand All @@ -211,6 +215,7 @@ export default function GraphDataPanel({ obj, setObj, onExpand, onDeleteElement,
icon={<Trash2 />}
label="Delete"
onClick={onDeleteElement}
disabled={data?.user.role === "Read-Only"}
/>
</div>
</div>
Expand Down
9 changes: 7 additions & 2 deletions app/graph/GraphView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ImperativePanelHandle } from "react-resizable-panels";
import { ChevronLeft, Maximize2, Minimize2 } from "lucide-react"
import { cn, ElementDataDefinition, prepareArg, securedFetch } from "@/lib/utils";
import dynamic from "next/dynamic";
import { Session } from "next-auth";
import { Category, Graph } from "../api/graph/model";
import DataPanel from "./GraphDataPanel";
import Labels from "./labels";
Expand Down Expand Up @@ -95,14 +96,15 @@ function getStyle() {

const getElementId = (element: ElementDataDefinition) => element.source ? { id: element.id?.slice(1), query: "()-[e]-()" } : { id: element.id, query: "(e)" }

const GraphView = forwardRef(({ graph, selectedElement, setSelectedElement, runQuery, historyQuery, historyQueries, fetchCount }: {
const GraphView = forwardRef(({ graph, selectedElement, setSelectedElement, runQuery, historyQuery, historyQueries, fetchCount, data }: {
graph: Graph
selectedElement: ElementDataDefinition | undefined
setSelectedElement: Dispatch<SetStateAction<ElementDataDefinition | undefined>>
runQuery: (query: string) => Promise<void>
historyQuery: string
historyQueries: string[]
fetchCount: () => void
data: Session | null
}, ref) => {

const [query, setQuery] = useState<string>("")
Expand Down Expand Up @@ -349,13 +351,15 @@ const GraphView = forwardRef(({ graph, selectedElement, setSelectedElement, runQ
historyQueries={historyQueries}
runQuery={runQuery}
setCurrentQuery={setQuery}
data={data}
/>
<div className="flex items-center justify-between">
<Toolbar
disabled={!graph.Id}
deleteDisabled={Object.values(selectedElements).length === 0 && !selectedElement}
deleteDisabled={(Object.values(selectedElements).length === 0 && !selectedElement) || data?.user.role === "Read-Only"}
onDeleteElement={handelDeleteElement}
chartRef={chartRef}
addDisabled
/>
{
isCollapsed && graph.Id &&
Expand Down Expand Up @@ -436,6 +440,7 @@ const GraphView = forwardRef(({ graph, selectedElement, setSelectedElement, runQ
onExpand={onExpand}
graph={graph}
onDeleteElement={handelDeleteElement}
data={data}
/>
}
</ResizablePanel>
Expand Down
8 changes: 5 additions & 3 deletions app/graph/Selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Editor } from "@monaco-editor/react";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import { editor } from "monaco-editor";
import { Toast, cn, prepareArg, securedFetch } from "@/lib/utils";
import { Session } from "next-auth";
import Combobox from "../components/ui/combobox";
import { Graph, Query } from "../api/graph/model";
import UploadGraph from "../components/graph/UploadGraph";
Expand All @@ -15,7 +16,7 @@ import Duplicate from "./Duplicate";
import SchemaView from "../schema/SchemaView";
import View from "./View";

export default function Selector({ onChange, graphName, queries, runQuery, edgesCount, nodesCount, setGraph, graph }: {
export default function Selector({ onChange, graphName, queries, runQuery, edgesCount, nodesCount, setGraph, graph, data }: {
/* eslint-disable react/require-default-props */
onChange: (selectedGraphName: string) => void
graphName: string
Expand All @@ -25,6 +26,7 @@ export default function Selector({ onChange, graphName, queries, runQuery, edges
nodesCount: number
setGraph: (graph: Graph) => void
graph: Graph
data: Session | null
}) {

const [options, setOptions] = useState<string[]>([]);
Expand Down Expand Up @@ -64,7 +66,7 @@ export default function Selector({ onChange, graphName, queries, runQuery, edges
const handleOnChange = async (name: string) => {
if (runQuery) {
const q = 'MATCH (n)-[e]-(m) return n,e,m'
const result = await securedFetch(`api/graph/${prepareArg(name)}_schema/?query=${prepareArg(q)}&create=false`, {
const result = await securedFetch(`api/graph/${prepareArg(name)}_schema/?query=${prepareArg(q)}&create=false&role=${data?.user.role}`, {
method: "GET"
})

Expand Down Expand Up @@ -256,7 +258,7 @@ export default function Selector({ onChange, graphName, queries, runQuery, edges
/>
</DialogTrigger>
<DialogComponent className="h-[90%] w-[90%]" title={`${selectedValue} Schema`}>
<SchemaView schema={schema} />
<SchemaView schema={schema} data={data}/>
</DialogComponent>
</Dialog>
</div>
Expand Down
10 changes: 7 additions & 3 deletions app/graph/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useCallback, useEffect, useState } from "react";
import { Toast, prepareArg, securedFetch } from "@/lib/utils";
import { ElementDataDefinition } from "cytoscape";
import { useSession } from "next-auth/react";
import Selector from "./Selector";
import Header from "../components/Header";
import { Graph, Query } from "../api/graph/model";
Expand All @@ -17,18 +18,19 @@ export default function Page() {
const [queries, setQueries] = useState<Query[]>([])
const [historyQuery, setHistoryQuery] = useState<string>("")
const [selectedElement, setSelectedElement] = useState<ElementDataDefinition>();
const { data } = useSession()


const fetchCount = useCallback(async () => {
if (!graphName) return
const q1 = "MATCH (n) RETURN COUNT(n) as nodes"
const q2 = "MATCH ()-[e]->() RETURN COUNT(e) as edges"

const nodes = await (await securedFetch(`api/graph/${prepareArg(graphName)}/?query=${q1}`, {
const nodes = await (await securedFetch(`api/graph/${prepareArg(graphName)}/?query=${q1}&role=${data?.user.role}`, {
method: "GET"
})).json()

const edges = await (await securedFetch(`api/graph/${prepareArg(graphName)}/?query=${q2}`, {
const edges = await (await securedFetch(`api/graph/${prepareArg(graphName)}/?query=${q2}&role=${data?.user.role}`, {
method: "GET"
})).json()

Expand Down Expand Up @@ -56,7 +58,7 @@ export default function Page() {
return null
}

const result = await securedFetch(`api/graph/${prepareArg(graphName)}/?query=${prepareArg(query)}`, {
const result = await securedFetch(`api/graph/${prepareArg(graphName)}/?query=${prepareArg(query)}&role=${data?.user.role}`, {

method: "GET"
})
Expand Down Expand Up @@ -99,6 +101,7 @@ export default function Page() {
nodesCount={nodesCount}
setGraph={setGraph}
graph={graph}
data={data}

/>
<GraphView
Expand All @@ -109,6 +112,7 @@ export default function Page() {
historyQuery={historyQuery}
historyQueries={queries.map(({ text }) => text)}
fetchCount={fetchCount}
data={data}
/>
</div>
</div>
Expand Down
Loading
Loading