Skip to content

Commit 35f6b48

Browse files
committed
Neo4j: support plugins other than Apoc
Add withPlugins method to Neo4jContainer for setting up plugins included in the docker image. Previously only withApoc was provided for setting up Apoc-plugin. Now withApoc uses the same plugin list under-the-hood. Availability of specific plugins may depend on what version of Neo4j image is used.
1 parent 4471f40 commit 35f6b48

File tree

3 files changed

+72
-11
lines changed

3 files changed

+72
-11
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { Neo4jContainer, StartedNeo4jContainer } from "./neo4j-container";
1+
export { Neo4jContainer, Neo4jPlugin, StartedNeo4jContainer } from "./neo4j-container";

packages/modules/neo4j/src/neo4j-container.test.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import neo4j from "neo4j-driver";
2-
import { Neo4jContainer } from "./neo4j-container";
2+
import { Neo4jContainer, Neo4jPlugin } from "./neo4j-container";
33

44
describe("Neo4jContainer", { timeout: 180_000 }, () => {
55
// createNode {
@@ -83,4 +83,34 @@ describe("Neo4jContainer", { timeout: 180_000 }, () => {
8383
await container.stop();
8484
});
8585
// }
86+
87+
// plugins list {
88+
it("should work with plugin list", async () => {
89+
const container = await new Neo4jContainer("neo4j:5.26.5")
90+
.withPlugins([Neo4jPlugin.APOC_EXTENDED, Neo4jPlugin.GRAPH_DATA_SCIENCE])
91+
.withStartupTimeout(120_000)
92+
.start();
93+
const driver = neo4j.driver(
94+
container.getBoltUri(),
95+
neo4j.auth.basic(container.getUsername(), container.getPassword())
96+
);
97+
98+
const session = driver.session();
99+
100+
// Monitor methods are only available in extended Apoc.
101+
const result = await session.run("CALL apoc.monitor.ids()");
102+
expect(result.records[0].get("nodeIds")).toEqual({ high: 0, low: 0 });
103+
104+
// Insert one node.
105+
await session.run("CREATE (a:Person {name: $name}) RETURN a", { name: "Some dude" });
106+
107+
// Monitor result should reflect increase in data.
108+
const result2 = await session.run("CALL apoc.monitor.ids()");
109+
expect(result2.records[0].get("nodeIds")).toEqual({ high: 0, low: 1 });
110+
111+
await session.close();
112+
await driver.close();
113+
await container.stop();
114+
});
115+
// }
86116
});

packages/modules/neo4j/src/neo4j-container.ts

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,21 @@ const BOLT_PORT = 7687;
44
const HTTP_PORT = 7474;
55
const USERNAME = "neo4j";
66

7+
export enum Neo4jPlugin {
8+
APOC = "apoc",
9+
APOC_EXTENDED = "apoc-extended",
10+
BLOOM = "bloom",
11+
GEN_AI = "genai",
12+
GRAPHQL = "graphql",
13+
GRAPH_ALGORITHMS = "graph-algorithms",
14+
GRAPH_DATA_SCIENCE = "graph-data-science",
15+
NEO_SEMANTICS = "n10s",
16+
}
17+
718
export class Neo4jContainer extends GenericContainer {
819
private password = "pass123!@#WORD";
9-
private apoc = false;
1020
private ttl?: number;
21+
private plugins: Neo4jPlugin[] = [];
1122

1223
constructor(image = "neo4j:4.4.12") {
1324
super(image);
@@ -23,11 +34,12 @@ export class Neo4jContainer extends GenericContainer {
2334
}
2435

2536
public withApoc(): this {
26-
this.apoc = true;
27-
this.withEnvironment({
28-
NEO4JLABS_PLUGINS: '["apoc"]',
29-
NEO4J_dbms_security_procedures_unrestricted: "apoc.*",
30-
});
37+
this.plugins.push(Neo4jPlugin.APOC);
38+
return this;
39+
}
40+
41+
public withPlugins(plugins: Neo4jPlugin[]): this {
42+
this.plugins = plugins;
3143
return this;
3244
}
3345

@@ -44,10 +56,29 @@ export class Neo4jContainer extends GenericContainer {
4456
NEO4J_apoc_ttl_schedule: this.ttl.toString(),
4557
});
4658
}
47-
if (this.apoc) {
59+
60+
if (this.plugins.length > 0) {
61+
const whitelists: string[] = [];
62+
63+
this.plugins.forEach((plugin) => {
64+
if (plugin === Neo4jPlugin.APOC || plugin === Neo4jPlugin.APOC_EXTENDED) {
65+
whitelists.push("apoc.*");
66+
}
67+
68+
if (plugin === Neo4jPlugin.GRAPH_DATA_SCIENCE) {
69+
whitelists.push("gds.*");
70+
}
71+
72+
if (plugin === Neo4jPlugin.BLOOM) {
73+
whitelists.push("bloom.*");
74+
}
75+
});
76+
4877
this.withEnvironment({
49-
NEO4JLABS_PLUGINS: '["apoc"]',
50-
NEO4J_dbms_security_procedures_unrestricted: "apoc.*",
78+
NEO4JLABS_PLUGINS: JSON.stringify(this.plugins), // Older variant for older images.
79+
NEO4J_PLUGINS: JSON.stringify(this.plugins),
80+
NEO4J_dbms_security_procedures_unrestricted: whitelists.join(","),
81+
NEO4J_dbms_security_procedures_whitelist: whitelists.join(","),
5182
});
5283
}
5384
return new StartedNeo4jContainer(await super.start(), this.password);

0 commit comments

Comments
 (0)