Skip to content

Commit 338263f

Browse files
delchevclaude
andcommitted
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 <noreply@anthropic.com>
1 parent 948aeb1 commit 338263f

2 files changed

Lines changed: 105 additions & 0 deletions

File tree

components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/generator/IntentSettings.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
import com.google.gson.Gson;
2323
import com.google.gson.GsonBuilder;
24+
import com.google.gson.JsonElement;
25+
import com.google.gson.JsonParser;
2426

2527
/**
2628
* The {@code <intent>.settings} document: the per-project recipe for turning the generated model
@@ -121,9 +123,41 @@ public static IntentSettings parse(String json) {
121123
if (settings == null) {
122124
settings = new IntentSettings();
123125
}
126+
if (!hasUserTasksSection(json)) {
127+
// A .settings scaffolded before the userTasks setting existed carries no such section -
128+
// it must keep the documented ADMINISTRATOR default, exactly as scaffolding would have
129+
// written it. Only an EXPLICIT userTasks block opts out (an empty candidateGroupsExtra
130+
// is a deliberate "no extra groups"). Without this, every pre-existing settings file
131+
// silently emitted single-group tasks that no administrator inbox could see.
132+
if (settings.userTasks == null) {
133+
settings.userTasks = new UserTasks();
134+
}
135+
if (settings.userTasks.candidateGroupsExtra == null) {
136+
settings.userTasks.candidateGroupsExtra = new ArrayList<>();
137+
}
138+
settings.userTasks.candidateGroupsExtra.add("ADMINISTRATOR");
139+
}
124140
return settings;
125141
}
126142

143+
/**
144+
* Whether the raw settings document declares a {@code userTasks} member at all (a {@code null}
145+
* member counts as absent). The deserialized POJO cannot answer this - a missing section and an
146+
* explicitly empty one both surface as an empty list.
147+
*/
148+
private static boolean hasUserTasksSection(String json) {
149+
if (json == null || json.isBlank()) {
150+
return false;
151+
}
152+
JsonElement root = JsonParser.parseString(json);
153+
if (!root.isJsonObject()) {
154+
return false;
155+
}
156+
JsonElement userTasks = root.getAsJsonObject()
157+
.get("userTasks");
158+
return userTasks != null && !userTasks.isJsonNull();
159+
}
160+
127161
/**
128162
* Build the initial settings for a model: the default template recipes plus a {@code generate:true}
129163
* entry per discoverable trigger / resolver / form (so the developer sees the full editable list)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2010-2026 Eclipse Dirigible contributors
3+
*
4+
* All rights reserved. This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v20.html
7+
*
8+
* SPDX-FileCopyrightText: Eclipse Dirigible contributors SPDX-License-Identifier: EPL-2.0
9+
*/
10+
package org.eclipse.dirigible.components.intent.generator;
11+
12+
import static org.junit.jupiter.api.Assertions.assertEquals;
13+
import static org.junit.jupiter.api.Assertions.assertTrue;
14+
15+
import java.util.List;
16+
17+
import org.junit.jupiter.api.Test;
18+
19+
/**
20+
* The {@code userTasks.candidateGroupsExtra} default contract: a settings document that predates
21+
* the section (or omits it) keeps the documented ADMINISTRATOR default, while an explicit block -
22+
* including an explicitly empty list, a deliberate opt-out - means exactly what it says. Without
23+
* the on-load default, every pre-existing {@code .settings} silently emitted single-group tasks
24+
* invisible to administrators (GH-6338).
25+
*/
26+
class IntentSettingsTest {
27+
28+
@Test
29+
void aSettingsFileWithoutTheUserTasksSectionKeepsTheAdministratorDefault() {
30+
IntentSettings settings = IntentSettings.parse("""
31+
{
32+
"generation": {},
33+
"overrides": {}
34+
}
35+
""");
36+
assertEquals(List.of("ADMINISTRATOR"), settings.candidateGroupsExtra());
37+
}
38+
39+
@Test
40+
void anEmptyDocumentKeepsTheAdministratorDefault() {
41+
assertEquals(List.of("ADMINISTRATOR"), IntentSettings.parse("{}")
42+
.candidateGroupsExtra());
43+
}
44+
45+
@Test
46+
void aNullUserTasksSectionCountsAsAbsent() {
47+
assertEquals(List.of("ADMINISTRATOR"), IntentSettings.parse("{\"userTasks\": null}")
48+
.candidateGroupsExtra());
49+
}
50+
51+
@Test
52+
void anExplicitlyEmptyListIsADeliberateOptOut() {
53+
IntentSettings settings = IntentSettings.parse("""
54+
{
55+
"userTasks": { "candidateGroupsExtra": [] }
56+
}
57+
""");
58+
assertTrue(settings.candidateGroupsExtra()
59+
.isEmpty());
60+
}
61+
62+
@Test
63+
void anExplicitListIsKeptVerbatim() {
64+
IntentSettings settings = IntentSettings.parse("""
65+
{
66+
"userTasks": { "candidateGroupsExtra": ["Supervisor"] }
67+
}
68+
""");
69+
assertEquals(List.of("Supervisor"), settings.candidateGroupsExtra());
70+
}
71+
}

0 commit comments

Comments
 (0)