@jupiterone/integration-sdk-cli 7.3.1 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/src/commands/index.d.ts +1 -0
  2. package/dist/src/commands/index.js +1 -0
  3. package/dist/src/commands/index.js.map +1 -1
  4. package/dist/src/commands/neo4j.d.ts +2 -0
  5. package/dist/src/commands/neo4j.js +70 -0
  6. package/dist/src/commands/neo4j.js.map +1 -0
  7. package/dist/src/index.js +2 -1
  8. package/dist/src/index.js.map +1 -1
  9. package/dist/src/neo4j/index.d.ts +2 -0
  10. package/dist/src/neo4j/index.js +15 -0
  11. package/dist/src/neo4j/index.js.map +1 -0
  12. package/dist/src/neo4j/neo4jGraphStore.d.ts +23 -0
  13. package/dist/src/neo4j/neo4jGraphStore.js +134 -0
  14. package/dist/src/neo4j/neo4jGraphStore.js.map +1 -0
  15. package/dist/src/neo4j/neo4jUtilities.d.ts +4 -0
  16. package/dist/src/neo4j/neo4jUtilities.js +46 -0
  17. package/dist/src/neo4j/neo4jUtilities.js.map +1 -0
  18. package/dist/src/neo4j/uploadToNeo4j.d.ts +9 -0
  19. package/dist/src/neo4j/uploadToNeo4j.js +33 -0
  20. package/dist/src/neo4j/uploadToNeo4j.js.map +1 -0
  21. package/dist/src/neo4j/wipeNeo4j.d.ts +14 -0
  22. package/dist/src/neo4j/wipeNeo4j.js +41 -0
  23. package/dist/src/neo4j/wipeNeo4j.js.map +1 -0
  24. package/dist/tsconfig.dist.tsbuildinfo +4128 -1259
  25. package/package.json +12 -10
  26. package/src/commands/index.ts +1 -0
  27. package/src/commands/neo4j.ts +63 -0
  28. package/src/index.ts +3 -1
  29. package/src/neo4j/README.md +42 -0
  30. package/src/neo4j/__tests__/neo4jGraphStore.test.ts +99 -0
  31. package/src/neo4j/__tests__/neo4jUtilities.test.ts +25 -0
  32. package/src/neo4j/index.ts +2 -0
  33. package/src/neo4j/neo4jGraphStore.ts +139 -0
  34. package/src/neo4j/neo4jUtilities.ts +43 -0
  35. package/src/neo4j/uploadToNeo4j.ts +47 -0
  36. package/src/neo4j/wipeNeo4j.ts +58 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupiterone/integration-sdk-cli",
3
- "version": "7.3.1",
3
+ "version": "8.1.0",
4
4
  "description": "The SDK for developing JupiterOne integrations",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -22,31 +22,33 @@
22
22
  "prepack": "yarn build:dist"
23
23
  },
24
24
  "dependencies": {
25
- "@jupiterone/integration-sdk-runtime": "^7.3.1",
25
+ "@jupiterone/integration-sdk-runtime": "^8.1.0",
26
26
  "commander": "^5.0.0",
27
27
  "globby": "^11.0.0",
28
28
  "js-yaml": "^4.1.0",
29
29
  "json-diff": "^0.5.4",
30
30
  "lodash": "^4.17.19",
31
31
  "markdown-table": "^2.0.0",
32
+ "neo4j-driver": "^4.3.3",
32
33
  "runtypes": "5.1.0",
33
34
  "upath": "^1.2.0",
34
35
  "vis": "^4.21.0-EOL"
35
36
  },
