Skip to content

Commit 25d5b56

Browse files
authored
Support Neo4j plugins other than Apoc (#994)
1 parent 6059491 commit 25d5b56

File tree

4 files changed

+72
-11
lines changed

4 files changed

+72
-11
lines changed

docs/modules/neo4j.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ npm install @testcontainers/neo4j --save-dev
2121
<!--codeinclude-->
2222
[Configure APOC:](../../packages/modules/neo4j/src/neo4j-container.test.ts) inside_block:apoc
2323
<!--/codeinclude-->
24+
25+
<!--codeinclude-->
26+
[Configure other supported plugins:](../../packages/modules/neo4j/src/neo4j-container.test.ts) inside_block:pluginsList
27+
<!--/codeinclude-->

packages/modules/neo4j/src/index.ts

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+
// pluginsList {
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: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,28 @@ 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+
18+
const pluginWhitelists: Partial<Record<Neo4jPlugin, string>> = {
19+
[Neo4jPlugin.APOC]: "apoc.*",
20+
[Neo4jPlugin.APOC_EXTENDED]: "apoc.*",
21+
[Neo4jPlugin.BLOOM]: "bloom.*",
22+
[Neo4jPlugin.GRAPH_DATA_SCIENCE]: "gds.*",
23+
};
24+
725
export class Neo4jContainer extends GenericContainer {
826
private password = "pass123!@#WORD";
9-
private apoc = false;
1027
private ttl?: number;
28+
private plugins: Neo4jPlugin[] = [];
1129

1230
constructor(image = "neo4j:4.4.12") {
1331
super(image);
@@ -23,11 +41,12 @@ export class Neo4jContainer extends GenericContainer {
2341
}
2442

2543
public withApoc(): this {
26-
this.apoc = true;
27-
this.withEnvironment({
28-
NEO4JLABS_PLUGINS: '["apoc"]',
29-
NEO4J_dbms_security_procedures_unrestricted: "apoc.*",
30-
});
44+
this.plugins.push(Neo4jPlugin.APOC);
45+
return this;
46+
}
47+
48+
public withPlugins(plugins: Neo4jPlugin[]): this {
49+
this.plugins = plugins;
3150
return this;
3251
}
3352

@@ -44,10 +63,18 @@ export class Neo4jContainer extends GenericContainer {
4463
NEO4J_apoc_ttl_schedule: this.ttl.toString(),
4564
});
4665
}
47-
if (this.apoc) {
66+
67+
if (this.plugins.length > 0) {
68+
const whitelists: string = this.plugins
69+
.map((plugin) => (plugin in pluginWhitelists ? pluginWhitelists[plugin] : null))
70+
.filter((whitelisted) => whitelisted !== null)
71+
.join(",");
72+
4873
this.withEnvironment({
49-
NEO4JLABS_PLUGINS: '["apoc"]',
50-
NEO4J_dbms_security_procedures_unrestricted: "apoc.*",
74+
NEO4JLABS_PLUGINS: JSON.stringify(this.plugins), // Older variant for older images.
75+
NEO4J_PLUGINS: JSON.stringify(this.plugins),
76+
NEO4J_dbms_security_procedures_unrestricted: whitelists,
77+
NEO4J_dbms_security_procedures_whitelist: whitelists,
5178
});
5279
}
5380
return new StartedNeo4jContainer(await super.start(), this.password);

0 commit comments

Comments
 (0)