Skip to content

Commit 77ae7af

Browse files
authored
Merge pull request #817 from FalkorDB/staging
Staging
2 parents e03f2aa + db495bf commit 77ae7af

File tree

93 files changed

+5197
-1419
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+5197
-1419
lines changed

.env.local.template

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
NEXTAUTH_URL=http://localhost:3000/
33
NEXTAUTH_SECRET=SECRET
44
NEXT_PUBLIC_GOOGLE_ANALYTICS=ANALYTICS
5+
INITIAL=3000

.github/workflows/playwright.yml

Lines changed: 88 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ concurrency:
1717
cancel-in-progress: true
1818

1919
jobs:
20-
test:
20+
test-part-1:
21+
name: Run tests part 1 (except settings)
2122
timeout-minutes: 60
2223
runs-on: ubuntu-latest
2324

@@ -28,33 +29,89 @@ jobs:
2829
- 6379:6379
2930

3031
steps:
31-
- uses: actions/checkout@v4
32-
- uses: actions/setup-node@v4
33-
with:
34-
node-version: lts/*
35-
- name: Install dependencies
36-
run: npm ci
37-
- name: Install Playwright Browsers
38-
run: npx playwright install --with-deps
39-
- name: Run Playwright tests with detailed reporting
40-
run: |
41-
npm install
42-
npm run build
43-
NEXTAUTH_SECRET=SECRET npm start & npx playwright test --reporter=dot,list
44-
- name: Ensure required directories exist
45-
run: |
46-
mkdir -p playwright-report
47-
mkdir -p playwright-report/artifacts
48-
- uses: actions/upload-artifact@v4
49-
if: always()
50-
with:
51-
name: playwright-report
52-
path: playwright-report/
53-
retention-days: 30
54-
- name: Upload failed test screenshots
55-
if: always()
56-
uses: actions/upload-artifact@v4
57-
with:
58-
name: failed-test-screenshots
59-
path: playwright-report/artifacts/
60-
retention-days: 30
32+
- uses: actions/checkout@v4
33+
- uses: actions/setup-node@v4
34+
with:
35+
node-version: lts/*
36+
- name: Install dependencies
37+
run: npm ci
38+
- name: Install Playwright Browsers
39+
run: npx playwright install --with-deps
40+
- name: Run Playwright tests (shard 1 of 2)
41+
run: |
42+
npm run build
43+
NEXTAUTH_SECRET=SECRET npm start & npx playwright test --grep-invert @config --shard=1/2 --reporter=dot,list
44+
- name: Upload artifacts
45+
if: always()
46+
uses: actions/upload-artifact@v4
47+
with:
48+
name: playwright-report-part-1
49+
path: playwright-report/
50+
retention-days: 30
51+
52+
test-part-2:
53+
name: Run tests part 2 (except settings)
54+
timeout-minutes: 60
55+
runs-on: ubuntu-latest
56+
57+
services:
58+
falkordb:
59+
image: falkordb/falkordb:latest
60+
ports:
61+
- 6379:6379
62+
63+
steps:
64+
- uses: actions/checkout@v4
65+
- uses: actions/setup-node@v4
66+
with:
67+
node-version: lts/*
68+
- name: Install dependencies
69+
run: npm ci
70+
- name: Install Playwright Browsers
71+
run: npx playwright install --with-deps
72+
- name: Run Playwright tests (shard 2 of 2)
73+
run: |
74+
npm run build
75+
NEXTAUTH_SECRET=SECRET npm start & npx playwright test --grep-invert @config --shard=2/2 --reporter=dot,list
76+
- name: Upload artifacts
77+
if: always()
78+
uses: actions/upload-artifact@v4
79+
with:
80+
name: playwright-report-part-2
81+
path: playwright-report/
82+
retention-days: 30
83+
84+
run-settings-tests:
85+
name: Run settings tests
86+
runs-on: ubuntu-latest
87+
88+
services:
89+
falkordb:
90+
image: falkordb/falkordb:latest
91+
ports:
92+
- 6379:6379
93+
94+
steps:
95+
- uses: actions/checkout@v4
96+
- uses: actions/setup-node@v4
97+
with:
98+
node-version: lts/*
99+
- name: Install dependencies
100+
run: npm ci
101+
- name: Install Playwright Browsers
102+
run: npx playwright install --with-deps
103+
- name: Run only config test
104+
run: |
105+
npm run build
106+
NEXTAUTH_SECRET=SECRET npm start & npx playwright test --grep @config --reporter=dot,list
107+
- name: Ensure required directories exist
108+
run: |
109+
mkdir -p playwright-report
110+
mkdir -p playwright-report/artifacts
111+
- uses: actions/upload-artifact@v4
112+
if: always()
113+
with:
114+
name: playwright-report-settings
115+
path: playwright-report/
116+
retention-days: 30
117+

app/api/auth/[...nextauth]/options.ts

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@ import { NextResponse } from "next/server";
55
import { FalkorDBOptions } from "falkordb/dist/src/falkordb";
66
import { ErrorReply } from "redis";
77
import { v4 as uuidv4 } from 'uuid'
8+
import { GraphReply } from "falkordb/dist/src/graph";
89

9-
const connections = new Map<string, FalkorDB>();
10+
export type CACHE = {
11+
callback: (() => void) | undefined,
12+
result: GraphReply<unknown> | Error | undefined
13+
}
1014

11-
async function newClient(credentials: { host: string, port: string, password: string, username: string, tls: string, ca: string }, id: string): Promise<{ role: Role, client: FalkorDB }> {
15+
const connections = new Map<string, { client: FalkorDB, cache: Map<number, CACHE> }>();
1216

17+
async function newClient(credentials: { host: string, port: string, password: string, username: string, tls: string, ca: string }, id: string): Promise<{ role: Role, client: FalkorDB, cache: Map<number, CACHE> }> {
1318
const connectionOptions: FalkorDBOptions = credentials.tls === "true" ?
1419
{
1520
socket: {
@@ -34,15 +39,15 @@ async function newClient(credentials: { host: string, port: string, password: st
3439
const client = await FalkorDB.connect(connectionOptions)
3540

3641
// Save connection in connections map for later use
37-
connections.set(id, client)
42+
connections.set(id, { client, cache: new Map<number, CACHE>() })
3843

3944
client.on('error', err => {
4045
// Close coonection on error and remove from connections map
4146
console.error('FalkorDB Client Error', err)
4247
const connection = connections.get(id)
4348
if (connection) {
4449
connections.delete(id)
45-
connection.close()
50+
connection.client.close()
4651
.catch((e) => {
4752
console.warn('FalkorDB Client Disconnect Error', e)
4853
})
@@ -51,10 +56,11 @@ async function newClient(credentials: { host: string, port: string, password: st
5156

5257
// Verify connection and Role
5358
const connection = await client.connection
59+
const cache = new Map<number, CACHE>()
5460

5561
try {
5662
await connection.aclGetUser(credentials.username || "default")
57-
return { role: "Admin", client }
63+
return { role: "Admin", client, cache }
5864
} catch (err) {
5965
if (err instanceof ErrorReply && (err as ErrorReply).message.startsWith("NOPERM")) {
6066
console.debug(err);
@@ -66,13 +72,13 @@ async function newClient(credentials: { host: string, port: string, password: st
6672
} catch (err) {
6773
if ((err as Error).message.includes("permissions")) {
6874
console.debug(err);
69-
return { role: "Read-Only", client }
75+
return { role: "Read-Only", client, cache }
7076
}
7177
console.debug(err);
72-
return { role: "Read-Write", client }
78+
return { role: "Read-Write", client, cache }
7379
}
7480

75-
return { role: "Admin", client }
81+
return { role: "Admin", client, cache }
7682
}
7783

7884
function generateTimeUUID() {
@@ -170,25 +176,27 @@ export async function getClient() {
170176
}
171177

172178
const { user } = session;
173-
let client = connections.get(user.id)
179+
let connection = connections.get(user.id)
174180

175181
// If client is not found, create a new one
176-
if (!client) {
177-
client = (await newClient({
182+
if (!connection) {
183+
connection = (await newClient({
178184
host: user.host,
179185
port: user.port.toString() ?? "6379",
180186
username: user.username,
181187
password: user.password,
182188
tls: String(user.tls),
183189
ca: user.ca
184-
}, user.id)).client
190+
}, user.id))
185191
}
186192

193+
const { client, cache } = connection
194+
187195
if (!client) {
188196
return NextResponse.json({ message: "Not authenticated" }, { status: 401 })
189197
}
190198

191-
return { client, user }
199+
return { client, user, cache }
192200
}
193201

194202
export default authOptions
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { NextResponse, NextRequest } from "next/server"
2+
import { getClient } from "@/app/api/auth/[...nextauth]/options"
3+
4+
export async function POST(request: NextRequest, { params }: { params: Promise<{ graph: string, node: string, key: string }> }) {
5+
const session = await getClient()
6+
7+
if (session instanceof NextResponse) {
8+
return session
9+
}
10+
11+
const { client } = session
12+
13+
const { graph: graphId, node, key } = await params
14+
const nodeId = Number(node)
15+
const { value, type } = await request.json()
16+
17+
try {
18+
if (type === undefined) throw new Error("Type is required")
19+
20+
const graph = client.selectGraph(graphId);
21+
22+
if (!value) throw new Error("Value is required")
23+
24+
const query = type
25+
? `MATCH (n) WHERE ID(n) = $nodeId SET n.${key} = $value`
26+
: `MATCH (n)-[e]-(m) WHERE ID(e) = $nodeId SET e.${key} = $value`;
27+
28+
const result = await graph.query(query, { params: { nodeId, value } });
29+
30+
if (!result) throw new Error("Something went wrong")
31+
32+
return NextResponse.json({ result }, { status: 200 })
33+
} catch (err: unknown) {
34+
console.error(err)
35+
return NextResponse.json({ message: (err as Error).message }, { status: 400 })
36+
}
37+
}
38+
39+
export async function DELETE(request: NextRequest, { params }: { params: Promise<{ graph: string, node: string, key: string }> }) {
40+
const session = await getClient()
41+
if (session instanceof NextResponse) {
42+
return session
43+
}
44+
45+
const { client } = session
46+
47+
const { graph: graphId, node, key } = await params
48+
const nodeId = Number(node)
49+
const { type } = await request.json()
50+
51+
try {
52+
if (type === undefined) throw new Error("Type is required")
53+
54+
const graph = client.selectGraph(graphId);
55+
56+
const query = type
57+
? `MATCH (n) WHERE ID(n) = $nodeId SET n.${key} = NULL`
58+
: `MATCH (n)-[e]-(m) WHERE ID(e) = $nodeId SET e.${key} = NULL`;
59+
60+
const result = await graph.query(query, { params: { nodeId } });
61+
62+
if (!result) throw new Error("Something went wrong")
63+
64+
return NextResponse.json({ result }, { status: 200 })
65+
} catch (err: unknown) {
66+
console.error(err)
67+
return NextResponse.json({ message: (err as Error).message }, { status: 400 })
68+
}
69+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { getClient } from "@/app/api/auth/[...nextauth]/options";
2+
import { NextRequest, NextResponse } from "next/server";
3+
4+
export async function DELETE(request: NextRequest, { params }: { params: Promise<{ graph: string, node: string }> }) {
5+
6+
const session = await getClient()
7+
8+
if (session instanceof NextResponse) {
9+
return session
10+
}
11+
12+
const { client } = session
13+
14+
const { graph: graphId, node } = await params
15+
const nodeId = Number(node)
16+
const { label } = await request.json()
17+
18+
try {
19+
if (!label) throw new Error("Label is required")
20+
21+
const query = `MATCH (n) WHERE ID(n) = $nodeId REMOVE n:${label}`
22+
const graph = client.selectGraph(graphId);
23+
const result = await graph.query(query, { params: { nodeId } })
24+
25+
if (!result) throw new Error("Something went wrong")
26+
27+
return NextResponse.json({ message: "Label removed successfully" }, { status: 200 });
28+
} catch (err: unknown) {
29+
console.error(err)
30+
return NextResponse.json({ message: (err as Error).message }, { status: 400 })
31+
}
32+
}
33+
34+
export async function POST(request: NextRequest, { params }: { params: Promise<{ graph: string, node: string }> }) {
35+
36+
const session = await getClient()
37+
38+
if (session instanceof NextResponse) {
39+
return session
40+
}
41+
42+
const { client } = session
43+
44+
const { graph: graphId, node } = await params
45+
const nodeId = Number(node)
46+
const { label } = await request.json()
47+
48+
try {
49+
if (!label) throw new Error("Label is required")
50+
51+
const query = `MATCH (n) WHERE ID(n) = $nodeId SET n:${label}`
52+
const graph = client.selectGraph(graphId);
53+
const result = await graph.query(query, { params: { nodeId } })
54+
55+
if (!result) throw new Error("Something went wrong")
56+
57+
return NextResponse.json({ message: "Label added successfully" }, { status: 200 });
58+
} catch (err: unknown) {
59+
console.error(err)
60+
return NextResponse.json({ message: (err as Error).message }, { status: 400 })
61+
}
62+
}

0 commit comments

Comments
 (0)