mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-11 05:11:49 +01:00
Bound issue detail text scans
This commit is contained in:
1 parent
5efedffa0e
commit
9bc1b9dc5b
5 files changed
+108
-12
No files matched your search
@@ -190,6 +190,10 @@ describe("issue goal context routes", () => {
|
||||
title: projectGoal.title,
|
||||
}),
|
||||
);
|
||||
expect(mockIssueService.findMentionedProjectIds).toHaveBeenCalledWith(
|
||||
"11111111-1111-4111-8111-111111111111",
|
||||
{ includeCommentBodies: false },
|
||||
);
|
||||
expect(mockGoalService.getDefaultCompanyGoal).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
} from "./helpers/embedded-postgres.js";
|
||||
import { instanceSettingsService } from "../services/instance-settings.ts";
|
||||
import { issueService } from "../services/issues.ts";
|
||||
import { buildProjectMentionHref } from "@paperclipai/shared";
|
||||
|
||||
const embeddedPostgresSupport = await getEmbeddedPostgresTestSupport();
|
||||
const describeEmbeddedPostgres = embeddedPostgresSupport.supported ? describe : describe.skip;
|
||||
@@ -1182,3 +1183,84 @@ describeEmbeddedPostgres("issueService blockers and dependency wake readiness",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describeEmbeddedPostgres("issueService.findMentionedProjectIds", () => {
|
||||
let db!: ReturnType<typeof createDb>;
|
||||
let svc!: ReturnType<typeof issueService>;
|
||||
let tempDb: Awaited<ReturnType<typeof startEmbeddedPostgresTestDatabase>> | null = null;
|
||||
|
||||
beforeAll(async () => {
|
||||
tempDb = await startEmbeddedPostgresTestDatabase("paperclip-issues-mentioned-projects-");
|
||||
db = createDb(tempDb.connectionString);
|
||||
svc = issueService(db);
|
||||
await ensureIssueRelationsTable(db);
|
||||
}, 20_000);
|
||||
|
||||
afterEach(async () => {
|
||||
await db.delete(issueComments);
|
||||
await db.delete(issueRelations);
|
||||
await db.delete(issueInboxArchives);
|
||||
await db.delete(activityLog);
|
||||
await db.delete(issues);
|
||||
await db.delete(executionWorkspaces);
|
||||
await db.delete(projectWorkspaces);
|
||||
await db.delete(projects);
|
||||
await db.delete(agents);
|
||||
await db.delete(instanceSettings);
|
||||
await db.delete(companies);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await tempDb?.cleanup();
|
||||
});
|
||||
|
||||
it("can skip comment-body scans for bounded issue detail reads", async () => {
|
||||
const companyId = randomUUID();
|
||||
const issueId = randomUUID();
|
||||
const titleProjectId = randomUUID();
|
||||
const commentProjectId = randomUUID();
|
||||
|
||||
await db.insert(companies).values({
|
||||
id: companyId,
|
||||
name: "Paperclip",
|
||||
issuePrefix: `T${companyId.replace(/-/g, "").slice(0, 6).toUpperCase()}`,
|
||||
requireBoardApprovalForNewAgents: false,
|
||||
});
|
||||
|
||||
await db.insert(projects).values([
|
||||
{
|
||||
id: titleProjectId,
|
||||
companyId,
|
||||
name: "Title project",
|
||||
status: "in_progress",
|
||||
},
|
||||
{
|
||||
id: commentProjectId,
|
||||
companyId,
|
||||
name: "Comment project",
|
||||
status: "in_progress",
|
||||
},
|
||||
]);
|
||||
|
||||
await db.insert(issues).values({
|
||||
id: issueId,
|
||||
companyId,
|
||||
title: `Link [Title](${buildProjectMentionHref(titleProjectId)})`,
|
||||
description: null,
|
||||
status: "todo",
|
||||
priority: "medium",
|
||||
});
|
||||
|
||||
await db.insert(issueComments).values({
|
||||
companyId,
|
||||
issueId,
|
||||
body: `Comment link [Comment](${buildProjectMentionHref(commentProjectId)})`,
|
||||
});
|
||||
|
||||
expect(await svc.findMentionedProjectIds(issueId, { includeCommentBodies: false })).toEqual([titleProjectId]);
|
||||
expect(await svc.findMentionedProjectIds(issueId)).toEqual([
|
||||
titleProjectId,
|
||||
commentProjectId,
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -112,6 +112,7 @@ export function redactCurrentUserText(input: string, opts?: CurrentUserRedaction
|
||||
let result = input;
|
||||
|
||||
for (const homeDir of [...homeDirs].sort((a, b) => b.length - a.length)) {
|
||||
if (!result.includes(homeDir)) continue;
|
||||
const lastSegment = splitPathSegments(homeDir).pop() ?? "";
|
||||
const replacementDir = lastSegment
|
||||
? replaceLastPathSegment(homeDir, maskUserNameForLogs(lastSegment, replacement))
|
||||
@@ -120,6 +121,7 @@ export function redactCurrentUserText(input: string, opts?: CurrentUserRedaction
|
||||
}
|
||||
|
||||
for (const userName of [...userNames].sort((a, b) => b.length - a.length)) {
|
||||
if (!result.includes(userName)) continue;
|
||||
const pattern = new RegExp(`(?<![A-Za-z0-9._-])${escapeRegExp(userName)}(?![A-Za-z0-9._-])`, "g");
|
||||
result = result.replace(pattern, maskUserNameForLogs(userName, replacement));
|
||||
}
|
||||
|
||||
@@ -729,7 +729,7 @@ export function issueRoutes(
|
||||
const [{ project, goal }, ancestors, mentionedProjectIds, documentPayload, relations] = await Promise.all([
|
||||
resolveIssueProjectAndGoal(issue),
|
||||
svc.getAncestors(issue.id),
|
||||
svc.findMentionedProjectIds(issue.id),
|
||||
svc.findMentionedProjectIds(issue.id, { includeCommentBodies: false }),
|
||||
documentsSvc.getIssueDocumentPayload(issue),
|
||||
svc.getRelationSummaries(issue.id),
|
||||
]);
|
||||
|
||||
@@ -2350,7 +2350,10 @@ export function issueService(db: Db) {
|
||||
return [...resolved];
|
||||
},
|
||||
|
||||
findMentionedProjectIds: async (issueId: string) => {
|
||||
findMentionedProjectIds: async (
|
||||
issueId: string,
|
||||
opts?: { includeCommentBodies?: boolean },
|
||||
) => {
|
||||
const issue = await db
|
||||
.select({
|
||||
companyId: issues.companyId,
|
||||
@@ -2362,21 +2365,26 @@ export function issueService(db: Db) {
|
||||
.then((rows) => rows[0] ?? null);
|
||||
if (!issue) return [];
|
||||
|
||||
const comments = await db
|
||||
.select({ body: issueComments.body })
|
||||
.from(issueComments)
|
||||
.where(eq(issueComments.issueId, issueId));
|
||||
|
||||
const mentionedIds = new Set<string>();
|
||||
for (const source of [
|
||||
issue.title,
|
||||
issue.description ?? "",
|
||||
...comments.map((comment) => comment.body),
|
||||
]) {
|
||||
for (const source of [issue.title, issue.description ?? ""]) {
|
||||
for (const projectId of extractProjectMentionIds(source)) {
|
||||
mentionedIds.add(projectId);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts?.includeCommentBodies !== false) {
|
||||
const comments = await db
|
||||
.select({ body: issueComments.body })
|
||||
.from(issueComments)
|
||||
.where(eq(issueComments.issueId, issueId));
|
||||
|
||||
for (const comment of comments) {
|
||||
for (const projectId of extractProjectMentionIds(comment.body)) {
|
||||
mentionedIds.add(projectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mentionedIds.size === 0) return [];
|
||||
|
||||
const rows = await db
|
||||
|
||||
Reference in new issue
Block a user