Files
task-stream/libs/api/tracker/application/src/lib/commands/vote-issue.command-handler.ts
Josep Piguillem 8bf7d47b2e feat: guest role for clients and outsiders
A project member can be a GUEST. In a private project a guest only sees
the tickets they take part in (opened, assigned or followed): every other
ticket answers 404, also in my work, realtime changes, blockers and
linked tickets, and project-wide views (activity, statistics, dashboard,
releases, sprints, epics, trash) answer 403. Guests open tickets without
assigning or planning them, and comment, attach, follow and vote on their
own; they change nothing else. In a public project they see what anyone
does. The web gives them only Tablero, Lista and Miembros and loads none
of the project-wide data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 18:02:36 +02:00

36 lines
1.3 KiB
TypeScript

import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import {
BusinessRuleError,
ForbiddenError,
} from '@task-stream/api/shared/domain';
import { CommandResult, ack } from './command-result';
import { VoteIssueCommand } from './vote-issue.command';
import { WriteAccess } from './write-access';
/**
* Anyone signed in votes on a public project's tickets (that is the point:
* the people it is for say what they want); on a private one, its members.
*/
@CommandHandler(VoteIssueCommand)
export class VoteIssueCommandHandler
implements ICommandHandler<VoteIssueCommand>
{
constructor(private readonly access: WriteAccess) {}
async execute(command: VoteIssueCommand): Promise<CommandResult> {
const issue = await this.access.issue(command.issueId);
const project = await this.access.project(issue.projectId);
if (!project.isPublic && !project.isMember(command.actorId))
throw new ForbiddenError(
`You are not a member of project ${project.key}`,
);
project.assertNotArchived();
this.access.assertSees(project, issue, command.actorId);
if (issue.trashedAt)
throw new BusinessRuleError(`${issue.key} is in the trash`);
issue.vote(command.actorId, command.on);
await issue.commitAndWait();
return ack(issue);
}
}