Repository: scimmyjs/scimmy (js)

Issue:
email not working with attributes query anymore
Providing the attributes filter in the 1.3.0 version doesn't return the emails anymore.

GET `?attributes=userName,emails` will only return id and userName.


Pull request description:
Fix presence filtering of complex multi-value attributes in `SCIMMY.Types.SchemaDefinition`
Previously, it was possible to include the entire value of a complex multi-value attribute by specifying it in the positive attributes filter of a request (e.g. `?attributes=userName,emails`). Changes introduced in PR #56 to facilitate complex expressions in these filters unexpectedly removed this expected behaviour.

This change returns the `SCIMMY.Types.SchemaDefinition` coerce method to the expected behaviour (fixes #61).
New test fixtures have been added to ensure the behaviour is working as expected and is not unexpectedly removed again.

Patch:
diff --git a/src/lib/types/definition.js b/src/lib/types/definition.js
index 53f6bd6..e583461 100644
--- a/src/lib/types/definition.js
+++ b/src/lib/types/definition.js
@@ -417,11 +417,14 @@ export class SchemaDefinition {
                     // If the attribute is always returned, add it to the result
                     if (returned === "always") target[key] = data[key];
                     // Otherwise, if the attribute was requested and ~can~ be returned, process it
-                    else if (![false, "never"].includes(returned)) {
+                    else if (![false, "never"].includes(returned) && !exclusions.includes(key)) {
                         // If there's a filter for a complex attribute, evaluate it...
                         if (key in filter && type === "complex") {
-                            // ...either using Filter instance match method, or by recursing into this filter method to get specified attributes
-                            const value = Array.isArray(filter[key]) ? new Filter(filter[key].filter((expr) => Object.getPrototypeOf(expr).constructor === Object)).match(data[key]) : SchemaDefinition.#filter(definition, filter[key], data[key], key);
+                            const value = Array.isArray(filter[key]) ? !filter[key].some((expr) => typeof expr === "object") ? data[key] :
+                                // ...either using Filter instance match method for complex expressions...
+                                new Filter(filter[key].filter((expr) => Object.getPrototypeOf(expr).constructor === Object)).match(data[key]) :
+                                // ...or by recursing into this filter method to get specified attributes
+                                SchemaDefinition.#filter(definition, filter[key], data[key], key);
                             
                             // Only set the value if it isn't empty
                             if ((!multiValued && value !== undefined) || (Array.isArray(value) && value.length))


Test patch:
diff --git a/test/lib/types/definition.js b/test/lib/types/definition.js
index f061963..b6bbb42 100644
--- a/test/lib/types/definition.js
+++ b/test/lib/types/definition.js
@@ -594,8 +594,10 @@ describe("SCIMMY.Types.SchemaDefinition", () => {
         for (let [target, outcome, unexpected, expected, filter, multiValued] of [
             ["complex attributes", "filtered positively", "unexpectedly included", {test: {value: "False"}}, "test.value pr"],
             ["complex attributes", "filtered negatively", "unexpectedly excluded", {test: {name: "Test"}}, "test.value np"],
-            ["complex multi-value attributes", "filtered positively", "unexpectedly included", {test: [{name: "Test"}]}, "test.name pr", true],
-            ["complex multi-value attributes", "filtered negatively", "unexpectedly excluded", {test: [{value: "Test"}, {value: "False"}]}, "test.name np", true],
+            ["complex multi-value attributes", "filtered positively", "unexpectedly included", {test: [{name: "Test", value: "Test"}, {name: undefined, value: "False"}]}, "test pr", true],
+            ["complex multi-value attributes", "filtered negatively", "unexpectedly excluded", {}, "test np", true],
+            ["nested complex multi-value attributes", "filtered positively", "unexpectedly included", {test: [{name: "Test"}]}, "test.name pr", true],
+            ["nested complex multi-value attributes", "filtered negatively", "unexpectedly excluded", {test: [{value: "Test"}, {value: "False"}]}, "test.name np", true],
             ["complex multi-value attributes", "matched against filter expressions", "unexpectedly excluded", {test: [{name: "Test", value: "Test"}]}, "test[name eq \"Test\"] pr", true]
         ]) it(`should expect ${target} to be ${outcome}`, () => {
             const source = {test: multiValued ? [{name: "Test", value: "Test"}, {value: "False"}] : {name: "Test", value: "False"}};