Comparing version 1.2.4 to 1.3.1
@@ -16,5 +16,8 @@ "use strict"; | ||
, UPDATE: "update" | ||
, FILE_SAVE: "file_save" | ||
, FILE_REMOVE: "file_remove" | ||
, ESCAPE_PREFIX: "*_**ojlint**escape*__tx__00***___string" | ||
, DEFAULT_COLLECTION: "OJLINTTASKCOLLECTION" | ||
, OJ_FUTURE: "$ojFuture" | ||
, MONGOOSE_READY: 1 | ||
}; |
@@ -62,3 +62,3 @@ "use strict"; | ||
function checkInitStatus(){ | ||
if(!(Roller)){ | ||
if (!(Roller)) { | ||
throw new Error("Fawn has not been initialized. Call Fawn.init"); | ||
@@ -65,0 +65,0 @@ } |
@@ -13,6 +13,8 @@ "use strict"; | ||
var DONE = constants.DONE; | ||
var ROLLED = constants.ROLLED; | ||
var SAVE = constants.SAVE; | ||
var UPDATE = constants.UPDATE; | ||
var REMOVE = constants.REMOVE; | ||
var ROLLED = constants.ROLLED; | ||
var FILE_REMOVE = constants.FILE_REMOVE; | ||
var FILE_SAVE = constants.FILE_SAVE; | ||
var TaskMdl; | ||
@@ -108,3 +110,8 @@ var utils; | ||
case UPDATE: | ||
case REMOVE: return rollbackDeleteOrUpdate | ||
case REMOVE: | ||
return rollbackRemoveOrUpdate; | ||
case FILE_SAVE: | ||
return rollbackFileSave; | ||
case FILE_REMOVE: | ||
return rollbackFileRemove; | ||
} | ||
@@ -118,2 +125,4 @@ } | ||
* @param task the task containing the step | ||
* | ||
* @returns {Promise|*} | ||
*/ | ||
@@ -129,5 +138,5 @@ function rollbackSave(save, task) { | ||
/** | ||
* Rollback for delete or update step. | ||
* Rollback for remove or update step. | ||
* | ||
* @param step the step to update | ||
* @param step the update or remove step | ||
* @param task the task containing the step. | ||
@@ -137,3 +146,3 @@ * | ||
*/ | ||
function rollbackDeleteOrUpdate(step, task){ | ||
function rollbackRemoveOrUpdate(step, task) { | ||
var Collection = getModel(step.name); | ||
@@ -145,8 +154,11 @@ var chain = Promise.resolve(); | ||
return Collection.findById(data._id).exec().then(function (doc) { | ||
if (doc) { | ||
if (doc && step.type === UPDATE) { | ||
return doc.update(data).exec(); | ||
} | ||
else if (!doc && step.type === REMOVE) { | ||
doc = new Collection(data); | ||
return doc.save(); | ||
} | ||
doc = new Collection(data); | ||
return doc.save(); | ||
return Promise.resolve(); | ||
}); | ||
@@ -161,2 +173,64 @@ }); | ||
/** | ||
* Rollback for file save step. | ||
* | ||
* @param step the file save step | ||
* @param task the task containing the step. | ||
* | ||
* @returns {Promise|*} | ||
*/ | ||
function rollbackFileSave(step, task) { | ||
return new Promise(function (resolve, reject) { | ||
var gfs = new Grid(mongoose.connection.db); | ||
utils.removeFile(step.dataStore[0]._id, gfs) | ||
.then(resolve) | ||
.catch(reject); | ||
}).then(function () { | ||
return updateState(task, step.index, ROLLED); | ||
}); | ||
} | ||
/** | ||
* Rollback for file remove step. | ||
* | ||
* @param step the file remove step | ||
* @param task the task containing the step. | ||
* | ||
* @returns {Promise|*} | ||
*/ | ||
function rollbackFileRemove(step, task) { | ||
return new Promise(function (resolve, reject) { | ||
var gfs = Grid(mongoose.connection.db); | ||
var data = step.dataStore[0]; | ||
gfs.exist({_id: data.removed}, function (err, exists) { | ||
if (err) return reject(err); | ||
gfs.findOne({_id: data.shadow}, function (err, shadowFile) { | ||
if (err) return reject(err); | ||
if (!shadowFile) return resolve(); | ||
function done() { | ||
utils.removeFile(data.shadow, gfs).then(function () { | ||
updateState(task, step.index, ROLLED) | ||
.then(resolve) | ||
.catch(reject) | ||
}); | ||
} | ||
if (exists) return done(); | ||
var writeStream = gfs.createWriteStream(shadowFile.metadata.oldFile); | ||
writeStream.on("close", done); | ||
writeStream.on("error", reject); | ||
gfs.createReadStream({_id: data.shadow}).pipe(writeStream); | ||
}); | ||
}); | ||
}); | ||
} | ||
module.exports = RollerProvider; |
302
lib/task.js
"use strict"; | ||
var mongoose = require("mongoose"); | ||
var Promise = require("bluebird"); | ||
var Grid = require("gridfs-stream"); | ||
var fs = require("fs"); | ||
var constants = require("./constants"); | ||
@@ -14,4 +16,7 @@ | ||
var REMOVE = constants.REMOVE; | ||
var FILE_SAVE = constants.FILE_SAVE; | ||
var FILE_REMOVE = constants.FILE_REMOVE; | ||
// variables which require a mongoose instance | ||
var mongoose; | ||
var utils; | ||
@@ -30,5 +35,4 @@ var updateState; | ||
* | ||
* @param mongoose The mongoose instance to be used | ||
* @param _mongoose The mongoose instance to be used | ||
* @param _TaskMdl The mongoose model for tasks (where tasks are stored) | ||
* | ||
* @returns {Task} | ||
@@ -38,3 +42,5 @@ * | ||
*/ | ||
var TaskProvider = function (mongoose, _TaskMdl) { | ||
var TaskProvider = function (_mongoose, _TaskMdl) { | ||
mongoose = _mongoose; | ||
Grid.mongo = mongoose.mongo; | ||
TaskMdl = _TaskMdl; | ||
@@ -48,3 +54,2 @@ Roller = require("./roller")(mongoose, _TaskMdl); | ||
return Task; | ||
@@ -223,2 +228,60 @@ }; | ||
/** | ||
* Adds a saveFile step (saveFileObj) to the steps queue | ||
* and increments the index. | ||
* | ||
* @param filePath path of the file to be saved | ||
* @param options options for saving the file | ||
* | ||
* @returns {Task} | ||
*/ | ||
task.saveFile = function (filePath, options) { | ||
if (!filePath) throw new Error("File path is required and must be a string"); | ||
if (options) { | ||
if (!isObject(options)) throw new Error("options must be an object"); | ||
handle$Token(options); | ||
} | ||
var saveFileObj = { | ||
type: FILE_SAVE | ||
, index: index | ||
, state: INITIAL | ||
, data: { | ||
file_path: filePath | ||
} | ||
, options: options | ||
}; | ||
steps.push(saveFileObj); | ||
index++; | ||
return task; | ||
}; | ||
/** | ||
* Adds a removeFile step (removeFileObj) to the steps queue | ||
* and increments the index. | ||
* | ||
* @param options options for removing the file | ||
* | ||
* @returns {Task} | ||
*/ | ||
task.removeFile = function (options) { | ||
if (!isObject(options)) throw new Error("options required. Must be an object"); | ||
handle$Token(options); | ||
var removeFileObj = { | ||
type: FILE_REMOVE | ||
, index: index | ||
, state: INITIAL | ||
, options: options | ||
}; | ||
steps.push(removeFileObj); | ||
index++; | ||
return task; | ||
}; | ||
/** | ||
* Runs a task. This function saves the steps to | ||
@@ -247,6 +310,11 @@ * the db and proceeds to complete each step. If | ||
return chain.then(function (results) { | ||
return _task.remove() | ||
.then(function () { | ||
return Promise.resolve(results); | ||
}); | ||
var gfs = Grid(mongoose.connection.db); | ||
var removeChain = utils.makeRemoveChain(_task, gfs); | ||
return removeChain.then(function () { | ||
return _task.remove() | ||
.then(function () { | ||
return Promise.resolve(results); | ||
}); | ||
}); | ||
}).catch(function (err) { | ||
@@ -274,2 +342,6 @@ return Roller.rollOne(_task).then(function () { | ||
case REMOVE: return performRemove; | ||
case FILE_SAVE: | ||
return performFileSave; | ||
case FILE_REMOVE: | ||
return performFileRemove | ||
} | ||
@@ -281,4 +353,4 @@ } | ||
* | ||
* @param update the update step | ||
* @param task the task containing update | ||
* @param step the update step | ||
* @param task the task which step belongs to | ||
* @param results array of results from previous operations | ||
@@ -288,17 +360,17 @@ * | ||
*/ | ||
function performUpdate(update, task, results) { | ||
var Collection = getModel(update.name); | ||
function performUpdate(step, task, results) { | ||
var Collection = getModel(step.name); | ||
handle$Token(update.condition, true); | ||
handle$Token(update.data, true); | ||
handle$Token(step.condition, true); | ||
handle$Token(step.data, true); | ||
resolveFuture(update.condition, results); | ||
resolveFuture(update.data, results); | ||
resolveFuture(step.condition, results); | ||
resolveFuture(step.data, results); | ||
// console.log(update); | ||
return storeOldData(update, task) | ||
return storeOldData(step) | ||
.then(function(){ | ||
return updateState(task, update.index, PENDING) | ||
return updateState(task, step.index, PENDING) | ||
.then(function () { | ||
return Collection.update(update.condition, update.data, update.options) | ||
return Collection.update(step.condition, step.data, step.options) | ||
.exec() | ||
@@ -308,3 +380,3 @@ .then(function (result) { | ||
return updateState(task, update.index, DONE, results); | ||
return updateState(task, step.index, DONE, results); | ||
}); | ||
@@ -315,8 +387,7 @@ }); | ||
/** | ||
* This function handles the save step. | ||
* | ||
* @param save the save step | ||
* @param task the task containing save | ||
* @param step the save step | ||
* @param task the task which step belongs to | ||
* @param results array of results from previous operations | ||
@@ -326,9 +397,9 @@ * | ||
*/ | ||
function performSave(save, task, results) { | ||
var Collection = getModel(save.name); | ||
function performSave(step, task, results) { | ||
var Collection = getModel(step.name); | ||
handle$Token(save.data, true); | ||
resolveFuture(save.data, results); | ||
handle$Token(step.data, true); | ||
resolveFuture(step.data, results); | ||
var doc = new Collection(save.data); | ||
var doc = new Collection(step.data); | ||
var dataStore = []; | ||
@@ -339,10 +410,10 @@ | ||
task.steps[save.index].dataStore = dataStore; | ||
task.steps[save.index].markModified("dataStore"); | ||
step.dataStore = dataStore; | ||
step.markModified("dataStore"); | ||
return updateState(task, save.index, PENDING).then(function () { | ||
return updateState(task, step.index, PENDING).then(function () { | ||
return doc.save().then(function (result) { | ||
results.push(result); | ||
return updateState(task, save.index, DONE, results); | ||
return updateState(task, step.index, DONE, results); | ||
}); | ||
@@ -355,4 +426,4 @@ }); | ||
* | ||
* @param remove the remove step | ||
* @param task the task containing remove | ||
* @param step the remove step | ||
* @param task the task which step belongs to | ||
* @param results array of results from previous operations | ||
@@ -362,14 +433,14 @@ * | ||
*/ | ||
function performRemove(remove, task, results) { | ||
var Collection = getModel(remove.name); | ||
function performRemove(step, task, results) { | ||
var Collection = getModel(step.name); | ||
handle$Token(remove.condition, true); | ||
resolveFuture(remove.condition, results); | ||
handle$Token(step.condition, true); | ||
resolveFuture(step.condition, results); | ||
return storeOldData(remove, task).then(function () { | ||
return updateState(task, remove.index, PENDING).then(function () { | ||
return Collection.remove(remove.condition).exec().then(function (result) { | ||
return storeOldData(step).then(function () { | ||
return updateState(task, step.index, PENDING).then(function () { | ||
return Collection.remove(step.condition).exec().then(function (result) { | ||
results.push(result); | ||
return updateState(task, remove.index, DONE, results); | ||
return updateState(task, step.index, DONE, results); | ||
}); | ||
@@ -381,2 +452,120 @@ }); | ||
/** | ||
* This function handles the file save step. | ||
* | ||
* @param step the file save step | ||
* @param task the task which step belongs to | ||
* @param results array of results from previous operations | ||
* | ||
* @returns {Promise|*} | ||
*/ | ||
function performFileSave(step, task, results) { | ||
handle$Token(step.options, true); | ||
resolveFuture(step.options, results); | ||
var options = step.options || {}; | ||
var dataStore = []; | ||
options._id = options._id || utils.generateId(); | ||
dataStore.push({_id: options._id}); | ||
step.options = options; | ||
step.dataStore = dataStore; | ||
step.markModified("dataStore"); | ||
step.markModified("options"); | ||
return updateState(task, step.index, PENDING).then(function () { | ||
return new Promise(function (resolve, reject) { | ||
var conn = mongoose.connection; | ||
var gfs = Grid(conn.db); | ||
var writeStream = gfs.createWriteStream(options); | ||
writeStream.on("close", function (file) { | ||
results.push(file); | ||
resolve(results); | ||
}); | ||
writeStream.on("error", reject); | ||
fs.createReadStream(step.data.file_path).pipe(writeStream); | ||
}).then(function (results) { | ||
return updateState(task, step.index, DONE, results); | ||
}); | ||
}); | ||
} | ||
/** | ||
* This function handles the file remove step. | ||
* | ||
* @param step the file remove step | ||
* @param task the task which step belongs to | ||
* @param results array of results from previous operations | ||
* | ||
* @returns {Promise|*} | ||
*/ | ||
function performFileRemove(step, task, results) { | ||
handle$Token(step.options, true); | ||
resolveFuture(step.options, results); | ||
var options = step.options; | ||
return storeOldFile(step).then(function (file) { | ||
return updateState(task, step.index, PENDING).then(function () { | ||
return new Promise(function (resolve, reject) { | ||
if (!file) { | ||
results.push(null); | ||
return resolve(); | ||
} | ||
var gfs = Grid(mongoose.connection.db); | ||
utils.removeFile(options, gfs) | ||
.then(function (result) { | ||
results.push(result); | ||
resolve(); | ||
}) | ||
.catch(reject); | ||
}).then(function () { | ||
return updateState(task, step.index, DONE, results); | ||
}); | ||
}); | ||
}); | ||
} | ||
/** | ||
* This function stores a file that's about to be | ||
* removed by a step, for rollback purposes | ||
* | ||
* @param step the step | ||
* | ||
* @returns {Promise|*} | ||
*/ | ||
function storeOldFile(step) { | ||
return new Promise(function (resolve, reject) { | ||
var dataStore = []; | ||
var gfs = Grid(mongoose.connection.db); | ||
gfs.findOne(step.options, function (err, file) { | ||
if (err) return reject(err); | ||
if (!file) return resolve(false); | ||
dataStore.push({removed: file._id, shadow: utils.generateId()}); | ||
step.dataStore = dataStore; | ||
step.save().then(function () { | ||
var writeStream = gfs.createWriteStream({_id: dataStore[0].shadow, metadata: {oldFile: file}}); | ||
writeStream.on("close", resolve); | ||
writeStream.on("error", reject); | ||
gfs.createReadStream({_id: file._id}).pipe(writeStream); | ||
}); | ||
}); | ||
}); | ||
} | ||
/** | ||
* This function stores data that's about to be | ||
@@ -386,10 +575,9 @@ * changed by a step, for rollback purposes | ||
* @param step the step | ||
* @param task the task containing step | ||
* | ||
* @returns {Promise|*} | ||
*/ | ||
function storeOldData(step, task){ | ||
function storeOldData(step) { | ||
var Collection = getModel(step.name); | ||
var options = step.options || step.type === REMOVE ? {multi: true} : null; | ||
var query = Collection.find(step.condition); | ||
var query = Collection.find(step.condition).lean(); | ||
var searchQuery = options && options.multi === true | ||
@@ -402,10 +590,4 @@ ? query | ||
.then(function(result) { | ||
var oldData = []; | ||
for (var i = 0; i < result.length; i++) { | ||
oldData.push(result[i].toObject()); | ||
} | ||
task.steps[step.index].dataStore = oldData; | ||
task.steps[step.index].markModified("dataStore"); | ||
step.dataStore = result; | ||
step.markModified("dataStore"); | ||
}); | ||
@@ -523,3 +705,3 @@ } | ||
var parts = placeholder.split("."); | ||
if (isNaN(parts[0])) throw new Error("index must be a number"); | ||
if (isNaN(parts[0])) throw new Error("step index must be a number"); | ||
@@ -534,5 +716,11 @@ var index = parseInt(parts[0]); | ||
if (!isObject(result))throw new Error("Can't use keys on non-object"); | ||
if (result instanceof Array) { | ||
if (isNaN(parts[i])) throw new Error("Array index must be a number"); | ||
parts[i] = parseInt(parts[i]); | ||
if (parts[i] >= result.length)throw new Error("Array index out of bounds"); | ||
} | ||
else if (!isObject(result))throw new Error("Can't use keys on non-object"); | ||
if (!result[parts[i]]) throw new Error("No such key exists in result"); | ||
result = result[parts[i]] | ||
@@ -539,0 +727,0 @@ } |
113
lib/utils.js
@@ -10,2 +10,4 @@ "use strict"; | ||
var constants = require("./constants"); | ||
module.exports = function(_mongoose){ | ||
@@ -15,2 +17,12 @@ var mongoose = _mongoose || require("mongoose"); | ||
/** | ||
* Updates the state of a task's step. | ||
* | ||
* @param task the task | ||
* @param index the index of the step to update | ||
* @param state the new state of the step | ||
* @param results array of results from previous steps | ||
* | ||
* @returns {Promise|*} | ||
*/ | ||
function updateState(task, index, state, results) { | ||
@@ -25,2 +37,8 @@ task.steps[index].state = state; | ||
/** | ||
* gets a collection as a mongoose model. | ||
* | ||
* @param name name of the collection | ||
* @param schema schema for the model | ||
*/ | ||
function getCollection(name, schema){ | ||
@@ -34,2 +52,8 @@ if (schema) { | ||
/** | ||
* Adds a model to the model cache | ||
* | ||
* @param name name of model | ||
* @param schema schema for the model | ||
*/ | ||
function setModel(name, schema){ | ||
@@ -39,2 +63,11 @@ modelCache[name] = getCollection(name, schema); | ||
/** | ||
* Gets a mongoose model. Creates one if it | ||
* doesn't exist already. | ||
* | ||
* @param name name of the model to retrieve | ||
* @param schema schema for the model | ||
* | ||
* @returns a mongoose model | ||
*/ | ||
function getModel(name, schema){ | ||
@@ -66,2 +99,7 @@ if(!modelCache[name]){ | ||
/** | ||
* Drops a MongoDB collection. For testing. | ||
* | ||
* @param collection the name of the collection to be dropped | ||
*/ | ||
function dropCollection(collection){ | ||
@@ -76,2 +114,74 @@ return new Promise(function(resolve, reject){ | ||
/** | ||
* Removes a file from the db | ||
* | ||
* @param id file id or options object | ||
* @param gfs GridFS | ||
*/ | ||
function removeFile(id, gfs) { | ||
var options = {_id: id}; | ||
if (id.constructor && id.constructor === Object) { | ||
options = id; | ||
} | ||
return new Promise(function (resolve, reject) { | ||
gfs.remove(options, function (err, result) { | ||
if (err) return reject(err); | ||
resolve(result); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Checks if a file exists in the db with the | ||
* specified file name | ||
* | ||
* @param id MongoDB ObjectId | ||
* @param gfs GridFS | ||
*/ | ||
function fileExists(id, gfs) { | ||
return new Promise(function (resolve, reject) { | ||
gfs.exist({_id: id}, function (err, exists) { | ||
if (err) return reject(err); | ||
resolve(exists); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Chains together the deletion of shadow files | ||
* from file remove steps. | ||
* | ||
* @param task the task to check for shadow files | ||
* @param gfs GridFS | ||
* | ||
* @returns {Promise|*} | ||
*/ | ||
function makeRemoveChain(task, gfs) { | ||
var removeChain = Promise.resolve(); | ||
task.steps.forEach(function (step) { | ||
if (step.type !== constants.FILE_REMOVE) return; | ||
var shadowId = step.dataStore[0].shadow; | ||
removeChain = removeChain.then(function () { | ||
return fileExists(shadowId, gfs) | ||
.then(function (exists) { | ||
if (!exists) return Promise.resolve(); | ||
return removeFile(shadowId, gfs); | ||
}); | ||
}); | ||
}); | ||
return removeChain; | ||
} | ||
/** | ||
* generates a MongoDB ObjectId | ||
*/ | ||
function generateID(){ | ||
@@ -89,3 +199,6 @@ return mongoose.Types.ObjectId(); | ||
, generateId: generateID | ||
, removeFile: removeFile | ||
, fileExists: fileExists | ||
, makeRemoveChain: makeRemoveChain | ||
}; | ||
}; |
@@ -13,3 +13,3 @@ /** | ||
, state: {type: Number, required: true} | ||
, name: {type: String, required: true} | ||
, name: {type: String} | ||
, condition: {} | ||
@@ -16,0 +16,0 @@ , dataStore: [{}] |
{ | ||
"name": "fawn", | ||
"version": "1.2.4", | ||
"version": "1.3.1", | ||
"description": "Library for atomic-ish operations in MongoDB", | ||
@@ -34,2 +34,3 @@ "main": "index.js", | ||
"bluebird": "3.4.1", | ||
"gridfs-stream": "1.1.1", | ||
"mongoose": "4.5.1" | ||
@@ -36,0 +37,0 @@ }, |
@@ -60,3 +60,3 @@ # Fawn | ||
task.update("Accounts", {firstName: "Broke", lastName: "Ass"}, {$inc: {balance: -20}}) | ||
task.update("Accounts", {firstName: "Coke", lastName: "Dealer"}, {$inc: {balance: 20}}) | ||
task.update("Accounts", {firstName: "The", lastName: "Plug"}, {$inc: {balance: 20}}) | ||
task.run() | ||
@@ -73,5 +73,22 @@ .then(function(){ | ||
``` | ||
[GridFS]: <https://docs.mongodb.com/manual/core/gridfs/> | ||
The server could crash before a task is complete, You can use the Roller to rollback all incomplete transactions before starting your server. | ||
Files can be saved and removed to and from [GridFS][]: | ||
```javascript | ||
var newImageId = someMongoDbId; | ||
task.saveFile("/path/to/new/profile/img", {_id: newImageId, filename: "profile.png"}) | ||
.removeFile({_id: oldImageId}) | ||
.update("users", {_id: userId}, {profileImageId: newImageId}) | ||
.run() | ||
.then(function(results){ | ||
var newImgFile = results[0]; | ||
console.log(newImgFile.filename) // profile.png | ||
}); | ||
``` | ||
The server could crash before a task is complete, You can use the Roller to rollback all incomplete transactions before starting your server: | ||
```javascript | ||
@@ -96,2 +113,4 @@ // assuming Fawn has been initialized. See Fawn.init below | ||
- [task.remove](#task_remove) | ||
- [task.saveFile](#task_savefile) | ||
- [task.removeFile](#task_removefile) | ||
- [task.run](#task_run) | ||
@@ -267,2 +286,45 @@ - [Fawn.Roller](#fawn_roller) | ||
### <a name="task_savefile"></a>task.saveFile(filePath, options): Save a file to the db via [GridFS][] | ||
> filePath (required): Name of the collection we're deleting from or a mongoose model or a mongoose document | ||
> options (optional): Same as in [GridStore](http://mongodb.github.io/node-mongodb-native/api-generated/gridstore.html#constructor) | ||
Saves the file at "filePath" to the database using GridFS. The result of this operation is the saved file's object. See [File object](https://docs.mongodb.com/manual/core/gridfs/#the-files-collection) | ||
```javascript | ||
task.saveFile("path/to/some/file", {filename: "a_string_filename.ext"}) | ||
.update("SomeCollection", updateConditions, updateData) | ||
.run() | ||
.then(function(results){ | ||
var file = results[0]; | ||
console.log(file.filename); // a_string_filename.ext | ||
}); | ||
``` | ||
*Note: No changes will be made to to your database until you call task.run()* | ||
<br> | ||
### <a name="task_removefile"></a>task.removeFile(options): Remove a file from the db via [GridFS][] | ||
> options (required): Same as in [GridStore](http://mongodb.github.io/node-mongodb-native/api-generated/gridstore.html#constructor) | ||
Removes a file that matches "options" from the database using GridFS. The result of this operation is a GridStore instance (can be ignored). See [GridStore](http://mongodb.github.io/node-mongodb-native/api-generated/gridstore.html) | ||
```javascript | ||
task.removeFile({_id: fileId}) | ||
.update("SomeCollection", updateConditions, updateData) | ||
.run() | ||
.then(function(results){ | ||
// if you need the gridStore instance | ||
var gridStore = results[0]; | ||
}); | ||
``` | ||
*Note: No changes will be made to to your database until you call task.run()* | ||
<br> | ||
### <a name="task_run"></a>task.run(): Run a task. | ||
@@ -319,2 +381,3 @@ | ||
``` | ||
<br> | ||
@@ -321,0 +384,0 @@ ## <a name="misc"></a>Miscellaneous |
@@ -5,3 +5,3 @@ "use strict"; | ||
db: "mongodb://127.0.0.1:27017/" | ||
, DB: "test" | ||
, DB: "OJFAWNTESTS" | ||
, TASKS: "lints" | ||
@@ -12,2 +12,4 @@ , Fawn: require("./lib/fawn") | ||
, TEST_COLLECTION_B: "animals" | ||
, TEST_FILE_TEXT: "This text is used to test file features" | ||
, TEST_FILE_PATH: "./test.oj" | ||
, chai: require("chai") | ||
@@ -14,0 +16,0 @@ }; |
@@ -8,2 +8,4 @@ "use strict"; | ||
var fs = require("fs"); | ||
var config = require("../test_conf"); | ||
@@ -16,2 +18,6 @@ config.init(); | ||
global.mongoose = require("mongoose"); | ||
global.Grid = require("gridfs-stream"); | ||
Grid.mongo = mongoose.mongo; | ||
global.utils = require("../lib/utils")(); | ||
@@ -22,2 +28,6 @@ global.expect = config.expect; | ||
global.TEST_COLLECTION_B = config.TEST_COLLECTION_B; | ||
global.TEST_FILE_PATH = config.TEST_FILE_PATH; | ||
global.TEST_FILE_TEXT = config.TEST_FILE_TEXT; | ||
global.TEST_FILE_NAME = "FAWN_TEST.oj"; | ||
global.TEST_FILE_ID = utils.generateId(); | ||
@@ -38,5 +48,7 @@ describe("ALL TESTS", function(){ | ||
fs.writeFileSync(TEST_FILE_PATH, TEST_FILE_TEXT); | ||
}); | ||
after(function(){ | ||
fs.unlinkSync(TEST_FILE_PATH); | ||
return utils.dropCollection(TASKS); | ||
@@ -43,0 +55,0 @@ }); |
@@ -53,2 +53,38 @@ /** | ||
}); | ||
it("should rollback file save", function () { | ||
var gfs = Grid(mongoose.connection.db); | ||
var id = utils.generateId(); | ||
return task.saveFile(TEST_FILE_PATH, {_id: id}) | ||
.remove(TestMdlA, {_id: "fail"}) | ||
.run() | ||
.then(failure) | ||
.catch(function () { | ||
return expect(utils.fileExists(id, gfs)).to.eventually.equal(false); | ||
}); | ||
}); | ||
it("should rollback file remove", function (done) { | ||
var gfs = Grid(mongoose.connection.db); | ||
var id = utils.generateId(); | ||
var writeStream = gfs.createWriteStream({_id: id}); | ||
writeStream.on("close", function () { | ||
task.removeFile({_id: id}) | ||
.remove(TestMdlA, {_id: "fail"}) | ||
.run() | ||
.then(failure) | ||
.catch(function () { | ||
utils.fileExists(id, gfs).then(function (exists) { | ||
if (exists) utils.removeFile(id, gfs); | ||
expect(exists).to.equal(true); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
require("fs").createReadStream(TEST_FILE_PATH).pipe(writeStream); | ||
}); | ||
}); | ||
@@ -55,0 +91,0 @@ }); |
@@ -178,2 +178,28 @@ "use strict"; | ||
describe("#saveFile", function () { | ||
it("should save file successfully", function () { | ||
return task.saveFile(TEST_FILE_PATH, {_id: TEST_FILE_ID, filename: TEST_FILE_NAME}) | ||
.run(); | ||
}); | ||
it("Should have file with _id '" + TEST_FILE_ID + "' in database", function () { | ||
var gfs = Grid(mongoose.connection.db); | ||
return expect(utils.fileExists(TEST_FILE_ID, gfs)).to.eventually.equal(true); | ||
}); | ||
}); | ||
describe("#removeFile", function () { | ||
it("should remove file successfully", function () { | ||
return task.removeFile({filename: TEST_FILE_NAME}) | ||
.run(); | ||
}); | ||
it("Should not have file with _id '" + TEST_FILE_ID + "' in database", function () { | ||
var gfs = Grid(mongoose.connection.db); | ||
return expect(utils.fileExists(TEST_FILE_ID, gfs)).to.eventually.equal(false); | ||
}); | ||
}); | ||
describe("allTogetherNow", function(){ | ||
@@ -222,2 +248,3 @@ it("should save, update and remove successfully", function(){ | ||
var gabe = new TestMdlB({name: "Gabe", age: 34}); | ||
var id = utils.generateId(); | ||
@@ -228,5 +255,7 @@ return expect( | ||
.update(gabe, {age: 64}) | ||
.saveFile(TEST_FILE_PATH, {_id: id, filename: {$ojFuture: "0.name"}}) | ||
.removeFile({_id: id}) | ||
.remove(TEST_COLLECTION_A, {name: "Gabe's Owner"}) | ||
.run()) | ||
.to.eventually.have.length(4); | ||
.to.eventually.have.length(6); | ||
}); | ||
@@ -237,3 +266,3 @@ }); | ||
it("task with templated data should run successfully", function () { | ||
var mickey = new TestMdlB({name: "Mickey Mouse", age: 53}); | ||
var mickey = new TestMdlB({name: "Mickey Mouse", age: 53, list: [{num: 53}]}); | ||
var mick = new TestMdlA({name: "Mick", age: 3}); | ||
@@ -244,3 +273,3 @@ | ||
.save(TEST_COLLECTION_A, {name: "Alfie", age: {$ojFuture: "1.age"}}) | ||
.save(TEST_COLLECTION_B, {name: "Minnie Mouse", age: {$ojFuture: "0.age"}}) | ||
.save(TEST_COLLECTION_B, {name: "Minnie Mouse", age: {$ojFuture: "0.list.0.num"}}) | ||
.update(TEST_COLLECTION_B, {name: {$ojFuture: "0.name"}}, {age: {$ojFuture: "1.age"}}) | ||
@@ -247,0 +276,0 @@ .update(TEST_COLLECTION_A, {name: {$ojFuture: "1.name"}}, {age: {$ojFuture: "3.age"}}) |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
60819
1463
410
3
3
+ Addedgridfs-stream@1.1.1
+ Addedflushwritable@1.0.0(transitive)
+ Addedgridfs-stream@1.1.1(transitive)