Skip to content

Commit

Permalink
add any.strip for objects and arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
nlf committed Feb 28, 2015
1 parent ca2eea8 commit c2841eb
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 26 deletions.
24 changes: 23 additions & 1 deletion README.md
Expand Up @@ -27,6 +27,7 @@ Lead Maintainer: [Nicolas Morel](https://github.com/marsup)
- [`any.required()`](#anyrequired)
- [`any.optional()`](#anyoptional)
- [`any.forbidden()`](#anyforbidden)
- [`any.strip()`](#anystrip)
- [`any.description(desc)`](#anydescriptiondesc)
- [`any.notes(notes)`](#anynotesnotes)
- [`any.tags(tags)`](#anytagstags)
Expand Down Expand Up @@ -339,6 +340,27 @@ var schema = {
};
```

#### `any.strip()`

Marks a key to be removed from a resulting object or array after validation. Used to sanitize output.

```javascript
var schema = {
username: Joi.string(),
password: Joi.string().strip()
};

schema.validate({ username: 'test', password: 'hunter2' }, function (err, value) {
// value = { username: 'test' }
});

var schema = Joi.array().items(Joi.string(), Joi.any().strip());

schema.validate(['one', 'two', true, false, 1, 2], function (err, value) {
// value = ['one', 'two']
});
```

#### `any.description(desc)`

Annotates the key where:
Expand Down Expand Up @@ -1284,4 +1306,4 @@ var schema = Joi.object().keys({
});

Joi.validate({ a: 5, b: { c: 5 } }, schema, { context: { x: 5 } }, function (err, value) {});
```
```
56 changes: 34 additions & 22 deletions lib/any.js
Expand Up @@ -21,7 +21,8 @@ internals.defaults = {
stripUnknown: false,
language: {},
presence: 'optional',
raw: false
raw: false,
strip: false
// context: null
};

Expand All @@ -36,7 +37,8 @@ internals.checkOptions = function (options) {
language: 'object',
presence: ['string', 'required', 'optional', 'forbidden', 'ignore'],
raw: 'boolean',
context: 'object'
context: 'object',
strip: 'boolean'
};

var keys = Object.keys(options);
Expand Down Expand Up @@ -295,6 +297,14 @@ internals.Any.prototype.forbidden = function () {
};


internals.Any.prototype.strip = function () {

var obj = this.clone();
obj._flags.strip = true;
return obj;
};


internals.Any.prototype.applyFunctionToChildren = function (children, fn, args, root) {

children = [].concat(children);
Expand Down Expand Up @@ -450,31 +460,33 @@ internals.Any.prototype._validate = function (value, state, options, reference)

var finalValue;

if (value !== undefined) {
finalValue = options.raw ? originalValue : value;
}
else if (Ref.isRef(self._flags.default)) {
finalValue = self._flags.default(state.parent, options);
}
else if (typeof self._flags.default === 'function' &&
!(self._type === 'func' && !self._flags.default.description)) {
if (!self._flags.strip) {
if (value !== undefined) {
finalValue = options.raw ? originalValue : value;
}
else if (Ref.isRef(self._flags.default)) {
finalValue = self._flags.default(state.parent, options);
}
else if (typeof self._flags.default === 'function' &&
!(self._type === 'func' && !self._flags.default.description)) {

var arg;
var arg;

if (state.parent !== null &&
self._flags.default.length > 0) {
if (state.parent !== null &&
self._flags.default.length > 0) {

arg = Hoek.clone(state.parent);
}
arg = Hoek.clone(state.parent);
}

var defaultValue = internals._try(self._flags.default, arg);
finalValue = defaultValue.value;
if (defaultValue.error) {
errors.push(Errors.create('any.default', defaultValue.error, state, options));
var defaultValue = internals._try(self._flags.default, arg);
finalValue = defaultValue.value;
if (defaultValue.error) {
errors.push(Errors.create('any.default', defaultValue.error, state, options));
}
}
else {
finalValue = self._flags.default;
}
}
else {
finalValue = self._flags.default;
}

return {
Expand Down
24 changes: 22 additions & 2 deletions lib/array.js
Expand Up @@ -11,6 +11,19 @@ var Hoek = require('hoek');
var internals = {};


internals.fastSplice = function (arr, i) {

var il = arr.length;
var pos = i;

while (pos < il) {
arr[pos++] = arr[pos];
}

--arr.length;
};


internals.Array = function () {

Any.call(this);
Expand Down Expand Up @@ -139,7 +152,7 @@ internals.checkItems = function (items, wasArray, state, options) {
items[v] = res.value;
isValid = true;
inclusions.push(requireds[i]);
requireds.splice(i, 1);
internals.fastSplice(requireds, i);
--i;
--il;
break;
Expand All @@ -155,7 +168,14 @@ internals.checkItems = function (items, wasArray, state, options) {
for (i = 0, il = inclusions.length; i < il; ++i) {
var res = inclusions[i]._validate(item, localState, options);
if (!res.errors) {
items[v] = res.value;
if (inclusions[i]._flags.strip) {
internals.fastSplice(items, v);
--v;
--vl;
}
else {
items[v] = res.value;
}
isValid = true;
break;
}
Expand Down
5 changes: 4 additions & 1 deletion lib/object.js
Expand Up @@ -157,7 +157,10 @@ internals.Object.prototype._base = function (value, state, options) {
}
}

if (result.value !== undefined) {
if (child.schema._flags.strip) {
delete target[key];
}
else if (result.value !== undefined) {
target[key] = result.value;
}
}
Expand Down
27 changes: 27 additions & 0 deletions test/any.js
Expand Up @@ -524,6 +524,33 @@ describe('any', function () {
});
});

describe('#strip', function () {

it('validates and returns undefined', function (done) {

var schema = Joi.string().strip();

schema.validate('test', function (err, value) {

expect(err).to.not.exist();
expect(value).to.not.exist();
done();
});
});

it('validates and returns an error', function (done) {

var schema = Joi.string().strip();

schema.validate(1, function (err, value) {

expect(err).to.exist();
expect(err.message).to.equal('"value" must be a string');
done();
});
});
});

describe('#description', function () {

it('sets the description', function (done) {
Expand Down
11 changes: 11 additions & 0 deletions test/array.js
Expand Up @@ -249,6 +249,17 @@ describe('array', function () {
done();
});
});

it('can strip matching items', function (done) {

var schema = Joi.array().items(Joi.string(), Joi.any().strip());
schema.validate(['one', 'two', 3, 4], function (err, value) {

expect(err).to.not.exist();
expect(value).to.deep.equal(['one', 'two']);
done();
});
});
});

describe('#min', function () {
Expand Down
38 changes: 38 additions & 0 deletions test/object.js
Expand Up @@ -383,6 +383,44 @@ describe('object', function () {
});
});
});

it('strips keys flagged with strip', function (done) {

var schema = Joi.object({
a: Joi.string().strip(),
b: Joi.string()
});
schema.validate({ a: 'test', b: 'test' }, function (err, value) {

expect(err).to.not.exist();
expect(value.a).to.not.exist();
expect(value.b).to.equal('test');
done();
});
});

it('does not alter the original object when stripping keys', function (done) {

var schema = Joi.object({
a: Joi.string().strip(),
b: Joi.string()
});

var valid = {
a: 'test',
b: 'test'
};

schema.validate(valid, function (err, value) {

expect(err).to.not.exist();
expect(value.a).to.not.exist();
expect(valid.a).to.equal('test');
expect(value.b).to.equal('test');
expect(valid.b).to.equal('test');
done();
});
});
});

describe('#unknown', function () {
Expand Down

0 comments on commit c2841eb

Please sign in to comment.