-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Added util functions to manage temp databases (create and delete) and integrated them as methods on the `Neogma` class. - Copied over the functions from the [@neo4j-labs/temp-dbs](https://github.com/neo4j-contrib/neo4j-temp-db) repo and ported them to typescript. - Integrated the functions into the `Neogma` class and removed the `driver.close()` calls in the functions in favor of the global driver manager. - Added tests to make sure the intended behaviour was successfully implemented. --------- Co-authored-by: Jason Athanasoglou <jathanasoglou@outlook.com>
- Loading branch information
1 parent
4537e8b
commit c05c4b5
Showing
5 changed files
with
236 additions
and
0 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
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,40 @@ | ||
# Temp DBs | ||
|
||
You can work with temporary dbs (which can be cleared all at once) by using the static `fromTempDatabase` to initialize your Neogma instance. You pass in all the same props as in the constructor except the name of the database, as it will be created for you and managed internally by Neogma. | ||
|
||
```js | ||
/* --> create a neogma instance that is latched onto an internally managed temp database */ | ||
const neogma = Neogma.fromTempDatabase( | ||
{ | ||
/* --> use your connection details */ | ||
url: 'bolt://localhost', | ||
username: 'neo4j', | ||
password: 'password', | ||
}, | ||
{ | ||
logger: console.log, | ||
} | ||
); | ||
``` | ||
|
||
To dispose of a temp databse when you're done using it, you can use one of the following three methods: | ||
|
||
```js | ||
const database = neogma.database; | ||
await neogma.clearTempDatabase(database); | ||
``` | ||
|
||
As shown above this method requires you to pass the exact name of the database to dispose of. Sometimes that may not be what's needed if working with a large number of temp dbs so you could get them all at once. | ||
|
||
```js | ||
await neogma.clearAllTempDatabases(); | ||
``` | ||
|
||
You could also specify the time frame (of creation) in seconds to delete the dbs that are older than it. | ||
|
||
```js | ||
const seconds = 1000; | ||
await neogma.clearTempDatabasesOlderThan(seconds); | ||
``` | ||
|
||
> :ToCPrevNext |
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,33 @@ | ||
import { Neogma } from './Neogma'; | ||
import * as dotenv from 'dotenv'; | ||
import { TEMPORARY_DB_PREFIX } from './utils/temp'; | ||
|
||
describe('Neogma', () => { | ||
let neogma: Neogma; | ||
|
||
beforeAll(async () => { | ||
dotenv.config(); | ||
neogma = await Neogma.fromTempDatabase({ | ||
url: process.env.NEO4J_URL ?? '', | ||
username: process.env.NEO4J_USERNAME ?? '', | ||
password: process.env.NEO4J_PASSWORD ?? '', | ||
}); | ||
|
||
await neogma.verifyConnectivity(); | ||
}); | ||
|
||
afterAll(async () => { | ||
await neogma.clearAllTempDatabases(); | ||
await neogma.driver.close(); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(neogma).toBeDefined(); | ||
expect(neogma.database).toBeDefined(); | ||
expect(neogma.database).not.toBeNull(); | ||
}); | ||
|
||
it('should have created a temp db', async () => { | ||
expect(neogma.database?.indexOf(TEMPORARY_DB_PREFIX)).toBe(0); | ||
}); | ||
}); |
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,124 @@ | ||
import * as uuid from 'uuid'; | ||
import neo4j, { Driver, QueryResult, Session } from 'neo4j-driver'; | ||
|
||
export const TEMPORARY_DB_PREFIX = 'tempneogmadb'; | ||
|
||
const getCurrentTimestamp = () => { | ||
return Math.floor(new Date().getTime() / 1000); | ||
}; | ||
|
||
const filterConsoleDatabasesFromResult = (result: QueryResult<any>) => { | ||
return result.records.filter( | ||
(record) => record.get('name').indexOf(TEMPORARY_DB_PREFIX) === 0, | ||
); | ||
}; | ||
|
||
const deleteDatabaseUserAndRole = async ( | ||
session: Session, | ||
database: string, | ||
) => { | ||
try { | ||
await session.run(`STOP DATABASE ${database};`); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
try { | ||
await session.run(`DROP DATABASE ${database};`); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
try { | ||
await session.run(`DROP USER ${database};`); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
try { | ||
await session.run(`DROP ROLE ${database};`); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
}; | ||
|
||
export const createTempDatabase = async (driver: Driver) => { | ||
const sessionId = uuid.v4().replace(/-/g, ''); | ||
const currentTimestamp = getCurrentTimestamp(); | ||
const database = `${TEMPORARY_DB_PREFIX}${sessionId}${currentTimestamp}`; | ||
|
||
const session = driver.session({ | ||
database: 'system', | ||
defaultAccessMode: neo4j.session.WRITE, | ||
}); | ||
|
||
try { | ||
await session.run(`CREATE DATABASE ${database} WAIT;`); | ||
await session.run( | ||
`CREATE USER ${database} SET PASSWORD '${database}' SET PASSWORD CHANGE NOT REQUIRED;`, | ||
); | ||
await session.run(`CREATE ROLE ${database};`); | ||
await session.run(`GRANT ROLE ${database} TO ${database};`); | ||
await session.run(`GRANT ALL ON DATABASE ${database} TO ${database};`); | ||
await session.run(`GRANT ACCESS ON DATABASE ${database} TO ${database};`); | ||
await session.run(`GRANT READ {*} ON GRAPH ${database} TO ${database};`); | ||
await session.run(`GRANT TRAVERSE ON GRAPH ${database} TO ${database};`); | ||
await session.run(`GRANT WRITE ON GRAPH ${database} TO ${database};`); | ||
} catch (error) { | ||
console.error(error); | ||
} finally { | ||
await session.close(); | ||
} | ||
|
||
return database; | ||
}; | ||
|
||
export const clearTempDatabase = async (driver: Driver, database: string) => { | ||
const session = driver.session({ database: 'system' }); | ||
try { | ||
await deleteDatabaseUserAndRole(session, database); | ||
} catch (error) { | ||
console.error(error); | ||
} finally { | ||
await session.close(); | ||
} | ||
}; | ||
|
||
export const clearTempDatabasesOlderThan = async ( | ||
driver: Driver, | ||
seconds: number, | ||
) => { | ||
const session = driver.session({ database: 'system' }); | ||
const result = await session.run('SHOW DATABASES'); | ||
const shouldExpireAt = getCurrentTimestamp() - seconds; | ||
try { | ||
const records = filterConsoleDatabasesFromResult(result); | ||
console.log('Databases found: ' + records.length); | ||
for (const record of records) { | ||
const database = record.get('name'); //tempneogmadb56e7794ad165454282ee0a7c32a5e3eb1705341040 | ||
const dbTimestamp = parseInt(database.slice(44), 10); | ||
const isExpired = dbTimestamp <= shouldExpireAt; | ||
if (isExpired) { | ||
await deleteDatabaseUserAndRole(session, database); | ||
} else { | ||
} | ||
} | ||
} catch (error) { | ||
console.error(error); | ||
} finally { | ||
await session.close(); | ||
} | ||
}; | ||
|
||
export const clearAllTempDatabases = async (driver: Driver) => { | ||
const session = driver.session({ database: 'system' }); | ||
const result = await session.run('SHOW DATABASES'); | ||
try { | ||
const records = filterConsoleDatabasesFromResult(result); | ||
for (const record of records) { | ||
const database = record.get('name'); | ||
await deleteDatabaseUserAndRole(session, database); | ||
} | ||
} catch (error) { | ||
console.error(error); | ||
} finally { | ||
await session.close(); | ||
} | ||
}; |