36
37
  "devDependencies": {
37
- "@jupiterone/integration-sdk-private-test-utils": "^7.3.1",
38
- "@pollyjs/adapter-node-http": "^4.0.4",
39
- "@pollyjs/core": "^4.0.4",
40
- "@pollyjs/persister-fs": "^4.0.4",
38
+ "@jupiterone/integration-sdk-private-test-utils": "^8.1.0",
39
+ "@pollyjs/adapter-node-http": "^5.1.1",
40
+ "@pollyjs/core": "^5.1.1",
41
+ "@pollyjs/persister-fs": "^5.1.1",
41
42
  "@types/js-yaml": "^4.0.3",
42
43
  "@types/json-diff": "^0.5.1",
43
44
  "@types/lodash": "^4.14.158",
44
- "@types/pollyjs__adapter-node-http": "^2.0.0",
45
- "@types/pollyjs__core": "^4.0.0",
46
- "@types/pollyjs__persister": "^2.0.1",
45
+ "@types/pollyjs__adapter-node-http": "^2.0.1",
46
+ "@types/pollyjs__core": "^4.3.3",
47
+ "@types/pollyjs__persister": "^4.3.1",
47
48
  "@types/vis": "^4.21.20",
48
49
  "memfs": "^3.2.0",
50
+ "neo-forgery": "^2.0.0",
49
51
  "uuid": "^8.2.0"
50
52
  },
51
- "gitHead": "879afd690d8a369030a29b464a86d47daf03f55b"
53
+ "gitHead": "0ac133e50ed20a51949810d27d6ed873c977fc46"
52
54
  }
@@ -6,3 +6,4 @@ export * from './run';
6
6
  export * from './document';
7
7
  export * from './visualize-types';
8
8
  export * from './validate-question-file';
9
+ export * from './neo4j';
@@ -0,0 +1,63 @@
1
+ import * as commander from 'commander';
2
+ import path from 'path';
3
+
4
+ import dotenv from 'dotenv';
5
+ import dotenvExpand from 'dotenv-expand';
6
+
7
+ import * as log from '../log';
8
+ import { uploadToNeo4j, wipeNeo4jByID, wipeAllNeo4j } from '../neo4j';
9
+
10
+ export function neo4j() {
11
+ dotenvExpand(dotenv.config());
12
+
13
+ const program = new commander.Command();
14
+ program.description(`Suite of neo4j commands. Options are currently 'neo4j push', 'neo4j wipe', and 'neo4j wipe-all'`);
15
+ const neo4jCommand = program.command('neo4j');
16
+ neo4jCommand
17
+ .command('push')
18
+ .description('upload collected entities and relationships to local Neo4j')
19
+ .option(
20
+ '-d, --data-dir <directory>',
21
+ 'path to collected entities and relationships',
22
+ path.resolve(process.cwd(), '.j1-integration'),
23
+ )
24
+ .option(
25
+ '-i, --integration-instance-id <id>',
26
+ '_integrationInstanceId assigned to uploaded entities',
27
+ 'defaultLocalInstanceID'
28
+ )
29
+ .action(async (options) => {
30
+ log.info(`Beginning data upload to local neo4j`);
31
+ // Point `fileSystem.ts` functions to expected location relative to
32
+ // integration project path.
33
+ const finalDir = path.resolve(process.cwd(), options.dataDir);
34
+ process.env.JUPITERONE_INTEGRATION_STORAGE_DIRECTORY = finalDir;
35
+
36
+ await uploadToNeo4j({
37
+ pathToData: finalDir,
38
+ integrationInstanceID: options.integrationInstanceId
39
+ });
40
+ log.info(`Data uploaded to local neo4j`);
41
+ });
42
+
43
+ neo4jCommand
44
+ .command('wipe')
45
+ .description('wipe entities and relationships for a given integrationInstanceID in the Neo4j database')
46
+ .option(
47
+ '-i, --integration-instance-id <id>',
48
+ '_integrationInstanceId assigned to uploaded entities',
49
+ 'defaultLocalInstanceID'
50
+ )
51
+ .action(async (options) => {
52
+ await wipeNeo4jByID({integrationInstanceID: options.integrationInstanceId});
53
+ });
54
+
55
+ neo4jCommand
56
+ .command('wipe-all')
57
+ .description('wipe all entities and relationships in the Neo4j database')
58
+ .action(async (options) => {
59
+ await wipeAllNeo4j({});
60
+ });
61
+
62
+ return neo4jCommand;
63
+ }
package/src/index.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  visualize,
10
10
  visualizeTypes,
11
11
  validateQuestionFile,
12
+ neo4j,
12
13
  } from './commands';
13
14
 
