mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 03:55:09 +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();
|
WikiTransformationRunEntity run = new WikiTransformationRunEntity();
|
||||||
run.setTransformationId(transformation.getId());
|
run.setTransformationId(transformation.getId());
|
||||||
run.setKbId(page.getKbId());
|
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.setInputKind("page");
|
||||||
run.setPageId(pageId);
|
run.setPageId(pageId);
|
||||||
run.setStatus("running");
|
run.setStatus("running");
|
||||||
@ -420,7 +423,10 @@ public class WikiTransformationExecutor {
|
|||||||
WikiTransformationRunEntity run = new WikiTransformationRunEntity();
|
WikiTransformationRunEntity run = new WikiTransformationRunEntity();
|
||||||
run.setTransformationId(transformation.getId());
|
run.setTransformationId(transformation.getId());
|
||||||
run.setKbId(raw.getKbId());
|
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.setInputKind("raw");
|
||||||
run.setRawId(rawId);
|
run.setRawId(rawId);
|
||||||
run.setStatus("running");
|
run.setStatus("running");
|
||||||
|
|||||||
@ -29,7 +29,12 @@ public class WikiTransformationService {
|
|||||||
private final WikiTransformationMapper transformationMapper;
|
private final WikiTransformationMapper transformationMapper;
|
||||||
private final WikiTransformationRunMapper runMapper;
|
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) {
|
public List<WikiTransformationEntity> listForKb(Long kbId, Long workspaceId) {
|
||||||
if (kbId == null) {
|
if (kbId == null) {
|
||||||
return List.of();
|
return List.of();
|
||||||
@ -38,14 +43,17 @@ public class WikiTransformationService {
|
|||||||
new LambdaQueryWrapper<WikiTransformationEntity>()
|
new LambdaQueryWrapper<WikiTransformationEntity>()
|
||||||
.and(w -> w.eq(WikiTransformationEntity::getKbId, kbId)
|
.and(w -> w.eq(WikiTransformationEntity::getKbId, kbId)
|
||||||
.or(g -> g.isNull(WikiTransformationEntity::getKbId)
|
.or(g -> g.isNull(WikiTransformationEntity::getKbId)
|
||||||
.eq(WikiTransformationEntity::getWorkspaceId, workspaceId)))
|
.and(ws -> ws.eq(WikiTransformationEntity::getWorkspaceId, workspaceId)
|
||||||
|
.or().isNull(WikiTransformationEntity::getWorkspaceId))))
|
||||||
.orderByDesc(WikiTransformationEntity::getUpdateTime));
|
.orderByDesc(WikiTransformationEntity::getUpdateTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<WikiTransformationEntity> listByWorkspace(Long workspaceId) {
|
public List<WikiTransformationEntity> listByWorkspace(Long workspaceId) {
|
||||||
return transformationMapper.selectList(
|
return transformationMapper.selectList(
|
||||||
new LambdaQueryWrapper<WikiTransformationEntity>()
|
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));
|
.orderByDesc(WikiTransformationEntity::getUpdateTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +73,9 @@ public class WikiTransformationService {
|
|||||||
WikiTransformationEntity global = transformationMapper.selectOne(
|
WikiTransformationEntity global = transformationMapper.selectOne(
|
||||||
new LambdaQueryWrapper<WikiTransformationEntity>()
|
new LambdaQueryWrapper<WikiTransformationEntity>()
|
||||||
.isNull(WikiTransformationEntity::getKbId)
|
.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)
|
.eq(WikiTransformationEntity::getName, name)
|
||||||
.last("LIMIT 1"));
|
.last("LIMIT 1"));
|
||||||
return Optional.ofNullable(global);
|
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