diff --git a/openidm-script/src/test/java/org/forgerock/openidm/script/CustomEndpointFieldProjectionTest.java b/openidm-script/src/test/java/org/forgerock/openidm/script/CustomEndpointFieldProjectionTest.java new file mode 100644 index 0000000000..1b86803e92 --- /dev/null +++ b/openidm-script/src/test/java/org/forgerock/openidm/script/CustomEndpointFieldProjectionTest.java @@ -0,0 +1,111 @@ +/* + * The contents of this file are subject to the terms of the Common Development and + * Distribution License (the License). You may not use this file except in compliance with the + * License. + * + * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + * specific language governing permission and limitations under the License. + * + * When distributing Covered Software, include this CDDL Header Notice in each file and include + * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + * Header, with the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions copyright [year] [name of copyright owner]". + * + * Copyright 2026 3A Systems LLC. + */ + +package org.forgerock.openidm.script; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.forgerock.json.JsonValue.field; +import static org.forgerock.json.JsonValue.json; +import static org.forgerock.json.JsonValue.object; +import static org.forgerock.json.resource.Responses.newResourceResponse; + +import org.forgerock.json.JsonValue; +import org.forgerock.json.resource.AbstractRequestHandler; +import org.forgerock.json.resource.Connection; +import org.forgerock.json.resource.ReadRequest; +import org.forgerock.json.resource.Requests; +import org.forgerock.json.resource.ResourceException; +import org.forgerock.json.resource.ResourceResponse; +import org.forgerock.json.resource.Resources; +import org.forgerock.services.context.Context; +import org.forgerock.services.context.RootContext; +import org.forgerock.util.promise.Promise; +import org.testng.annotations.Test; + +/** + * Reproduces the custom-endpoint field-projection bug discussed in + * discussion #183. + * + *
A custom (scripted) endpoint returns the full object without explicitly setting the + * response fields (see {@code ScriptedRequestHandler.evaluate()} which calls + * {@code newResourceResponse(id, null, resultJson)} without {@code addField(...)}). As a + * consequence the generic CREST field projection in + * {@code org.forgerock.json.resource.Resources.filterResource(JsonValue, Collection)} is applied + * to the raw result using {@code request.getFields()}. + * + *
That generic projection flattens every requested {@link org.forgerock.json.JsonPointer} + * down to its {@code leaf()} name when building the filtered output. When two requested fields + * share the same leaf name on different nesting levels (e.g. {@code userName} and + * {@code manager/userName}), the nested one overwrites the top-level one, so the top-level + * {@code userName} ends up containing the manager's {@code userName}. + * + *
This test asserts the correct behaviour. It therefore fails against the buggy
+ * commons {@code json-resource} (3.1.1-SNAPSHOT) and is expected to pass once the generic
+ * projection is fixed to preserve the pointer structure instead of collapsing to the leaf name.
+ */
+public class CustomEndpointFieldProjectionTest {
+
+ private static final String USER_NAME = "bjensen";
+ private static final String MANAGER_USER_NAME = "jdoe";
+
+ /**
+ * A request handler that mimics a custom scripted endpoint: it returns the full object on
+ * read and does not set the response fields (no {@code addField(...)}), exactly like
+ * {@code ScriptedRequestHandler.evaluate()}.
+ */
+ private static final class FullObjectEndpoint extends AbstractRequestHandler {
+ @Override
+ public Promise