
Research
NPM targeted by malware campaign mimicking familiar library names
Socket uncovered npm malware campaign mimicking popular Node.js libraries and packages from other ecosystems; packages steal data and execute remote code.
active-graph-record
Advanced tools
this software is beta stage and is not intended to be used in serous production projects developers of this software are not responsible for data loss and corruption, lunar eclipses and dead kittens
ActiveRecord is common pattern in software development which declares that there is special class or classes who are responsible for database reflection, line-by-line or node-by-node.
Neo4j is Graph Database, it's schema-less and ACID-compliant.
Dead simple. It's mostly purposed for ES2015-featured JavaScript, so all of the examples are written using it.
const {Connection, Record} = require('active-graph-record')
class Entry extends Record {}
Entry.connection = new Connection('http://neo4j:password@localhost:7474');
// or with babel-preset-stage-1
// here and further where static properties are used they can be replaced
// by assignment of property to class function, like shown above
class Entry extends Record {
static connection = new Connection('http://neo4j:password@localhost:7474');
}
Entry.register() //creates indexes and makes some internal magic for resolving
async function main() {
const entry = new Entry()
entry.foo = 'bar'
await entry.save()
const entries = await Entry.where({foo: 'bar'})
console.log(entries.length) // => 1
console.log(entries[0].foo) // => 'bar'
}
no problems. It's dead simple too:
const {Connection, Record, Relation} = require('active-graph-record')
class ConnectedRecord extends Record {
static connection = new Connection('http://neo4j:password@localhost:7474');
}
class RecordObject extends ConnectedRecord {
subjects = new Relation(this, 'relation' /*internal relation label-name*/);
//note: it is NOT a static property. It can be replaced with
constructor(...args){
super(...args);
this.subjects = new Relation(this, 'relation' /*internal relation label-name*/);
}
}
class RecordSubject extends ConnectedRecord {
//target is optional! and direction is optional too, it should be -1 for reverse relations.
subjects = new Relation(this, 'relation', {target: Object, direction: -1});
}
RecordObject.register()
RecordSubject.register()
async function main() {
const object = await new RecordObject({baz: true}).save()
const subject = await new RecordSubject().save()
await object.subjects.add(subject)
console.log(await subject.objects.size()) // => 1
const objects = await subject.objects.entries()
console.log(objects[0].baz) => //true
}
even for deep relations:
class User extends ConnectedRecord {
roles = new Relation(this, 'has_role', {target: Role});
permissions = new Relation(this.roles, 'has_permission', {target: Permission});
async hasPermission(permission) {
return await this.permissions.has(permission)
}
}
class Role extends ConnectedRecord {
users = new Relation(this, 'has_role', {target: Role, direction: -1});
permissions = new Relation(this, 'has_permission', {target: Permission});
}
class Permission extends ConnectedRecord {
roles = new Relation(this, 'has_permission', {target: Role, direction: -1});
users = new Relation(this.roles, 'has_role', {target: User, direction: -1});
}
Relation instances have bunch of pretty methods to use (you can always pass a transaction as last argument):
class Record {
async only(): Record
async only(null | Record): void
async has(records: Array<Record>): bool
async intersect(records: Array<Record>): Array<Record>
async add(records: Array<Record>): void
async delete(records: Array<Record>): void
async clear(): void
async size(): number
async entries(): Array<Record>
async where(params?: WhereParams, opts?: WhereOpts): Array<Record>
}
AGR automatically brings uuid key (cannot be re-defined), created_at and updated_at (in milliseconds) fields when record is reflected.
Record and Relation have static where method to use for querying. All details are provided in API page, in brief - order, limit, offset can be used for filtering, equality, existence, numeric (greater/less), string (starts/ends with, contains), array (contains/includes) queries are available
Examples:
Entry.where({foo: 1000}, {limit: 10, offset: 10, order: 'created_at'})
Entry.where({updated_at: {$gte: Date.now() - 1000}}, {order: ['created_at DESC']})
Entry.where({foo: {$exists: true, $startsWith: ['b', 'ba', 'baz'], $endsWith: 'bar', $contains: 'z'}})
// here e.g. {foo: [1,2,3,4,5], bar: 3} will be reflected.
// $has stands for "db record has fields", $in - for "db record is in list of possible fields"
Entry.where({foo: {$has: [1,2,3]}, bar: {$in: [1,2,3]}})
//$in can also work with array
Entry.where({foo: {$in: [[0], [1,2,3,4,5]]}}})
Sure. beforeCreate, afterCreate, beforeUpdate, afterUpdate, beforeDestroy, afterDestroy are available hooks
class Entry extends Record {
async beforeCreate() {
//this.connection points here to transaction, so you have to pass it if calling other classes
const test = await Test.where({id: this.id}, this.connection)
this.testId = test.testId
}
}
Yes, AGR has transactions. Hooks (see above) are always inside a transaction. They can be used by using special decorator (with babel-plugin-transform-decorators-legacy, will be changed to new syntax when new spec will become stable) @acceptsTransaction({force: true}) or called explicitly by connection.transaction() All transactions should be committed or rolled back. On SIGINT AGR will attempt to rollback all not closed yet transactions. By default Neo4j rolls back transactions in 60 seconds after last query.
Good example of transaction usage is Record#firstOrCreate sugar-ish method:
class Record {
@acceptsTransaction
static async firstOrInitialize(params) {
const tx = this.connection.transaction()
let [result] = await this.where(params, {limit: 1}, tx.transaction())
if (!result)
result = await new this().save(params, tx.transaction())
await tx.commit()
return result
}
}
FAQs
Schema-less ActiveRecord over neo4j
We found that active-graph-record demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Socket uncovered npm malware campaign mimicking popular Node.js libraries and packages from other ecosystems; packages steal data and execute remote code.
Research
Socket's research uncovers three dangerous Go modules that contain obfuscated disk-wiping malware, threatening complete data loss.
Research
Socket uncovers malicious packages on PyPI using Gmail's SMTP protocol for command and control (C2) to exfiltrate data and execute commands.