This is an automated email from the ASF dual-hosted git repository. jsinovassin pushed a commit to branch UNOMI-973-review-followups in repository https://gitbox.apache.org/repos/asf/unomi.git
commit c7bce26074cf50860f45b0fc8ce4a02715a341ad Author: jsinovassin <[email protected]> AuthorDate: Tue Sep 1 18:35:22 2026 +0200 UNOMI-973: Append moveFailed as an option, not to the directory name The router appends its own moveFailed to the configured source with an unconditional '&'. A source that carries no query has no separator to append to, so the option became part of the directory the endpoint names: file:///base turned into file:///base&moveFailed=.error, a directory called "base&moveFailed=.error". That was a malformed endpoint before this validation existed; with it, the two layers disagree. saveConfiguration validates the source as configured and answers 200, then the route builder validates the source with the option appended, refuses it and marks the configuration INVALID_ENDPOINT -- stored, answered 200, never run, which is what refusing at save time exists to prevent. The separator now depends on whether the source already carries a query. The two layers agree again in every case, since .error is relative and lands under the endpoint's own directory. --- .../router/core/route/ProfileImportFromSourceRouteBuilder.java | 4 +++- .../unomi/router/core/route/FileEndpointContainmentTest.java | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportFromSourceRouteBuilder.java b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportFromSourceRouteBuilder.java index 4c468a659..c226b7717 100644 --- a/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportFromSourceRouteBuilder.java +++ b/extensions/router/router-core/src/main/java/org/apache/unomi/router/core/route/ProfileImportFromSourceRouteBuilder.java @@ -94,7 +94,9 @@ public class ProfileImportFromSourceRouteBuilder extends RouterAbstractRouteBuil String endpoint = (String) importConfiguration.getProperties().get("source"); if (StringUtils.isNotBlank(endpoint)) { - endpoint += "&moveFailed=.error"; + // the separator depends on whether the source already carries a query: appending + // '&' to a source that has none makes the option part of the directory name + endpoint += (endpoint.indexOf('?') < 0 ? "?" : "&") + "moveFailed=.error"; } String refusal = EndpointValidator.validate(endpoint, allowedEndpoints, permittedBaseDirs); diff --git a/extensions/router/router-core/src/test/java/org/apache/unomi/router/core/route/FileEndpointContainmentTest.java b/extensions/router/router-core/src/test/java/org/apache/unomi/router/core/route/FileEndpointContainmentTest.java index 97ad266bc..85e22b368 100644 --- a/extensions/router/router-core/src/test/java/org/apache/unomi/router/core/route/FileEndpointContainmentTest.java +++ b/extensions/router/router-core/src/test/java/org/apache/unomi/router/core/route/FileEndpointContainmentTest.java @@ -165,6 +165,15 @@ public class FileEndpointContainmentTest { "a parent segment applies to the target of the link that precedes it, not to the link's own parent"); } + @Test + public void importRouteIsBuiltWhenSourceCarriesNoOption() throws Exception { + // the router appends moveFailed itself; a source with no query has no separator to append to, + // and the option would otherwise become part of the directory the endpoint names + addImportRoutes(recurrentImport("no-option", fileUri(permittedImportDir, ""))); + + assertRouteBuilt("no-option", "a source may name a directory and nothing else"); + } + @Test public void importRouteIsRefusedWhenSourceIsOutsidePermittedBaseDir() throws Exception { addImportRoutes(recurrentImport("arbitrary-dir", fileUri(arbitraryDir, "?fileName=profiles.csv")));
