From 338263fe32b2011661abf06344e17272924cbd68 Mon Sep 17 00:00:00 2001 From: delchev Date: Tue, 21 Jul 2026 12:56:22 +0300 Subject: [PATCH] fix(intent): keep the ADMINISTRATOR candidate-group default for .settings files that predate userTasks A settings document without a userTasks section now gets the same ADMINISTRATOR candidateGroupsExtra default that scaffolding writes - the deserialized POJO cannot distinguish an absent section from an explicitly empty one, so parse() checks the raw document for the member (a null member counts as absent). An explicit block - including an explicitly empty list, a deliberate opt-out - is kept verbatim. Without this, every module whose developer-owned .settings was scaffolded before the userTasks setting existed silently emitted user tasks with the authored role as the ONLY candidate group: the task existed in ACT_RU_TASK with a single candidate identity link, invisible to every administrator inbox, while the process showed the token parked at the task. Fixes #6338. Co-Authored-By: Claude Fable 5 --- .../intent/generator/IntentSettings.java | 34 +++++++++ .../intent/generator/IntentSettingsTest.java | 71 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 components/engine/engine-intent/src/test/java/org/eclipse/dirigible/components/intent/generator/IntentSettingsTest.java diff --git a/components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/generator/IntentSettings.java b/components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/generator/IntentSettings.java index 37a8ff21b8f..ddbdf8e5db7 100644 --- a/components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/generator/IntentSettings.java +++ b/components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/generator/IntentSettings.java @@ -21,6 +21,8 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; /** * The {@code .settings} document: the per-project recipe for turning the generated model @@ -121,9 +123,41 @@ public static IntentSettings parse(String json) { if (settings == null) { settings = new IntentSettings(); } + if (!hasUserTasksSection(json)) { + // A .settings scaffolded before the userTasks setting existed carries no such section - + // it must keep the documented ADMINISTRATOR default, exactly as scaffolding would have + // written it. Only an EXPLICIT userTasks block opts out (an empty candidateGroupsExtra + // is a deliberate "no extra groups"). Without this, every pre-existing settings file + // silently emitted single-group tasks that no administrator inbox could see. + if (settings.userTasks == null) { + settings.userTasks = new UserTasks(); + } + if (settings.userTasks.candidateGroupsExtra == null) { + settings.userTasks.candidateGroupsExtra = new ArrayList<>(); + } + settings.userTasks.candidateGroupsExtra.add("ADMINISTRATOR"); + } return settings; } + /** + * Whether the raw settings document declares a {@code userTasks} member at all (a {@code null} + * member counts as absent). The deserialized POJO cannot answer this - a missing section and an + * explicitly empty one both surface as an empty list. + */ + private static boolean hasUserTasksSection(String json) { + if (json == null || json.isBlank()) { + return false; + } + JsonElement root = JsonParser.parseString(json); + if (!root.isJsonObject()) { + return false; + } + JsonElement userTasks = root.getAsJsonObject() + .get("userTasks"); + return userTasks != null && !userTasks.isJsonNull(); + } + /** * Build the initial settings for a model: the default template recipes plus a {@code generate:true} * entry per discoverable trigger / resolver / form (so the developer sees the full editable list) diff --git a/components/engine/engine-intent/src/test/java/org/eclipse/dirigible/components/intent/generator/IntentSettingsTest.java b/components/engine/engine-intent/src/test/java/org/eclipse/dirigible/components/intent/generator/IntentSettingsTest.java new file mode 100644 index 00000000000..bbe6149a582 --- /dev/null +++ b/components/engine/engine-intent/src/test/java/org/eclipse/dirigible/components/intent/generator/IntentSettingsTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2010-2026 Eclipse Dirigible contributors + * + * All rights reserved. This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v20.html + * + * SPDX-FileCopyrightText: Eclipse Dirigible contributors SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.dirigible.components.intent.generator; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +/** + * The {@code userTasks.candidateGroupsExtra} default contract: a settings document that predates + * the section (or omits it) keeps the documented ADMINISTRATOR default, while an explicit block - + * including an explicitly empty list, a deliberate opt-out - means exactly what it says. Without + * the on-load default, every pre-existing {@code .settings} silently emitted single-group tasks + * invisible to administrators (GH-6338). + */ +class IntentSettingsTest { + + @Test + void aSettingsFileWithoutTheUserTasksSectionKeepsTheAdministratorDefault() { + IntentSettings settings = IntentSettings.parse(""" + { + "generation": {}, + "overrides": {} + } + """); + assertEquals(List.of("ADMINISTRATOR"), settings.candidateGroupsExtra()); + } + + @Test + void anEmptyDocumentKeepsTheAdministratorDefault() { + assertEquals(List.of("ADMINISTRATOR"), IntentSettings.parse("{}") + .candidateGroupsExtra()); + } + + @Test + void aNullUserTasksSectionCountsAsAbsent() { + assertEquals(List.of("ADMINISTRATOR"), IntentSettings.parse("{\"userTasks\": null}") + .candidateGroupsExtra()); + } + + @Test + void anExplicitlyEmptyListIsADeliberateOptOut() { + IntentSettings settings = IntentSettings.parse(""" + { + "userTasks": { "candidateGroupsExtra": [] } + } + """); + assertTrue(settings.candidateGroupsExtra() + .isEmpty()); + } + + @Test + void anExplicitListIsKeptVerbatim() { + IntentSettings settings = IntentSettings.parse(""" + { + "userTasks": { "candidateGroupsExtra": ["Supervisor"] } + } + """); + assertEquals(List.of("Supervisor"), settings.candidateGroupsExtra()); + } +}