Skip to content

Commit 5c737ee

Browse files
authored
Merge pull request #9 from e-oj/encode_special_chars
Encode special chars
2 parents c03eed2 + 878f520 commit 5c737ee

File tree

4 files changed

+90
-88
lines changed

4 files changed

+90
-88
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
node_modules/
66

77
lib/tests.js
8+
test.js
89
.DS_Store

lib/task.js

+46-83
Original file line numberDiff line numberDiff line change
@@ -109,16 +109,13 @@ var Task = function(){
109109
if(!isObject(condition)) throw new Error("Invalid Condition");
110110
if(!isObject(data)) throw new Error("Invalid data");
111111

112-
handle$Token(condition);
113-
handle$Token(data);
114-
115112
var updateObj = {
116113
index: index
117114
, type: UPDATE
118115
, state: INITIAL
119116
, name: getModelName(model)
120-
, condition: condition
121-
, data: data
117+
, condition: xcode(condition)
118+
, data: xcode(data)
122119
};
123120

124121
steps.push(updateObj);
@@ -148,14 +145,12 @@ var Task = function(){
148145
if(!validModel(model)) throw new Error("Invalid Model");
149146
if(!isObject(doc)) throw new Error("Invalid doc");
150147

151-
handle$Token(doc);
152-
153148
var saveObj = {
154149
index: index
155150
, type: SAVE
156151
, state: INITIAL
157152
, name: getModelName(model)
158-
, data: doc
153+
, data: xcode(doc)
159154
};
160155

161156
steps.push(saveObj);
@@ -183,14 +178,12 @@ var Task = function(){
183178
if(!validModel(model)) throw new Error("Invalid Model");
184179
if(!isObject(condition)) throw new Error("Invalid Condition");
185180

186-
handle$Token(condition);
187-
188181
var removeObj = {
189182
index: index
190183
, type: REMOVE
191184
, state: INITIAL
192185
, name: getModelName(model)
193-
, condition: condition
186+
, condition: xcode(condition)
194187
};
195188

196189
steps.push(removeObj);
@@ -231,10 +224,7 @@ var Task = function(){
231224
*/
232225
task.saveFile = function (filePath, options) {
233226
if (!filePath) throw new Error("File path is required and must be a string");
234-
if (options) {
235-
if (!isObject(options)) throw new Error("options must be an object");
236-
handle$Token(options);
237-
}
227+
if (options && !isObject(options)) throw new Error("options must be an object");
238228

239229
var saveFileObj = {
240230
type: FILE_SAVE
@@ -243,7 +233,7 @@ var Task = function(){
243233
, data: {
244234
file_path: filePath
245235
}
246-
, options: options
236+
, options: xcode(options)
247237
};
248238

249239
steps.push(saveFileObj);
@@ -263,13 +253,11 @@ var Task = function(){
263253
task.removeFile = function (options) {
264254
if (!isObject(options)) throw new Error("options required. Must be an object");
265255

266-
handle$Token(options);
267-
268256
var removeFileObj = {
269257
type: FILE_REMOVE
270258
, index: index
271259
, state: INITIAL
272-
, options: options
260+
, options: xcode(options)
273261
};
274262

275263
steps.push(removeFileObj);
@@ -349,27 +337,23 @@ function getResolveFunc(step){
349337
*/
350338
function performUpdate(step, task, results) {
351339
var Collection = getModel(step.name);
340+
var condition = xcode(step.condition);
341+
var data = xcode(step.data);
352342

353-
handle$Token(step.condition, true);
354-
handle$Token(step.data, true);
343+
resolveFuture(condition, results);
344+
resolveFuture(data, results);
355345

356-
resolveFuture(step.condition, results);
357-
resolveFuture(step.data, results);
358-
359-
// console.log(update);
360-
return storeOldData(step)
361-
.then(function(){
362-
return updateState(task, step.index, PENDING)
363-
.then(function () {
364-
return Collection.update(step.condition, step.data, step.options)
365-
.exec()
366-
.then(function (result) {
367-
results.push(result);
346+
return storeOldData(step, condition).then(function () {
347+
return updateState(task, step.index, PENDING).then(function () {
348+
return Collection.update(condition, data, step.options)
349+
.exec()
350+
.then(function (result) {
351+
results.push(result);
368352

369-
return updateState(task, step.index, DONE, results);
370-
});
353+
return updateState(task, step.index, DONE, results);
371354
});
372355
});
356+
});
373357
}
374358

375359
/**
@@ -383,11 +367,11 @@ function performUpdate(step, task, results) {
383367
*/
384368
function performSave(step, task, results) {
385369
var Collection = getModel(step.name);
370+
var data = xcode(step.data);
386371

387-
handle$Token(step.data, true);
388-
resolveFuture(step.data, results);
372+
resolveFuture(data, results);
389373

390-
var doc = new Collection(step.data);
374+
var doc = new Collection(data);
391375
var dataStore = [];
392376

393377
doc._id = doc._id || utils.generateId();
@@ -416,13 +400,13 @@ function performSave(step, task, results) {
416400
*/
417401
function performRemove(step, task, results) {
418402
var Collection = getModel(step.name);
403+
var condition = xcode(step.condition);
419404

420-
handle$Token(step.condition, true);
421-
resolveFuture(step.condition, results);
405+
resolveFuture(condition, results);
422406

423-
return storeOldData(step).then(function () {
407+
return storeOldData(step, condition).then(function () {
424408
return updateState(task, step.index, PENDING).then(function () {
425-
return Collection.remove(step.condition).exec().then(function (result) {
409+
return Collection.remove(condition).exec().then(function (result) {
426410
results.push(result);
427411

428412
return updateState(task, step.index, DONE, results);
@@ -441,12 +425,11 @@ function performRemove(step, task, results) {
441425
* @returns {Promise|*}
442426
*/
443427
function performFileSave(step, task, results) {
444-
handle$Token(step.options, true);
445-
resolveFuture(step.options, results);
446-
447-
var options = step.options || {};
428+
var options = step.options ? xcode(step.options) : {};
448429
var dataStore = [];
449430

431+
resolveFuture(options, results);
432+
450433
options._id = options._id || utils.generateId();
451434
dataStore.push({_id: options._id});
452435

@@ -488,10 +471,9 @@ function performFileSave(step, task, results) {
488471
* @returns {Promise|*}
489472
*/
490473
function performFileRemove(step, task, results) {
491-
handle$Token(step.options, true);
492-
resolveFuture(step.options, results);
474+
var options = xcode(step.options);
493475

494-
var options = step.options;
476+
resolveFuture(options, results);
495477

496478
return storeOldFile(step).then(function (file) {
497479
return updateState(task, step.index, PENDING).then(function () {
@@ -527,8 +509,9 @@ function storeOldFile(step) {
527509
return new Promise(function (resolve, reject) {
528510
var dataStore = [];
529511
var gfs = Grid(mongoose.connection.db);
512+
var options = xcode(step.options);
530513

531-
gfs.findOne(step.options, function (err, file) {
514+
gfs.findOne(options, function (err, file) {
532515
if (err) return reject(err);
533516
if (!file) return resolve(false);
534517

@@ -552,13 +535,16 @@ function storeOldFile(step) {
552535
* changed by a step, for rollback purposes
553536
*
554537
* @param step the step
538+
* @param condition literal obj rep of the step's condition
555539
*
556540
* @returns {Promise|*}
557541
*/
558-
function storeOldData(step) {
542+
function storeOldData(step, condition) {
559543
var Collection = getModel(step.name);
560-
var options = step.options || step.type === REMOVE ? {multi: true} : null;
561-
var query = Collection.find(step.condition).lean();
544+
var options = step.options
545+
? xcode(step.options)
546+
: step.type === REMOVE ? {multi: true} : null;
547+
var query = Collection.find(condition).lean();
562548
var searchQuery = options && options.multi === true
563549
? query
564550
: query.limit(1);
@@ -621,38 +607,15 @@ function getModelName(model){
621607
}
622608

623609
/**
624-
* Handles the "$" token in queries because
625-
* MongoDB does not allow object keys to start with
626-
* "$"
610+
* For encoding user provided conditions, options, and data as
611+
* JSON strings for writes and decoding JSON strings to objects
612+
* before operations.
627613
*
628-
* @param obj the object in question
629-
* @param decode To decode or not to decode?
614+
* @param item a JSON string or an object
615+
* @returns object or JSON string
630616
*/
631-
function handle$Token(obj, decode) {
632-
var ESCAPE_PREFIX = constants.ESCAPE_PREFIX;
633-
var escapeLength = ESCAPE_PREFIX.length;
634-
var keys = Object.keys(obj);
635-
var key;
636-
var $key;
637-
638-
for(var i = 0; i < keys.length; i++){
639-
key = keys[i];
640-
641-
if(isObject(obj[key])){
642-
handle$Token(obj[key], decode);
643-
}
644-
645-
if (!decode && key[0] === "$") {
646-
obj[ESCAPE_PREFIX + key] = obj[key];
647-
delete obj[key];
648-
}
649-
650-
if (decode && key.length >= escapeLength && key.substring(0, escapeLength) === ESCAPE_PREFIX) {
651-
$key = key.replace(ESCAPE_PREFIX, "");
652-
obj[$key] = obj[key];
653-
delete obj[key];
654-
}
655-
}
617+
function xcode(item){
618+
return typeof item === "string" ? JSON.parse(item) : JSON.stringify(item);
656619
}
657620

658621
/**

tests/roller.tests.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,21 @@ module.exports = describe("Roller", function(){
2727
});
2828
});
2929

30-
it("should rollback update", function(){
31-
return task.save(TestMdlA, {name: "Tyrion Lannister", age: 34})
30+
it("should rollback update", function(done){
31+
task.save(TestMdlA, {name: "Tyrion Lannister", age: 34})
3232
.run()
3333
.then(function(){
34-
return task.update(TestMdlA, {name: "Tyrion Lannister"}, {name: "Jamie", $inc: {age: 1}})
34+
task.update(TestMdlA, {name: "Tyrion Lannister"}, {name: "Jamie", $inc: {age: 1}})
3535
.update(TestMdlA, {_id: "blah"}, {name: "fail"})
3636
.run()
3737
.then(failure)
3838
.catch(function(){
39-
return expect(TestMdlA.find({name: "Tyrion Lannister"}).exec()).to.eventually.have.length(1);
39+
TestMdlA.find({name: "Tyrion Lannister"}).exec()
40+
.then(function(results){
41+
expect(results).have.length(1);
42+
done();
43+
})
44+
.catch(done)
4045
});
4146
})
4247
});

tests/task.tests.js

+34-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ module.exports = describe("Task", function(){
120120
});
121121
});
122122

123-
describe("#update $ token", function(){
123+
describe("Special chars ('$' and '.')", function(){
124124
it("should update successfully with $gte", function(){
125125
return task.update(TestMdlB, {name: "Yo momma", age: {$gte: 38}}, {$inc: {age: 20}}).run();
126126
});
@@ -136,6 +136,39 @@ module.exports = describe("Task", function(){
136136
it("should have Yo momma in " + TEST_COLLECTION_B + " with age 38", function(){
137137
return expect(TestMdlB.find({name: "Yo momma", age: 38}).exec()).to.eventually.have.length(1);
138138
});
139+
140+
it("should update nested objects successfully", function(){
141+
var getHeroes = function(count){
142+
var heroes = [];
143+
144+
while(count--){
145+
heroes.push({hero: "The Squirrel fighter in Milan"});
146+
}
147+
148+
return heroes;
149+
};
150+
151+
return task.save(TestMdlA, {name: "Frank", age: 12, heroes: getHeroes(5)})
152+
.update(TestMdlA,
153+
{'heroes.hero': "The Squirrel fighter in Milan"},
154+
{$set: {"heroes.$.hero": "Captain Underpants"}})
155+
.run();
156+
});
157+
158+
it("Should have Captain Underpants in Heroes", function(done){
159+
TestMdlA.findOne({name: "Frank", age: 12}).lean()
160+
.exec()
161+
.then(function(result){
162+
expect(result.heroes[0].hero).to.equal("Captain Underpants");
163+
164+
TestMdlA.remove({name: "Frank", age: 12})
165+
.exec()
166+
.then(function(){
167+
done();
168+
});
169+
})
170+
.catch(done);
171+
});
139172
});
140173

141174
describe("#remove", function(){

0 commit comments

Comments
 (0)