mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 11:13:43 +08:00
fix(wiki): make built-in transformation starter pack visible in every workspace
This commit is contained in:
parent
64b5587f56
commit
07d6f01b56
@ -145,7 +145,10 @@ public class WikiTransformationExecutor {
|
||||
WikiTransformationRunEntity run = new WikiTransformationRunEntity();
|
||||
run.setTransformationId(transformation.getId());
|
||||
run.setKbId(page.getKbId());
|
||||
run.setWorkspaceId(transformation.getWorkspaceId());
|
||||
// Global templates have a null workspace_id; the run row requires one, so
|
||||
// fall back to the default workspace for bookkeeping (run visibility is
|
||||
// gated by the KB's workspace, not this field).
|
||||
run.setWorkspaceId(transformation.getWorkspaceId() != null ? transformation.getWorkspaceId() : 1L);
|
||||
run.setInputKind("page");
|
||||
run.setPageId(pageId);
|
||||
run.setStatus("running");
|
||||
@ -420,7 +423,10 @@ public class WikiTransformationExecutor {
|
||||
WikiTransformationRunEntity run = new WikiTransformationRunEntity();
|
||||
run.setTransformationId(transformation.getId());
|
||||
run.setKbId(raw.getKbId());
|
||||
run.setWorkspaceId(transformation.getWorkspaceId());
|
||||
// Global templates have a null workspace_id; the run row requires one, so
|
||||
// fall back to the default workspace for bookkeeping (run visibility is
|
||||
// gated by the KB's workspace, not this field).
|
||||
run.setWorkspaceId(transformation.getWorkspaceId() != null ? transformation.getWorkspaceId() : 1L);
|
||||
run.setInputKind("raw");
|
||||
run.setRawId(rawId);
|
||||
run.setStatus("running");
|
||||
|
||||
@ -29,7 +29,12 @@ public class WikiTransformationService {
|
||||
private final WikiTransformationMapper transformationMapper;
|
||||
private final WikiTransformationRunMapper runMapper;
|
||||
|
||||
/** Templates visible to a KB: pinned to this KB plus workspace-wide ones (kb_id NULL). */
|
||||
/**
|
||||
* Templates visible to a KB: pinned to this KB, plus workspace-wide ones —
|
||||
* either scoped to this workspace ({@code workspace_id = current}) or global
|
||||
* ({@code workspace_id IS NULL}, e.g. the built-in starter pack, visible to
|
||||
* every workspace).
|
||||
*/
|
||||
public List<WikiTransformationEntity> listForKb(Long kbId, Long workspaceId) {
|
||||
if (kbId == null) {
|
||||
return List.of();
|
||||
@ -38,14 +43,17 @@ public class WikiTransformationService {
|
||||
new LambdaQueryWrapper<WikiTransformationEntity>()
|
||||
.and(w -> w.eq(WikiTransformationEntity::getKbId, kbId)
|
||||
.or(g -> g.isNull(WikiTransformationEntity::getKbId)
|
||||
.eq(WikiTransformationEntity::getWorkspaceId, workspaceId)))
|
||||
.and(ws -> ws.eq(WikiTransformationEntity::getWorkspaceId, workspaceId)
|
||||
.or().isNull(WikiTransformationEntity::getWorkspaceId))))
|
||||
.orderByDesc(WikiTransformationEntity::getUpdateTime));
|
||||
}
|
||||
|
||||
public List<WikiTransformationEntity> listByWorkspace(Long workspaceId) {
|
||||
return transformationMapper.selectList(
|
||||
new LambdaQueryWrapper<WikiTransformationEntity>()
|
||||
.eq(WikiTransformationEntity::getWorkspaceId, workspaceId)
|
||||
// This workspace's own templates plus global ones (workspace_id NULL).
|
||||
.and(w -> w.eq(WikiTransformationEntity::getWorkspaceId, workspaceId)
|
||||
.or().isNull(WikiTransformationEntity::getWorkspaceId))
|
||||
.orderByDesc(WikiTransformationEntity::getUpdateTime));
|
||||
}
|
||||
|
||||
@ -65,7 +73,9 @@ public class WikiTransformationService {
|
||||
WikiTransformationEntity global = transformationMapper.selectOne(
|
||||
new LambdaQueryWrapper<WikiTransformationEntity>()
|
||||
.isNull(WikiTransformationEntity::getKbId)
|
||||
.eq(WikiTransformationEntity::getWorkspaceId, workspaceId)
|
||||
// This workspace's own template, or a global one (workspace_id NULL).
|
||||
.and(ws -> ws.eq(WikiTransformationEntity::getWorkspaceId, workspaceId)
|
||||
.or().isNull(WikiTransformationEntity::getWorkspaceId))
|
||||
.eq(WikiTransformationEntity::getName, name)
|
||||
.last("LIMIT 1"));
|
||||
return Optional.ofNullable(global);
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
-- V165: make the built-in starter-pack transformation templates global.
|
||||
-- V108 seeded the 7 templates with a hardcoded workspace_id = 1, so any
|
||||
-- workspace other than 1 saw an empty Transformations list. The fix marks them
|
||||
-- global by clearing workspace_id (NULL = global). The column was NOT NULL, so
|
||||
-- relax it first; listForKb / listByWorkspace / findByName treat NULL as global,
|
||||
-- and the access checks already allow templates whose workspace_id IS NULL.
|
||||
-- Targeted by fixed seed ids so real user templates are untouched.
|
||||
|
||||
ALTER TABLE mate_wiki_transformation ALTER COLUMN workspace_id SET NULL;
|
||||
|
||||
UPDATE mate_wiki_transformation
|
||||
SET workspace_id = NULL
|
||||
WHERE id IN (1000004001, 1000004002, 1000004003, 1000004004, 1000004005, 1000004006, 1000004007)
|
||||
AND workspace_id = 1;
|
||||
@ -0,0 +1,14 @@
|
||||
-- V165: make the built-in starter-pack transformation templates global.
|
||||
-- V108 seeded the 7 templates with a hardcoded workspace_id = 1, so any
|
||||
-- workspace other than 1 saw an empty Transformations list. The fix marks them
|
||||
-- global by clearing workspace_id (NULL = global). The column was NOT NULL, so
|
||||
-- relax it first; listForKb / listByWorkspace / findByName treat NULL as global,
|
||||
-- and the access checks already allow templates whose workspace_id IS NULL.
|
||||
-- Targeted by fixed seed ids so real user templates are untouched.
|
||||
|
||||
ALTER TABLE mate_wiki_transformation ALTER COLUMN workspace_id DROP NOT NULL;
|
||||
|
||||
UPDATE mate_wiki_transformation
|
||||
SET workspace_id = NULL
|
||||
WHERE id IN (1000004001, 1000004002, 1000004003, 1000004004, 1000004005, 1000004006, 1000004007)
|
||||
AND workspace_id = 1;
|
||||
@ -0,0 +1,14 @@
|
||||
-- V165: make the built-in starter-pack transformation templates global.
|
||||
-- V108 seeded the 7 templates with a hardcoded workspace_id = 1, so any
|
||||
-- workspace other than 1 saw an empty Transformations list. The fix marks them
|
||||
-- global by clearing workspace_id (NULL = global). The column was NOT NULL, so
|
||||
-- relax it first; listForKb / listByWorkspace / findByName treat NULL as global,
|
||||
-- and the access checks already allow templates whose workspace_id IS NULL.
|
||||
-- Targeted by fixed seed ids so real user templates are untouched.
|
||||
|
||||
ALTER TABLE mate_wiki_transformation MODIFY COLUMN workspace_id BIGINT NULL DEFAULT 1;
|
||||
|
||||
UPDATE mate_wiki_transformation
|
||||
SET workspace_id = NULL
|
||||
WHERE id IN (1000004001, 1000004002, 1000004003, 1000004004, 1000004005, 1000004006, 1000004007)
|
||||
AND workspace_id = 1;
|
||||
@ -0,0 +1,131 @@
|
||||
package vip.mate.wiki.service;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import vip.mate.wiki.model.WikiKnowledgeBaseEntity;
|
||||
import vip.mate.wiki.model.WikiTransformationEntity;
|
||||
import vip.mate.wiki.repository.WikiKnowledgeBaseMapper;
|
||||
import vip.mate.wiki.repository.WikiTransformationMapper;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Regression for the starter-pack visibility bug: the 7 built-in transformation
|
||||
* templates were seeded with a hardcoded {@code workspace_id = 1}, so any other
|
||||
* workspace saw an empty Transformations list. V165 clears their workspace_id
|
||||
* (NULL = global) and the queries treat NULL as visible everywhere — this test
|
||||
* boots H2 with the real Flyway migrations (V108 seed + V165 fix) and asserts
|
||||
* the templates show up regardless of workspace, while workspace-scoped
|
||||
* templates stay isolated.
|
||||
*/
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.NONE,
|
||||
properties = {
|
||||
"spring.flyway.enabled=true",
|
||||
"spring.flyway.locations=classpath:db/migration/h2",
|
||||
"mateclaw.feature-flag.refresh-ms=999999"
|
||||
}
|
||||
)
|
||||
class WikiTransformationStarterPackGlobalE2ETest {
|
||||
|
||||
private static final Set<String> STARTER_PACK = Set.of(
|
||||
"contract-risk-extract", "meeting-action-items", "customer-profile",
|
||||
"competitor-update", "resume-structured-extract", "incident-postmortem", "paper-imrad");
|
||||
|
||||
private static final AtomicLong SEQ = new AtomicLong(System.nanoTime());
|
||||
|
||||
@Autowired
|
||||
private WikiTransformationService service;
|
||||
@Autowired
|
||||
private WikiKnowledgeBaseMapper kbMapper;
|
||||
@Autowired
|
||||
private WikiTransformationMapper transformationMapper;
|
||||
|
||||
private long newKb(long workspaceId) {
|
||||
WikiKnowledgeBaseEntity kb = new WikiKnowledgeBaseEntity();
|
||||
long id = SEQ.incrementAndGet();
|
||||
kb.setId(id);
|
||||
kb.setName("kb-" + id);
|
||||
kb.setStatus("active");
|
||||
kb.setWorkspaceId(workspaceId);
|
||||
kb.setCreateTime(LocalDateTime.now());
|
||||
kb.setUpdateTime(LocalDateTime.now());
|
||||
kb.setDeleted(0);
|
||||
kbMapper.insert(kb);
|
||||
return id;
|
||||
}
|
||||
|
||||
private void insertWorkspaceTemplate(long workspaceId, String name) {
|
||||
WikiTransformationEntity t = new WikiTransformationEntity();
|
||||
t.setId(SEQ.incrementAndGet());
|
||||
t.setKbId(null); // workspace-wide
|
||||
t.setWorkspaceId(workspaceId); // but scoped to one workspace
|
||||
t.setName(name);
|
||||
t.setTitle(name);
|
||||
t.setPromptTemplate("do something");
|
||||
t.setEnabled(true);
|
||||
t.setCreateTime(LocalDateTime.now());
|
||||
t.setUpdateTime(LocalDateTime.now());
|
||||
t.setDeleted(0);
|
||||
transformationMapper.insert(t);
|
||||
}
|
||||
|
||||
private Set<String> visibleNames(long kbId, long workspaceId) {
|
||||
return service.listForKb(kbId, workspaceId).stream()
|
||||
.map(WikiTransformationEntity::getName)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Starter pack is visible from a non-default workspace (the bug)")
|
||||
void starterPackVisibleFromOtherWorkspace() {
|
||||
long kb = newKb(999L);
|
||||
Set<String> names = visibleNames(kb, 999L);
|
||||
for (String expected : STARTER_PACK) {
|
||||
assertTrue(names.contains(expected),
|
||||
"workspace 999 should see starter-pack template '" + expected + "', got: " + names);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Starter pack still visible from workspace 1 (no regression)")
|
||||
void starterPackStillVisibleFromWorkspaceOne() {
|
||||
long kb = newKb(1L);
|
||||
assertTrue(visibleNames(kb, 1L).containsAll(STARTER_PACK));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("A workspace-scoped template stays isolated; globals are seen by both")
|
||||
void workspaceScopedTemplateStaysIsolated() {
|
||||
String unique = "ws-only-" + SEQ.incrementAndGet();
|
||||
insertWorkspaceTemplate(777L, unique);
|
||||
|
||||
long kb777 = newKb(777L);
|
||||
long kb888 = newKb(888L);
|
||||
|
||||
assertTrue(visibleNames(kb777, 777L).contains(unique), "owner workspace should see its template");
|
||||
assertFalse(visibleNames(kb888, 888L).contains(unique), "other workspace must NOT see it");
|
||||
|
||||
// Global starter pack reaches both workspaces.
|
||||
assertTrue(visibleNames(kb777, 777L).containsAll(STARTER_PACK));
|
||||
assertTrue(visibleNames(kb888, 888L).containsAll(STARTER_PACK));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("listByWorkspace surfaces globals alongside the workspace's own")
|
||||
void listByWorkspaceIncludesGlobals() {
|
||||
List<WikiTransformationEntity> rows = service.listByWorkspace(424242L);
|
||||
Set<String> names = rows.stream().map(WikiTransformationEntity::getName).collect(Collectors.toSet());
|
||||
assertTrue(names.containsAll(STARTER_PACK),
|
||||
"listByWorkspace for an arbitrary workspace should still include the global starter pack");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user