Skip to content

Commit

Permalink
Fixed undefined/null value must report MissingValue for properties
Browse files Browse the repository at this point in the history
This commit also marks the first use of Z (sanctuary-type-classes),
which should open up for many of the code reductions we already know
from Sanctuary.

Added one more test for Types with properties/keys, without a name, are
missing and the value is null/undefined.
  • Loading branch information
dotnetCarpenter committed Dec 25, 2021
1 parent 6d4f677 commit d404784
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 58 deletions.
117 changes: 80 additions & 37 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,13 @@
};
}

// map :: Functor f => (a -⁠> b) -⁠> f a -⁠> f b
// function map(f) {
// return function(xs) {
// return Z.map (f, xs);
// };
// }

// init :: Array a -> Array a
function init(xs) { return xs.slice (0, -1); }

Expand Down Expand Up @@ -1508,9 +1515,64 @@
//. which refers to the entire value.
function validate(t) {
return function(x) {
// 1. build list of prop, validate function and value object for all keys
// 2. when value = null, use MissingValue object
// 3. run all remainder validate functions for nested type validation
// 4. remove all Rights from list
// 5. concatenate $$ (in returnValue) with all Lefts

var returnValue = [];
var $$Result = t.validate ([]) (x);

// 1 and 2
// props :: Array (Either Object Object)
var props = t.keys.map (function(p) {
return x == null
? Left ({
error: 'MissingValue',
type: t.name || t.type,
name: p,
value: x
})
: Right ({
name: p,
type: t.types[p],
value: x[p]
});
});

var validateRights = Z.compose (function(p) {
if (p.result.isLeft) {
if (p.name in x) {
return Left ({
error: 'WrongValue',
// TODO: figure out what propPath really is
type: p.result.value.propPath.length > 0
? p.type.types[p.result.value.propPath[0]].name
: p.type.name,
name: p.name,
value: p.value
});
} else {
return Left ({
error: 'MissingValue',
type: p.type.name,
name: p.name,
value: p.value
});
}
} else {
return Right (p);
}
}, function(p) {
return {
name: p.name,
result: p.type.validate ([]) (p.value),
type: p.type,
value: p.value
};
});

returnValue.push (
$$Result.isLeft
? Left ({
Expand All @@ -1522,43 +1584,24 @@
: $$Result
);

if (x != null && t.keys.length > 0) {
var props = t.keys;

for (var i = 0, len = props.length; i < len; i += 1) {
var propName = props[i];
var propValue = x[propName];
var type = t.types[propName];
var result = type.validate ([]) (propValue);

if (result.isLeft) {
if (propName in x) {
returnValue.push (
Left ({
error: 'WrongValue',
// TODO: figure what propPath really is
type: result.value.propPath.length > 0
? type.types[result.value.propPath[0]].name
: type.name,
name: propName,
value: propValue
})
);
} else {
returnValue.push (
Left ({
error: 'MissingValue',
type: type.name,
name: propName,
value: propValue
})
);
}
}
}
}

return returnValue;
// 3
var tmp0 = Z.map (function(prop) {
return Z.chain (validateRights, prop);
}, props);

// 4
var tmp1 = Z.filter (function(either) {
return either.isLeft;
}, tmp0);

// 5
return Z.concat (returnValue, tmp1);
// return Z.concat (
// returnValue,
// Z.filter (
// either => either.isLeft,
// Z.map (prop => Z.map (validateRights, prop), props))
// );
};
}

Expand Down
57 changes: 36 additions & 21 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3898,42 +3898,50 @@ suite ('validate', () => {

// null is not a member of ‘FooBar’
eq ($.validate (FooBar) (null))
([Left ({'error': 'WrongValue', 'name': '$$', 'type': 'FooBar', 'value': null})]);
([
Left ({'error': 'WrongValue', 'name': '$$', 'type': 'FooBar', 'value': null}),
Left ({'error': 'MissingValue', 'name': 'bar', 'type': 'FooBar', 'value': null}),
Left ({'error': 'MissingValue', 'name': 'foo', 'type': 'FooBar', 'value': null}),
]);

// undefined is not a member of ‘FooBar’
eq ($.validate (FooBar) (undefined))
([Left ({'error': 'WrongValue', 'name': '$$', 'type': 'FooBar', 'value': undefined})]);
([
Left ({'error': 'WrongValue', 'name': '$$', 'type': 'FooBar', 'value': undefined}),
Left ({'error': 'MissingValue', 'name': 'bar', 'type': 'FooBar', 'value': undefined}),
Left ({'error': 'MissingValue', 'name': 'foo', 'type': 'FooBar', 'value': undefined}),
]);

// ''bar' field is missing', ''foo' field is missing'
eq ($.validate (FooBar) ({}))
([
Left ({'error': 'WrongValue', 'name': '$$', 'type': 'FooBar', 'value': {}}),
Left ({'error': 'MissingValue', 'name': 'bar', 'type': 'Number', 'value': undefined}),
Left ({'error': 'MissingValue', 'name': 'foo', 'type': 'String', 'value': undefined}),
]);
([
Left ({'error': 'WrongValue', 'name': '$$', 'type': 'FooBar', 'value': {}}),
Left ({'error': 'MissingValue', 'name': 'bar', 'type': 'Number', 'value': undefined}),
Left ({'error': 'MissingValue', 'name': 'foo', 'type': 'String', 'value': undefined}),
]);

// 'bar' field is missing
eq ($.validate (FooBar) ({foo: null}))
([
Left ({'error': 'WrongValue', 'name': '$$', 'type': 'FooBar', 'value': {'foo': null}}),
Left ({'error': 'MissingValue', 'name': 'bar', 'type': 'Number', 'value': undefined}),
Left ({'error': 'WrongValue', 'name': 'foo', 'type': 'String', 'value': null}),
]);
([
Left ({'error': 'WrongValue', 'name': '$$', 'type': 'FooBar', 'value': {'foo': null}}),
Left ({'error': 'MissingValue', 'name': 'bar', 'type': 'Number', 'value': undefined}),
Left ({'error': 'WrongValue', 'name': 'foo', 'type': 'String', 'value': null}),
]);

// Value of 'bar' field, null, is not a member of ‘Number’
eq ($.validate (FooBar) ({foo: null, bar: null}))
([
Left ({'error': 'WrongValue', 'name': '$$', 'type': 'FooBar', 'value': {'bar': null, 'foo': null}}),
Left ({'error': 'WrongValue', 'name': 'bar', 'type': 'Number', 'value': null}),
Left ({'error': 'WrongValue', 'name': 'foo', 'type': 'String', 'value': null}),
]);
([
Left ({'error': 'WrongValue', 'name': '$$', 'type': 'FooBar', 'value': {'bar': null, 'foo': null}}),
Left ({'error': 'WrongValue', 'name': 'bar', 'type': 'Number', 'value': null}),
Left ({'error': 'WrongValue', 'name': 'foo', 'type': 'String', 'value': null}),
]);

// Value of 'foo' field, null, is not a member of ‘String’
eq ($.validate (FooBar) ({foo: null, bar: 42}))
([
Left ({'error': 'WrongValue', 'name': '$$', 'type': 'FooBar', 'value': {'bar': 42, 'foo': null}}),
Left ({'error': 'WrongValue', 'name': 'foo', 'type': 'String', 'value': null}),
]);
([
Left ({'error': 'WrongValue', 'name': '$$', 'type': 'FooBar', 'value': {'bar': 42, 'foo': null}}),
Left ({'error': 'WrongValue', 'name': 'foo', 'type': 'String', 'value': null}),
]);

eq ($.validate (FooBar) ({foo: 'blue', bar: 42}))
([Right ({foo: 'blue', bar: 42})]);
Expand Down Expand Up @@ -3968,6 +3976,13 @@ suite ('validate', () => {
Left ({'error': 'WrongValue', 'name': 'date', 'type': 'DateIso', 'value': '2020-04-100'}),
]);

eq ($.validate (model2) (undefined))
([
Left ({'error': 'WrongValue', 'name': '$$', 'type': 'RECORD', 'value': undefined}),
Left ({'error': 'MissingValue', 'name': 'date', 'type': 'RECORD', 'value': undefined}),
Left ({'error': 'MissingValue', 'name': 'bool', 'type': 'RECORD', 'value': undefined}),
]);

eq ($.validate (model2) ({bool: 'foobar', date: '2020-04-100'}))
([
Left ({'error': 'WrongValue', 'name': '$$', 'type': 'RECORD', 'value': {'bool': 'foobar', 'date': '2020-04-100'}}),
Expand Down

0 comments on commit d404784

Please sign in to comment.