This repository has been archived by the owner on Apr 16, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #17 from FalkorDB/staging
ref #3 add query box
- Loading branch information
Showing
13 changed files
with
1,993 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { NextResponse } from 'next/server'; | ||
|
||
|
||
export async function GET() { | ||
|
||
return NextResponse.json({ ok: true }); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import authOptions from '@/app/api/auth/[...nextauth]/options'; | ||
import { getServerSession } from "next-auth/next" | ||
import { NextResponse } from "next/server"; | ||
import dataSource from '../db/appDataSource'; | ||
import { UserEntity } from '../models/entities'; | ||
import { createClient, Graph } from 'redis'; | ||
|
||
|
||
export async function GET(request: Request) { | ||
|
||
const session = await getServerSession(authOptions) | ||
|
||
if (!session) { | ||
return NextResponse.json({ message: "You must be logged in." }, { status: 401 }) | ||
} | ||
|
||
const email = session.user?.email; | ||
if (!email) { | ||
return NextResponse.json({ message: "Can't find user details" }, { status: 500 }) | ||
} | ||
|
||
const user = await dataSource.manager.findOneBy(UserEntity, { | ||
email: email | ||
}) | ||
|
||
const client = await createClient( { | ||
url: `redis://:${user?.db_password}@${user?.db_host}:${user?.db_port}` | ||
}).connect(); | ||
|
||
const graph = new Graph(client, 'graph'); | ||
|
||
const {searchParams} = new URL(request.url); | ||
let q = searchParams.get('q')?.toString() || "" | ||
|
||
try{ | ||
let result = await graph.query(q) | ||
return NextResponse.json({ result: result }, { status: 200 }) | ||
} catch (err: any) { | ||
return NextResponse.json({ message: err.message }, { status: 400 }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
"use client" | ||
|
||
import * as React from "react" | ||
import { Check, ChevronsUpDown } from "lucide-react" | ||
|
||
import { cn } from "@/lib/utils" | ||
import { Button } from "@/components/ui/button" | ||
import { | ||
Command, | ||
CommandEmpty, | ||
CommandGroup, | ||
CommandInput, | ||
CommandItem, | ||
} from "@/components/ui/command" | ||
import { | ||
Popover, | ||
PopoverContent, | ||
PopoverTrigger, | ||
} from "@/components/ui/popover" | ||
|
||
export interface Option { | ||
value: string | ||
label: string | ||
} | ||
|
||
export function Combobox(props: { title: string, options: Option[] }) { | ||
const [open, setOpen] = React.useState(false) | ||
const [value, setValue] = React.useState("") | ||
|
||
return ( | ||
<Popover open={open} onOpenChange={setOpen}> | ||
<PopoverTrigger asChild> | ||
<Button | ||
variant="outline" | ||
role="combobox" | ||
aria-expanded={open} | ||
className="w-[200px] justify-between" | ||
> | ||
{value | ||
? props.options.find((option) => option.value === value)?.label | ||
: props.title} | ||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" /> | ||
</Button> | ||
</PopoverTrigger> | ||
<PopoverContent className="w-[200px] p-0"> | ||
<Command> | ||
<CommandInput placeholder="Search framework..." /> | ||
<CommandEmpty>No framework found.</CommandEmpty> | ||
<CommandGroup> | ||
{props.options.map((option) => ( | ||
<CommandItem | ||
key={option.value} | ||
onSelect={(currentValue) => { | ||
setValue(currentValue === value ? "" : currentValue) | ||
setOpen(false) | ||
}} | ||
> | ||
<Check | ||
className={cn( | ||
"mr-2 h-4 w-4", | ||
value === option.value ? "opacity-100" : "opacity-0" | ||
)} | ||
/> | ||
{option.label} | ||
</CommandItem> | ||
))} | ||
</CommandGroup> | ||
</Command> | ||
</PopoverContent> | ||
</Popover> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { useState } from 'react'; | ||
import { Button } from "@/components/ui/button" | ||
import { Input } from "@/components/ui/input" | ||
import { Label } from "@/components/ui/label" | ||
import { Combobox } from './combobox'; | ||
|
||
// A function that checks if a string is a valid Cypher query | ||
// This is a very basic and incomplete validation, you may want to use a more robust parser | ||
function isValidCypher(query: string) { | ||
// Check if the query starts with a valid clause (e.g. MATCH, CREATE, RETURN, etc.) | ||
const clauses = ['MATCH', 'CREATE', 'MERGE', 'DELETE', 'DETACH DELETE', 'SET', 'REMOVE', 'WITH', 'UNWIND', 'RETURN', 'ORDER BY', 'SKIP', 'LIMIT', 'UNION', 'CALL', 'LOAD CSV', 'FOREACH', 'PROFILE', 'EXPLAIN']; | ||
const firstWord = query.split(' ')[0].toUpperCase(); | ||
if (!clauses.includes(firstWord)) { | ||
return false; | ||
} | ||
// Check if the query has balanced parentheses and brackets | ||
const stack = []; | ||
for (let char of query) { | ||
if (char === '(' || char === '[') { | ||
stack.push(char); | ||
} else if (char === ')' || char === ']') { | ||
if (stack.length === 0) { | ||
return false; | ||
} | ||
const top = stack.pop(); | ||
if ((char === ')' && top !== '(') || (char === ']' && top !== '[')) { | ||
return false; | ||
} | ||
} | ||
} | ||
if (stack.length !== 0) { | ||
return false; | ||
} | ||
// You can add more validation rules here | ||
return true; | ||
} | ||
|
||
// A component that renders an input box for Cypher queries | ||
export function CypherInput(props: { graphs: string[], onSubmit: (query: string) => Promise<any> }) { | ||
|
||
const [results, setResults] = useState<any[]>([]); | ||
|
||
// A state variable that stores the user input | ||
const [query, setQuery] = useState(''); | ||
|
||
// A state variable that stores the validation result | ||
const [valid, setValid] = useState(true); | ||
|
||
const options = props.graphs.map((graph) => { | ||
return {label: graph, value: graph} | ||
}) | ||
|
||
// A function that handles the change event of the input box | ||
function handleChange(event: any) { | ||
// Get the new value of the input box | ||
const value = event.target.value; | ||
|
||
// Update the query state | ||
setQuery(value); | ||
|
||
// Validate the query and update the valid state | ||
setValid(isValidCypher(value)); | ||
} | ||
|
||
// A function that handles the submit event of the form | ||
async function handleSubmit(event: any) { | ||
// Prevent the default browser behavior of reloading the page | ||
event.preventDefault(); | ||
|
||
// If the query is valid, pass it to the parent component as a prop | ||
if (valid) { | ||
let newResults = await props.onSubmit(query); | ||
setResults(newResults.data?? []) | ||
} | ||
} | ||
|
||
// Return the JSX element that renders the input box and a submit button | ||
return ( | ||
<div > | ||
<Combobox options={options} title={"Select Graph..."}/> | ||
<form className="flex items-center py-4 space-x-4" onSubmit={handleSubmit}> | ||
<Label className="text-2xl" htmlFor="cypher">Query:</Label> | ||
<Input type="text" id="cypher" name="cypher" value={query} onChange={handleChange} /> | ||
<Button className="rounded-full bg-blue-600 text-1xl p-4 text-black" type="submit">Submit</Button> | ||
</form> | ||
{/* Show an error message if the query is invalid */} | ||
{!valid && <p>Invalid Cypher query. Please check the syntax.</p>} | ||
<ul> | ||
{/* Render the lines as list items */} | ||
{results.map((line, index) => ( | ||
<li key={index}>{JSON.stringify(line)}</li> | ||
))} | ||
</ul> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.