Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
##DoQmentDB - A Promise-Based DocumentDB Client
DoQmentDB is a tiny layer that provides the simplicity of MongoDB for DocumentDB users(support schema, hooks/middleware, atomic-transactions, udf and more).
##Table of contents:
#Get Started (1) You can install DoQmentDB using 2 different methods:
(2) Add to your project:
var DoqmentDB = require('doqmentdb');
(3) Start Playing with DoqmentDB:
var DoQmentDB = require('doqmentdb');
// Create DocumentDB connection
var connection = new (require('documentdb').DocumentClient)(HOST, OPTIONS);
// Pass connection and database-name, if `test` is not exist it will create one.
var db = new DoQmentDB(connection, 'test');
// Create a CollectionManager instance, if `users` is not exist it will create one.
var users = db.use('users');
// Using schema
users.schema(model);
// Add hooks(see `users` full example)
users.pre('save', function(next) {
var doc = this;
doc.createdAt = new Date().toString();
next();
});
// Each http function returns a `Promise` with two specific methods: success and error.
users.create({ name: '6534' })
.then(console.log);
users.findById(1)
.then(console.log);
users.findAndRemove({ isAdmin: false })
.then(console.log);
#Database
Create a DatabaseManager by passing connection
and databaseName
.
var DoQmentDB = require('doqmentdb');
// Create DocumentDB connection
var connection = new (require('documentdb').DocumentClient)(HOST, OPTIONS);
// if `test` is not exist it will create one
var db = new DoQmentDB(connection, 'test');
##create
Get name and crete new collection in the used db.
Usage: db.create(string)
Aliases: insert
Returns: Object
db.create('users')
.then(console.log);
##getDatabase
Return the used database.
Usage: db.getDatabase()
db.getDatabase()
.then(console.log);
##find
find collection by given object params.
Note: to return all documents, omit params argument or pass an empty object({}).
Usage: db.find(object[optional])
Returns: Array
db.find()
.then(console.log); // Return all collections
db.find({ id: 'users' })
.then(console.log); // Return collections where id equal to `users`
##findById
find collection by given string
id.
Usage: db.findById(string)
Returns: Object
db.findById('users')
.then(console.log);
##findOrCreate
get object properties, search for collection, if it not exist create one.
Usage: db.findOrCreate(object)
Returns: Object
db.findOrCreate({ name: 'users', id: '#1' })
.then(console.log);
##remove
get collection id as a String
, if it exist - remove it and return undefined
, else return false
.
Usage: db.remove(string)
Returns: undefined
or Boolean
db.remove('test')
.then(console.log);
##use
get collection name and return CollectionManager
instance.
Note: if the given collection
is not exist it will create one.
Usage: var coll = db.use(string);
Returns: object
instanceof CollectionManager
var users = db.use('users'); // This operation is not async
#Collection
Create a CollectionManager by passing to .use
function a collection name.
var users = db.use('users');
console.log(users.constructor.name); // Collection
##create
get object properties, and create new document under the used collection.
Usage: users.create(object)
Aliases: insert
Returns: Object
users.create({ name: 'Ariel', admin: true })
.then(console.log); // { name: 'Ariel', admin: true, id: '8...31', _self: ... }
##createOrUpdate
create a new document under the current collection, or update an existing document with the same id.
Usage: users.createOrUpdate(object)
Aliases: upsert
Returns: Object
users.upsert({ id: 'my_user_id', admin: true })
.then(console.log); // { id: 'my_user_id', admin: true, _self: ... }
##getCollection
return the used collection.
Usage: users.getCollection()
users.getCollection()
.then(console.log);
##find
get object properties and return array of results.
Usage: users.find(object)
Note: to return all collections, omit params argument or pass an empty object({}).
Returns: Array
users.find({ active: true })
.then(console.log);
##findOne
get object properties and return the first matching result.
Usage: users.findOne(object)
Returns: Object
users.findOne({ active: true, name: 'Bar' })
.then(console.log);
##findById
find document by giving a string
id.
Usage: users.findById(string)
Returns: Object
users.findById('53...3')
.then(console.log);
##findAndRemove
get object properties to search, find the equivalents and remove them.
Usage: users.findAndRemove(object)
Returns: Array
Note: if you want support atomic-transactions(i.e: do things concurrently, e.g: distributed system),
you need use this method prefix with $
sign.
users.findAndRemove({ name: 'Ariel' })
.then(console.log);
// Using stored procedure
users.$findAndRemove({ name: 'Ariel' })
.then(console.log);
// Remove all users
users.findAndRemove({})
.then(console.log);
##findOneAndRemove
get object properties, and remove the first matching result.
Usage: users.findOneAndRemove(object)
Returns: undefined
or Boolean
Note: if you want support atomic-transactions(i.e: do things concurrently, e.g: distributed system),
you need use this method prefix with $
sign.
users.findOneAndRemove({ name: 'Ariel', admin: true })
.then(console.log);
// Using stored procedure
users.$findOneAndRemove({ name: 'Ariel', admin: true })
.then(console.log);
##findAndModify
get object properties to search, find the equivalents and modify them(extend
operation).
Usage: users.findAndModify(object, extend)
Aliases: update
Returns: Array
Note: if you want support atomic-transactions(i.e: do things concurrently, e.g: distributed system),
you need use this method prefix with $
sign.
users.update({ name: 'Ariel', admin: true }, { admin: false })
.then(console.log);
// Push 'a' and 'b' to `list` field(do it concurrently)
['a', 'b'].forEach(function(ch) {
users.$update({}, { list: { $push: ch } });
});
##findOneAndModify
get object properties and modify(extend
operation) the first matching.
Usage: users.findOneAndModify(object, extend)
Aliases: updateOne
Returns: Object
Note: if you want support atomic-transactions(i.e: do things concurrently, e.g: distributed system),
you need use this method prefix with $
sign.
users.findOneAndModify({ admin: false }, { admin: true })
.then(console.log);
// Using stored procedure
users.$findOneAndModify({ admin: false }, { admin: true })
.then(console.log);
##findOrCreate
get object properties, search for document, if it not exist create one.
Usage: users.findOrCreate(object)
Returns: Object
Note: if you want support atomic-transactions(i.e: do things concurrently, e.g: distributed system),
you need use this method prefix with $
sign.
users.findOrCreate({ admin: false, name: 'Ariel' })
.then(console.log);
// Using stored procedure
users.$findOrCreate({ admin: false, name: 'Ariel' })
.then(console.log);
#Queries ###Operators
$or
OR$and
AND$not
NOT$nor
NOT(... OR ...)$gt
>$gte
>=$lt
<$lte
<=$ne
<> or !=$in
like Array.prototype.some(...)
$all
like Array.prototype.every(...)
$type
typeof value
$regex
new RegExp(...).test(value)
$size
test array.length
###Examples
users.find({ a: 1, b: 2, c: '3' })
// ... r WHERE r.a=1 AND r.b=2 AND r.c="3"
users.find({ $or: [{ a: 2, b: 3}, { c: 3 }] })
// ... r WHERE ((r.a=2 AND r.b=3) OR r.c=3)
users.find({ $not: { a: 1, b: 2, c: 3 } })
// ... r WHERE NOT(r.a=1 AND r.b=2 AND r.c=3)
users.find({ $nor: [ { a: 1 }, { b: 3 }]})
// ... r WHERE NOT(r.a=1 OR r.b=3)
users.find({ $nor: [ { a: 1, b: 1 }, { c: 3 } ] })
// ... r WHERE NOT((r.a=1 AND r.b=1) OR r.c=3)
users.find({ $not: { name: { $gt: 3 }, age: 12 } })
// ... r WHERE NOT(r.name > 3 AND r.age=12)
users.find({ $not: { name: { $ne: 'bar' } } })
// ... r WHERE NOT(r.name <> "bar")
users.find({ $or: [
{ name: { $ne: 'Ariel' } },
{ age: { $lte: 26 } },
{ $and: [
{ isAdmin: { $ne: false } },
{ isUser: { $ne: false } }
]}
]})
// ... r WHERE r.name <> "Ariel" OR r.age <= 26 OR (r.isAdmin <> false AND r.isUser <> false)
users.find({ coins: { $in: 2 } })
// ... r WHERE inUDF(r.coins, 2)
users.find({ $not: { age: { $type: 'number' } } })
// ... r WHERE NOT(typeUDF(r.age, "number"))
#Operations
When using one of the update operations(e.g: .update()
, .findAndModify()
, etc...), you could use the build-in prototype
functions(based on the type) prefixing with $
sign.
Usage: users.update({ ... }, { keyName: { $method: value } })
Note: value could be single or array of arguments.
// Find all, and push 2 to `arr` field
users.update({}, { arr: { $push: 2 } });
// Suffix all users `name` with #
users.update({}, { name: { $concat: '#' } });
// Trim the name from `foo` to `o`
users.update({ name: 'foo' }, { name: { $substr: [1,1] } });
#Schema
Manage your documents with schema.
fields:
type
String
, Boolean
, Number
, etc..).default
regex
/^[a-zA-Z0-9@:%_\+.~#?&//=|/d]{10,}$/
).error
regex
/type
). see: exampleexpose
expose
by default is true
, unless you set it to false
, it's means that all the find
operations returns the documents without exposing this fields. see: exampleExample using schema:
schema: model.js
module.exports = {
/**
* @field name
* @default no default value
*/
name: {
type: String,
'default': ''
},
/**
* @field email
* @default no default value
* @regex email, min-length = 10
*/
email: {
type: String,
'default': '',
regex: /^[a-zA-Z0-9@:%_\+.~#?&//=|/d]{10,}$/,
error: '`email` must be type string, valid email address, and least 10 chars',
expose: true
},
/**
* @field password
* @default no default value
* @regex password
*/
password: {
type: String,
'default': '',
regex: /^.*(?=.{8,})(?=.*[a-zA-Z])(?=.*\d)(?=.*[!#$%&? "]).*$/,
error: '`password` must be type string, contain 8 chars and at least one number, ' +
'one letter and one unique character such as !#$%&? "',
expose: false
},
/**
* @field isAdmin
* @default false
*/
isAdmin: {
type: Boolean,
'default': false
}
};
using schema(model.js
)
var DoQmentDB = require('doqmentdb');
var model = require('./model'); // Get model/schema
var connection = new (require('documentdb') // Create DocumentDB connection
.DocumentClient)(CONFIG.HOST, CONFIG.OPTIONS);
var db = new DoQmentDB(connection, CONFIG.DB); // Create DBManager 'test'
var users = db.use('users'); // Create CollectionManager 'users'
users.schema(model); // Using schema
users.create({ password: 'Ar2!as_s'})
.then(console.log)
.catch(console.log);
/*
[Error:
`email` must be type string, valid email address, and least 10 chars
]
*/
users.create({ name: 'Ariel', email: 'ariel.com', password: 'Ar2!as_s'})
.then(console.log)
.catch(console.log);
/*
[Error:
`email` must be type string, valid email address, and least 10 chars
]
*/
users.create({ name: 'Ariel', email: 'a8m@gm.com', password: 'Ar2!as_s'})
.then(console.log)
.catch(console.log);
/*
{
name: 'Ariel',
email: 'a8m@gm.com',
password: 'Ar2!as_s',
id: '2eb7...c0',
...
}
*/
users.find({})
.then(console.log);
/*
Get all documents but without exposing fields(i.e: omit `password` field)
*/
see: How to architect your models
#Middleware
Middleware/Hooks are executed at the document level(create
/save
/insert
, update
, remove/delete
).
There are two types of middleware, pre and post.
##pre
Usage: users.pre(operation, callback)
Note: pre
middleware are executed one after another, when each middleware calls next.
Example:
users.pre('save', function(next) {
var doc = this;
doc.createdAt = new Date().toString();
next();
}, function(next) {
var doc = this;
doc.updatedAt = new Date().toString();
next();
});
// Do something async
users.pre('save', function(next) {
var doc = this;
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(doc.password, salt, function(err, hash) {
doc.password = hash;
next();
});
});
});
// ##Note: the order is importatnt, this example order:
// `createdAt()`, `updatedAT()`, `hash/bcrypt()`, and then the `.create` operation will called
##post
Usage: users.post(operation, callback)
Note: post
middleware are executed in parallel.
Example:
users.post('save', function(doc) {
logger(new Date(), doc, 'saved!')
});
#Atomic Transactions
Since v0.2.6 DoQmentDB supports atomic-transactions using a built-in sporcs(i.e: stored procedures) to handle concurrently well.
Note: To perform some operation this way, you should prefix it with $
.
Read More: DocumentDB - Atomic Transactions
// Lets take some example of `consuming` from two differents
// Service-Bus queues and update the same `model`/`document`
//
// Note: This also could happen in a distributed system, when two operations happens in parallel
// We have a `stores` collection that holds the `sales` and the `users`
// fields per `store`(a Document)
// We are using the `atomic` version of `update`, because we don't want to lose data
sbs.receiveQueueMessage('sales', function(msg) {
stores.$update({ id: msg.id }, { sales: { $push: msg.sale } });
// Polling again...
});
sbs.receiveQueueMessage('users', function(msg) {
stores.$update({ id: msg.id }, { users: { $push: msg.user } });
// ...
});
#Examples
#Changelog ##0.2.9
##0.2.8
updateOne
and $updateOne
(the conccurent one)findAndModify
)##0.2.6
Since 0.2.6 DoQmentDB support atomic transactions using DocumentDB stored procedures
.
Methods that support:
update
/findAndModify
findOneAndModify
findOrCreate
findAndRemove
findOneAndRemove
If you want to use one of this methods, you should use them prefix with $
sign.
// Push 'a' and 'b' to `list` field(do it concurrently)
['a', 'b'].forEach(function(ch) {
users.$update({}, { list: { $push: ch } });
});
FAQs
A Promise-Based DocumentDB ODM Client for NodeJS
We found that doqmentdb 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.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.