14
15
  export function createCli() {
@@ -20,5 +21,6 @@ export function createCli() {
20
21
  .addCommand(run())
21
22
  .addCommand(visualizeTypes())
22
23
  .addCommand(document())
23
- .addCommand(validateQuestionFile());
24
+ .addCommand(validateQuestionFile())
25
+ .addCommand(neo4j());
24
26
  }
@@ -0,0 +1,42 @@
1
+ # Neo4j JupiterOne CLI Command
2
+
3
+ ## Installation
4
+
5
+ This command assumes you have three additional values stored in your
6
+ local .env file:
7
+ NEO4J_URI
8
+ NEO4J_USER
9
+ NEO4J_PASSWORD
10
+
11
+ This can be used for uploading to local or remote Neo4j databases. If
12
+ SSL is needed for a remote connection, specify `bolt+s` or `bolt+ssc`
13
+ in the URI. For easy access to a local Neo4j instance, you can launch
14
+ one via a Neo4j provided Docker image with the command:
15
+
16
+ ```
17
+ docker run \
18
+ -p 7474:7474 -p 7687:7687 \
19
+ -d \
20
+ -v $PWD/.neo4j/data:/data \
21
+ -v $PWD/.neo4j/logs:/logs \
22
+ -v $PWD/.neo4j/import:/var/lib/neo4j/import \
23
+ -v $PWD/.neo4j/plugins:/plugins \
24
+ --env NEO4J_AUTH=neo4j/devpass \
25
+ neo4j:latest
26
+ ```
27
+
28
+ If you would like to use a different username and password, the NEO4J_AUTH
29
+ value can be modified to whatever username/password you prefer.
30
+
31
+ NOTE: Future updates are planned to streamline this without removing
32
+ the option of pushing to an external Neo4j database.
33
+
34
+ ## Usage
35
+
36
+ Data is still collected in the same way as before with a call to `yarn start`.
37
+
38
+ Once data has been collected, you can run `j1-integration neo4j push`. This will
39
+ push data to the Neo4j server listed in the NEO4J_URI .env parameter. If
40
+ running locally, you can then access data in the Neo4j database by visiting
41
+ http://localhost:7474. Alternatively, you can download the full Neo4j
42
+ Desktop application at https://neo4j.com/download/.
@@ -0,0 +1,99 @@
1
+ import {
2
+ mockDriver,
3
+ mockSessionFromQuerySet,
4
+ QuerySpec
5
+ } from 'neo-forgery';
6
+ import * as neo4j from 'neo4j-driver';
7
+ import { Neo4jGraphStore } from '../neo4jGraphStore';
8
+ import { Entity, Relationship } from '@jupiterone/integration-sdk-core';
9
+
10
+ const testInstanceID = 'testInstanceID';
11
+ const testEntityData: Entity[] = [{
12
+ _type: "testType",
13
+ _class: "testClass",
14
+ _key: "testKey",
15
+ }];
16
+ const testRelationshipData: Relationship[] = [{
17
+ _fromEntityKey: "testKey1",
18
+ _toEntityKey: "testKey2",
19
+ _type: "testRelType",
20
+ _key: "relKey",
21
+ _class: "testRelationshipClass",
22
+ }];
23
+ const constraintCall = 'CREATE CONSTRAINT unique_testType IF NOT EXISTS ON (n:testType) ASSERT n._key IS UNIQUE;'
24
+ const addEntityCall = `MERGE (n:testType {_key: 'testKey', _integrationInstanceID: '${testInstanceID}'}) SET n._type = 'testType', n._class = 'testClass';`;
25
+ const addRelationshipCall = `
26
+ MATCH (start {_key: 'testKey1', _integrationInstanceID: '${testInstanceID}'})
27
+ MATCH (end {_key: 'testKey2', _integrationInstanceID: '${testInstanceID}'})
28
+ MERGE (start)-[relationship:testRelType]->(end);`
29
+ const wipeByIDCall = `MATCH (n {_integrationInstanceID: '${testInstanceID}'}) DETACH DELETE n`;
30
+ const wipeAllCall = 'MATCH (n) DETACH DELETE n';
31
+ const querySet: QuerySpec[] = [{
32
+ name: 'addConstraint',
33
+ query: constraintCall,
34
+ params: undefined,
35
+ output: {records:[]}
36
+ },
37
+ {
38
+ name: 'addEntity',
39
+ query: addEntityCall,
40
+ params: undefined,
41
+ output: {records:[]}
42
+ },
43
+ {
44
+ name: 'addRelationship',
45
+ query: addRelationshipCall,
46
+ params: undefined,
47
+ output: {records:[]}
48
+ },
49
+ {
50
+ name: 'wipeByID',
51
+ query: wipeByIDCall,
52
+ params: undefined,
53
+ output: {records:[]}
54
+ },
55
+ {
56
+ name: 'wipeAll',
57
+ query: wipeAllCall,
58
+ params: undefined,
59
+ output: {records:[]}
60
+ }];
61
+
62
+ describe('#neo4jGraphStore', () => {
63
+ const mockDriverResp = mockDriver();
64
+ const mockSession = mockSessionFromQuerySet(querySet);
65
+ const store = new Neo4jGraphStore({
66
+ uri: '',
67
+ username: '',
68
+ password: '',
69
+ integrationInstanceID: testInstanceID,
70
+ session: mockSession,
71
+ });
72
+
73
+ test('should generate call to create a driver connection', () => {
74
+ const spy = jest.spyOn(neo4j, 'driver').mockReturnValue(mockDriverResp);
75
+
76
+ const emptyStore = new Neo4jGraphStore({
77
+ uri: '',
78
+ username: '',
79
+ password: '',
80
+ integrationInstanceID: testInstanceID,
81
+ });
82
+ expect(async () => await emptyStore.close()).toReturn;
83
+ expect(spy).toHaveBeenCalledTimes(1);
84
+ });
85
+
86
+ test('should generate call to create an Entity', () => {
87
+ expect(async () => await store.addEntities(testEntityData)).toReturn;
88
+ });
89
+
90
+ test('should generate call to create a Relationship', () => {
91
+
92
+ expect(async () => await store.addRelationships(testRelationshipData)).toReturn;
93
+ });
94
+
95
+ test('should generate call to wipe by ID', () => {
96
+ expect(async () => await store.wipeInstanceIdData()).toReturn;
97
+ });
98
+
99
+ });
@@ -0,0 +1,25 @@
1
+ import { startsWithNumeric, sanitizePropertyName, sanitizeValue, buildPropertyParameters } from '../neo4jUtilities';
2
+
3
+
4
+ describe('#neo4jUtilities', () => {
5
+ test('should return true for string starting with a numeric', () => {
6
+ const testNameResults: boolean = startsWithNumeric('1testname');
7
+ expect(testNameResults).toEqual(true);
8
+ });
9
+ test('should return false for string not starting with a numeric', () => {
10
+ const testTrailingNumeric: boolean = startsWithNumeric('another1testname');
11
+ expect(testTrailingNumeric).toEqual(false);
12
+ });
13
+ test('should sanitize property name properly', () => {
14
+ const testSanitize: string = sanitizePropertyName(`1a!b@c#d$e%f^g&h*i(j)k-l=m+n\\o|p'q"r;s:t/u?v.w,x>y<z\`1~2\t3\n4[5]6{7}8 90`);
15
+ expect(testSanitize).toEqual('n1a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_1_2_3_4_5_6_7_8_90');
16
+ });
17
+ test('should sanitize value properly', () => {
18
+ const testSanitize: string = sanitizeValue('1a!b@c#d$e%f^g&h*i(j)k-l=m+n\\o|p\'q"r;s:t/u?v.w,x>y<z`1~2\t3\n4[5]6{7}8 90');
19
+ expect(testSanitize).toEqual('1a!b@c#d$e%f^g&h*i(j)k-l=m+n\\o|p\'q\\"r;s:t/u?v.w,x>y<z`1~2\t3\n4[5]6{7}8 90');
20
+ });
21
+ test('should build property string correctly including sanitization', () => {
22
+ const testPropResults: Object = buildPropertyParameters({test: '123', '1sanitize1hi&$abc d': '1h"i&$abc d'});
23
+ expect(testPropResults).toEqual({test:'123', n1sanitize1hi__abc_d:'1h\\"i&$abc d'});
24
+ });
25
+ });
@@ -0,0 +1,2 @@
1
+ export * from './uploadToNeo4j';
2
+ export * from './wipeNeo4j';
@@ -0,0 +1,139 @@
1
+ import { Entity, Relationship } from '@jupiterone/integration-sdk-core';
2
+ import { sanitizeValue, buildPropertyParameters } from './neo4jUtilities';
3
+
4
+ import * as neo4j from 'neo4j-driver';
5
+
6
+ export interface Neo4jGraphObjectStoreParams {
7
+ uri: string;
8
+ username: string;
9
+ password: string;
10
+ integrationInstanceID: string,
11
+ session?: neo4j.Session
12
+ }
13
+
14
+ export class Neo4jGraphStore {
15
+ private neo4jDriver: neo4j.Driver;
16
+ private persistedSession: neo4j.Session;
17
+ private databaseName = 'neo4j';
18
+ private typeList = new Set<string>();
19
+ private integrationInstanceID: string;
20
+
21
+ constructor(params: Neo4jGraphObjectStoreParams) {
22
+ if(params.session) {
23
+ this.persistedSession = params.session;
24
+ }
25
+ else {
26
+ this.neo4jDriver = neo4j.driver(
27
+ params.uri,
28
+ neo4j.auth.basic(params.username, params.password),
29
+ );
30
+ }
31
+ this.integrationInstanceID = params.integrationInstanceID;
32
+ }
33
+
34
+ private async runCypherCommand(cypherCommand: string, cypherParameters?: any): Promise<neo4j.Result> {
35
+ if(this.persistedSession) {
36
+ const result = await this.persistedSession.run(cypherCommand);
37
+ return result;
38
+ }
39
+ else {
40
+ const session = this.neo4jDriver.session({
41
+ database: this.databaseName,
42
+ defaultAccessMode: neo4j.session.WRITE,
43
+ });
44
+ const result = await session.run(cypherCommand, cypherParameters);
45
+ await session.close();
46
+ return result;
47
+ }
48
+ }
49
+
50
+ async addEntities(newEntities: Entity[]) {
51
+ const nodeAlias: string = 'entityNode';
52
+ for (const entity of newEntities) {
53
+ //Add index if not already in types. This will optimize future
54
+ //MATCH/MERGE calls.
55
+ if (!this.typeList.has(entity._type)) {
56
+ await this.runCypherCommand(
57
+ `CREATE INDEX index_${entity._type} IF NOT EXISTS FOR (n:${entity._type}) ON (n._key, n._integrationInstanceID);`,
58
+ );
59
+ this.typeList.add(entity._type);
60
+ }
61
+ const propertyParameters = buildPropertyParameters(entity);
62
+ const finalKeyValue = sanitizeValue(entity._key.toString());
63
+ const buildCommand = `
64
+ MERGE (${nodeAlias} {_key: $finalKeyValue, _integrationInstanceID: $integrationInstanceID})
65
+ SET ${nodeAlias} += $propertyParameters
66
+ SET ${nodeAlias}:${entity._type};`;
67
+ await this.runCypherCommand(buildCommand, {
68
+ propertyParameters: propertyParameters,
69
+ finalKeyValue: finalKeyValue,
70
+ integrationInstanceID: this.integrationInstanceID
71
+ });
72
+ }
73
+ }
74
+
75
+ async addRelationships(newRelationships: Relationship[]) {
76
+ for (const relationship of newRelationships) {
77
+ const relationshipAlias: string = 'relationship';
78
+ const propertyParameters = buildPropertyParameters(relationship);
79
+
80
+ let startEntityKey = '';
81
+ let endEntityKey = '';
82
+ //Get start and end _keys. Will be overwritten if we're
83
+ //working with a mapped relationship.
84
+ if (relationship._fromEntityKey) {
85
+ startEntityKey = sanitizeValue(relationship._fromEntityKey.toString());
86
+ }
87
+ if(relationship._toEntityKey) {
88
+ endEntityKey = sanitizeValue(relationship._toEntityKey.toString());
89
+ }
90
+
91
+ if(relationship._mapping) { //Mapped Relationship
92
+ if(relationship._mapping['skipTargetCreation'] === false) {
93
+ //Create target entity first
94
+ const tempEntity: Entity = {
95
+ _class: relationship._mapping['targetEntity']._class,
96
+ //TODO, I think this key is wrong, but not sure what else to use
97
+ _key: sanitizeValue(relationship._key.replace(relationship._mapping['sourceEntityKey'], '')),
98
+ _type: relationship._mapping['targetEntity']._type,
99
+ }
100
+ await this.addEntities([tempEntity]);
101
+ }
102
+ startEntityKey = sanitizeValue(relationship._mapping['sourceEntityKey']);
103
+ // TODO, see above. This key might also be an issue for the same reason
104
+ endEntityKey = sanitizeValue(relationship._key.replace(relationship._mapping['sourceEntityKey'], ''));
105
+ }
106
+
107
+ const buildCommand = `
108
+ MERGE (start {_key: $startEntityKey, _integrationInstanceID: $integrationInstanceID})
109
+ MERGE (end {_key: $endEntityKey, _integrationInstanceID: $integrationInstanceID})
110
+ MERGE (start)-[${relationshipAlias}:${relationship._type}]->(end)
111
+ SET ${relationshipAlias} += $propertyParameters;`;
112
+ await this.runCypherCommand(buildCommand, {
113
+ propertyParameters: propertyParameters,
114
+ startEntityKey: startEntityKey,
115
+ endEntityKey: endEntityKey,
116
+ integrationInstanceID: this.integrationInstanceID
117
+ });
118
+ }
119
+ }
120
+
121
+ // TODO, if we get to very large databases we could reach a size where
122
+ // one or both both of the below wipe commands can't be easily executed
123
+ // in memory. At that time, we should consider requiring/using the APOC
124
+ // library so we can use apoc.periodic.iterate. Leaving out for now,
125
+ // since that would further complicate the Neo4j database setup.
126
+ async wipeInstanceIdData() {
127
+ const wipeCypherCommand = `MATCH (n {_integrationInstanceID: '${this.integrationInstanceID}'}) DETACH DELETE n`;
128
+ await this.runCypherCommand(wipeCypherCommand);
129
+ }
130
+
131
+ async wipeDatabase() {
132
+ const wipeCypherCommand = `MATCH (n) DETACH DELETE n`;
133
+ await this.runCypherCommand(wipeCypherCommand);
134
+ }
135
+
136
+ async close() {
137
+ await this.neo4jDriver.close();
138
+ }
139
+ }
@@ -0,0 +1,43 @@
1
+
2
+ export function startsWithNumeric(str: string): boolean{
3
+ return /^\d/.test(str);
4
+ }
5
+
6
+ export function sanitizePropertyName(propertyName: string): string {
7
+ let sanitizedName = '';
8
+ if(startsWithNumeric(propertyName)) {
9
+ sanitizedName += 'n';
10
+ }
11
+ sanitizedName += propertyName;
12
+ sanitizedName = sanitizedName.replace(/[\s!@#$%^&*()\-=+\\|'";:/?.,><`~\t\n[\]{}]/g, "_");
13
+ return sanitizedName;
14
+ }
15
+
16
+ export function sanitizeValue(value: string): string {
17
+ return value.replace(/"/gi, '\\"')
18
+ }
19
+
20
+ export function buildPropertyParameters(propList: Object) {
21
+ const propertyParameters = {};
22
+ for (const key in propList) {
23
+ if (key === '_rawData') {
24
+ //stringify JSON in rawData so we can store it.
25
+ propertyParameters[key] = `"${JSON.stringify(propList[key])}"`;
26
+ } else {
27
+ // Sanitize out characters that aren't allowed in property names
28
+ const propertyName = sanitizePropertyName(key);
29
+
30
+ //If we're dealing with a number or boolean, leave alone, otherwise
31
+ //wrap in single quotes to convert to a string and escape all
32
+ //other single quotes so they don't terminate strings prematurely.
33
+ if(typeof propList[key] == 'number' || typeof propList[key] == 'boolean') {
34
+ propertyParameters[propertyName] = propList[key];
35
+ }
36
+ else {
37
+ propertyParameters[propertyName] = sanitizeValue(propList[key].toString());
38
+ }
39
+ }
40
+ }
41
+
42
+ return propertyParameters;
43
+ }
@@ -0,0 +1,47 @@
1
+ import { Neo4jGraphStore } from './neo4jGraphStore';
2
+ import { iterateParsedGraphFiles, isDirectoryPresent } from '@jupiterone/integration-sdk-runtime';
3
+ import { FlushedGraphObjectData } from '@jupiterone/integration-sdk-runtime/src/storage/types';
4
+
5
+ type UploadToNeo4jParams = {
6
+ pathToData: string;
7
+ integrationInstanceID: string;
8
+ neo4jUri?: string;
9
+ neo4jUser?: string;
10
+ neo4jPassword?: string;
11
+ };
12
+
13
+ export async function uploadToNeo4j({
14
+ pathToData,
15
+ integrationInstanceID,
16
+ neo4jUri = process.env.NEO4J_URI,
17
+ neo4jUser = process.env.NEO4J_USER,
18
+ neo4jPassword = process.env.NEO4J_PASSWORD,
19
+ }: UploadToNeo4jParams) {
20
+ if (!neo4jUri || !neo4jUser || !neo4jPassword) {
21
+ throw new Error(
22
+ 'ERROR: must provide login information in function call or include NEO4J_URI, NEO4J_USER, and NEO4J_PASSWORD files in your .env file!',
23
+ );
24
+ }
25
+ if (!isDirectoryPresent(pathToData)) {
26
+ throw new Error('ERROR: graph directory does not exist!');
27
+ }
28
+
29
+ const store = new Neo4jGraphStore({
30
+ uri: neo4jUri,
31
+ username: neo4jUser,
32
+ password: neo4jPassword,
33
+ integrationInstanceID: integrationInstanceID,
34
+ });
35
+
36
+ async function handleGraphObjectFile(parsedData: FlushedGraphObjectData) {
37
+ if (parsedData.entities) await store.addEntities(parsedData.entities);
38
+ if (parsedData.relationships)
39
+ await store.addRelationships(parsedData.relationships);
40
+ }
41
+
42
+ try {
43
+ await iterateParsedGraphFiles(handleGraphObjectFile, pathToData);
44
+ } finally {
45
+ await store.close();
46
+ }
47
+ }
@@ -0,0 +1,58 @@
1
+ import { Neo4jGraphStore } from './neo4jGraphStore';
2
+
3
+ type WipeNeo4jParams = {
4
+ integrationInstanceID: string;
5
+ neo4jUri?: string;
6
+ neo4jUser?: string;
7
+ neo4jPassword?: string;
8
+ }
9
+ export async function wipeNeo4jByID({
10
+ integrationInstanceID,
11
+ neo4jUri = process.env.NEO4J_URI,
12
+ neo4jUser = process.env. NEO4J_USER,
13
+ neo4jPassword = process.env. NEO4J_PASSWORD,
14
+ }: WipeNeo4jParams) {
15
+ if(!neo4jUri || !neo4jUser || !neo4jPassword) {
16
+ throw new Error('ERROR: must provide login information in function call or include NEO4J_URI, NEO4J_USER, and NEO4J_PASSWORD files in your .env file!');
17
+ }
18
+
19
+ const store = new Neo4jGraphStore({
20
+ uri: neo4jUri,
21
+ username: neo4jUser,
22
+ password: neo4jPassword,
23
+ integrationInstanceID: integrationInstanceID,
24
+ });
25
+ try {
26
+ await store.wipeInstanceIdData();
27
+ } finally {
28
+ await store.close();
29
+ }
30
+ }
31
+
32
+ type WipeAllNeo4jParams = {
33
+ neo4jUri?: string;
34
+ neo4jUser?: string;
35
+ neo4jPassword?: string;
36
+ }
37
+
38
+ export async function wipeAllNeo4j({
39
+ neo4jUri = process.env.NEO4J_URI,
40
+ neo4jUser = process.env. NEO4J_USER,
41
+ neo4jPassword = process.env. NEO4J_PASSWORD,
42
+ }: WipeAllNeo4jParams) {
43
+ if(!neo4jUri || !neo4jUser || !neo4jPassword) {
44
+ throw new Error('ERROR: must provide login information in function call or include NEO4J_URI, NEO4J_USER, and NEO4J_PASSWORD files in your .env file!');
45
+ }
46
+
47
+ const store = new Neo4jGraphStore({
48
+ uri: neo4jUri,
49
+ username: neo4jUser,
50
+ password: neo4jPassword,
51
+ integrationInstanceID: '',
52
+ });
53
+ try {
54
+ await store.wipeDatabase();
55
+ } finally {
56
+ await store.close();
57
+ }
58
+ }