Skip to content

Commit

Permalink
Merge pull request #36 from scimmyjs/issue/34-filter-null-value
Browse files Browse the repository at this point in the history
Treat null as undefined in `match` method of `SCIMMY.Types.Filter` class
  • Loading branch information
sleelin authored Jul 24, 2024
2 parents a8fa579 + ecd44c6 commit 2efaec8
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 20 deletions.
6 changes: 3 additions & 3 deletions src/lib/messages/patchop.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,9 @@ export class PatchOp {
// Get rid of any empty values from the filter
.filter(([, value]) => value !== undefined)
// Turn it into an equity filter string
.map(([key, value]) => (`${key} eq ${value}`)).join(" and "))
.join(" or ")
)
.map(([key, value]) => (`${key} eq ${typeof value === "string" ? `"${value}"` : value}`))
// Join all comparisons into one logical expression
.join(" and ")).join(" or "))
// Get any matching values from the filter
.match(target[property])
));
Expand Down
11 changes: 6 additions & 5 deletions src/lib/types/filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const comparators = ["eq", "ne", "co", "sw", "ew", "gt", "lt", "ge", "le", "pr",
// Regular expressions that represent filter syntax
const lexicon = [
// White Space, Number Values
/(\s+)/, /([-+]?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?)/,
/(\s+)/, /([-+]?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?)(?![\w+-])/,
// Boolean Values, Empty Values, String Values
/(false|true)+/, /(null)+/, /("(?:[^"]|\\.|\n)*")/,
// Logical Groups, Complex Attribute Value Filters
Expand Down Expand Up @@ -311,20 +311,21 @@ export class Filter extends Array {
const negate = (expression[0].toLowerCase() === "not");
let [comparator, expected] = expression.slice(((+negate) - expression.length));

// Cast true and false strings to boolean values
expected = (expected === "false" ? false : (expected === "true" ? true : expected));
// For equality tests, cast true and false strings to boolean values, maintaining EntraID support
if (["eq", "ne"].includes(comparator.toLowerCase()) && typeof actual === "boolean" && typeof expected === "string")
expected = (expected.toLowerCase() === "false" ? false : (expected.toLowerCase() === "true" ? true : expected));

switch (comparator.toLowerCase()) {
default:
result = false;
break;

case "eq":
result = (actual === expected);
result = (actual === (expected ?? undefined));
break;

case "ne":
result = (actual !== expected);
result = (actual !== (expected ?? undefined));
break;

case "co":
Expand Down
8 changes: 7 additions & 1 deletion test/lib/messages/patchop.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ const TestSchema = createSchemaClass({
new Attribute("string", "nickName"), new Attribute("string", "password", {direction: "in", returned: false}),
new Attribute("complex", "name", {}, [new Attribute("string", "formatted"), new Attribute("string", "honorificPrefix")]),
new Attribute("complex", "emails", {multiValued: true}, [new Attribute("string", "value"), new Attribute("string", "type")]),
new Attribute("string", "throws"), new Attribute("dateTime", "date")
new Attribute("string", "throws"), new Attribute("dateTime", "date"),
new Attribute("complex", "members", {multiValued: true, uniqueness: false}, [
new Attribute("string", "value", {mutable: "immutable"}),
new Attribute("string", "display", {mutable: "immutable"}),
new Attribute("reference", "$ref", {mutable: "immutable", referenceTypes: ["User", "Group"]}),
new Attribute("string", "type", {mutable: "immutable", canonicalValues: ["User", "Group"]})
])
]
});

Expand Down
78 changes: 69 additions & 9 deletions test/lib/messages/patchop.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,55 +22,115 @@
"ops": [
{"op": "add", "path": "name", "value": {"formatted": "Test"}}
]
},
{
"source": {"id": "1234", "userName": "asdf", "members": [{"value": "f648f8d5ea4e4cd38e9c"}, {"value": "123abc"}]},
"target": {"id": "1234", "userName": "asdf", "members": [{"value": "f648f8d5ea4e4cd38e9c"}, {"value": "123abc", "$ref": "User"}]},
"ops": [
{"op": "add", "path": "members[value eq 123abc].$ref", "value": "User"}
]
}
],
"remove": [
{
"source": {"id": "1234", "userName": "asdf", "name": {"honorificPrefix": "Mr"}},
"target": {"id": "1234", "userName": "asdf"},
"ops": [{"op": "remove", "path": "name"}]
"ops": [
{"op": "remove", "path": "name"}
]
},
{
"source": {"id": "1234", "userName": "asdf", "emails": [{"type": "work", "value": "[email protected]"}, {"type": "home", "value": "[email protected]"}]},
"target": {"id": "1234", "userName": "asdf", "emails": [{"type": "work", "value": "[email protected]"}]},
"ops": [{"op": "remove", "path": "emails", "value": {"type": "home"}}]
"ops": [
{"op": "remove", "path": "emails", "value": {"type": "home"}}
]
},
{
"source": {"id": "1234", "userName": "asdf", "emails": [{"type": "work", "value": "[email protected]"}]},
"target": {"id": "1234", "userName": "asdf"},
"ops": [{"op": "remove", "path": "emails[type eq \"work\"]"}]
"ops": [
{"op": "remove", "path": "emails[type eq \"work\"]"}
]
},
{
"source": {"id": "1234", "userName": "asdf", "members": [{"value": "f648f8d5ea4e4cd38e9c"}, {"$ref": "User", "value": "f648f8d5ea4e4cd38e9c"}]},
"target": {"id": "1234", "userName": "asdf", "members": [{"$ref": "User", "value": "f648f8d5ea4e4cd38e9c"}]},
"ops": [
{"op": "remove", "path": "members", "value": [{"$ref": null, "value": "f648f8d5ea4e4cd38e9c"}]}
]
},
{
"source": {"id": "1234", "userName": "asdf", "members": [{"value": "f648f8d5ea4e4cd38e9c"}, {"$ref": "User", "value": "f648f8d5ea4e4cd38e9c"}]},
"target": {"id": "1234", "userName": "asdf", "members": [{"$ref": "User", "value": "f648f8d5ea4e4cd38e9c"}]},
"ops": [
{"op": "remove", "path": "members[$ref eq null and value eq \"f648f8d5ea4e4cd38e9c\"]"}
]
},
{
"source": {"id": "1234", "userName": "asdf", "members": [{"value": "f648f8d5ea4e4cd38e9c"}, {"value": "123abc"}]},
"target": {"id": "1234", "userName": "asdf", "members": [{"value": "f648f8d5ea4e4cd38e9c"}]},
"ops": [
{"op": "remove", "path": "members", "value": [{"value": "123abc"}]}
]
},
{
"source": {"id": "1234", "userName": "asdf", "members": [{"value": "f648f8d5ea4e4cd38e9c"}, {"value": "123abc"}]},
"target": {"id": "1234", "userName": "asdf", "members": [{"value": "f648f8d5ea4e4cd38e9c"}]},
"ops": [
{"op": "remove", "path": "members[value eq \"123abc\"]"}
]
},
{
"source": {"id": "1234", "userName": "asdf", "members": [{"value": "f648f8d5ea4e4cd38e9c"}, {"value": "123abc"}]},
"target": {"id": "1234", "userName": "asdf", "members": [{"value": "f648f8d5ea4e4cd38e9c"}]},
"ops": [
{"op": "Remove", "path": "members[value eq 123abc]"}
]
}
],
"replace": [
{
"source": {"id": "1234", "userName": "asdf", "name": {"honorificPrefix": "Mr"}},
"target": {"id": "1234", "userName": "ghjk", "name": {"honorificPrefix": "Mr"}},
"ops": [{"op": "replace", "path": "userName", "value": "ghjk"}]
"ops": [
{"op": "replace", "path": "userName", "value": "ghjk"}
]
},
{
"source": {"id": "1234", "userName": "asdf", "name": {"honorificPrefix": "Mr"}},
"target": {"id": "1234", "userName": "asdf", "name": {"formatted": "Test"}},
"ops": [{"op": "replace", "path": "name", "value": {"formatted": "Test"}}]
"ops": [
{"op": "replace", "path": "name", "value": {"formatted": "Test"}}
]
},
{
"source": {"id": "1234", "userName": "asdf", "emails": [{"type": "home", "value": "[email protected]"}]},
"target": {"id": "1234", "userName": "asdf", "emails": [{"type": "work", "value": "[email protected]"}]},
"ops": [{"op": "replace", "path": "emails", "value": {"type": "work", "value": "[email protected]"}}]
"ops": [
{"op": "replace", "path": "emails", "value": {"type": "work", "value": "[email protected]"}}
]
},
{
"source": {"id": "1234", "userName": "asdf", "emails": [{"type": "home", "value": "[email protected]"}]},
"target": {"id": "1234", "userName": "asdf", "emails": [{"type": "home", "value": "[email protected]"}, {"type": "work", "value": "[email protected]"}]},
"ops": [{"op": "replace", "path": "emails[type eq \"work\"]", "value": {"type": "work", "value": "[email protected]"}}]
"ops": [
{"op": "replace", "path": "emails[type eq \"work\"]", "value": {"type": "work", "value": "[email protected]"}}
]
},
{
"source": {"id": "1234", "userName": "asdf", "emails": [{"type": "work", "value": "[email protected]"}]},
"target": {"id": "1234", "userName": "asdf", "emails": [{"type": "work", "value": "[email protected]"}]},
"ops": [{"op": "replace", "path": "emails[type eq \"work\"]", "value": {"type": "work", "value": "[email protected]"}}]
"ops": [
{"op": "replace", "path": "emails[type eq \"work\"]", "value": {"type": "work", "value": "[email protected]"}}
]
},
{
"source": {"id": "1234", "userName": "asdf", "emails": [{"type": "work", "value": "[email protected]"}]},
"target": {"id": "1234", "userName": "asdf", "emails": [{"type": "work", "value": "[email protected]"}]},
"ops": [{"op": "replace", "path": "emails[type eq \"work\"].value", "value": "[email protected]"}]
"ops": [
{"op": "replace", "path": "emails[type eq \"work\"].value", "value": "[email protected]"}
]
}
]
}
Expand Down
13 changes: 11 additions & 2 deletions test/lib/types/filter.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@
{"source": "displayName co \"Bob\"", "target": [{"displayName": ["co", "Bob"]}]},
{"source": "name.formatted sw \"Bob\"", "target": [{"name": {"formatted": ["sw", "Bob"]}}]},
{"source": "quota gt 1.5", "target": [{"quota": ["gt", 1.5]}]},
{"source": "quota gt 2e2", "target": [{"quota": ["gt", 2e2]}]},
{"source": "UserType eq null", "target": [{"userType": ["eq", null]}]},
{"source": "$ref eq null", "target": [{"$ref": ["eq", null]}]},
{"source": "valid$Name eq null", "target": [{"valid$Name": ["eq", null]}]},
{"source": "-valid$Name eq -null", "target": [{"-valid$Name": ["eq", "-null"]}]},
{"source": "active eq false", "target": [{"active": ["eq", false]}]},
{"source": "emails.primary eq true", "target": [{"emails": {"primary": ["eq", true]}}]}
{"source": "emails.primary eq true", "target": [{"emails": {"primary": ["eq", true]}}]},
{"source": "value eq 123abc", "target": [{"value": ["eq", "123abc"]}]},
{"source": "value eq 123", "target": [{"value": ["eq", 123]}]},
{"source": "value eq 123abc5e4", "target": [{"value": ["eq", "123abc5e4"]}]}
],
"logical": [
{"source": "not eq pr", "target": [{"eq": ["not", "pr"]}]},
Expand Down Expand Up @@ -287,7 +291,12 @@
{"expression": {"userName": ["pr"]}, "expected": [1, 2, 3, 4]},
{"expression": {"exists": ["pr"]}, "expected": [1, 3]},
{"expression": {"userName": ["np"]}, "expected": []},
{"expression": {"exists": ["np"]}, "expected": [2, 4]}
{"expression": {"exists": ["np"]}, "expected": [2, 4]},
{"expression": {"exists": ["eq", null]}, "expected": [2, 4]},
{"expression": {"exists": ["ne", null]}, "expected": [1, 3]},
{"expression": {"exists": ["eq", true]}, "expected": [1]},
{"expression": {"exists": ["eq", "True"]}, "expected": [1]},
{"expression": {"exists": ["eq", "False"]}, "expected": [3]}
],
"nesting": [
{"expression": {"name": {"formatted": ["co", "a"]}}, "expected": [1, 2, 4]},
Expand Down

0 comments on commit 2efaec8

Please sign in to comment.