1 parent
40d579cdb9
commit
382cafa152
8 files changed
+25
-9
No files matched your search
+9
@@ -44,4 +44,13 @@ describe('GetIssueStatsUseCase', () => {
|
||||
expect(stats.total).toBe(0);
|
||||
expect(stats.byStatus.every((entry) => entry.count === 0)).toBe(true);
|
||||
});
|
||||
|
||||
it('passes projectId through to the repository to filter stats by project', async () => {
|
||||
vi.mocked(repository.countByStatus).mockResolvedValue([{ status: 'OPEN', count: 3 }]);
|
||||
|
||||
const stats = await useCase.execute('user-1', 'project-1');
|
||||
|
||||
expect(repository.countByStatus).toHaveBeenCalledWith('user-1', 'project-1');
|
||||
expect(stats.total).toBe(3);
|
||||
});
|
||||
});
|
||||
@@ -10,8 +10,8 @@ export class GetIssueStatsUseCase {
|
||||
private readonly issueRepository: IIssueRepository,
|
||||
) {}
|
||||
|
||||
async execute(userId: string): Promise<IssueStatsDto> {
|
||||
const counts = await this.issueRepository.countByStatus(userId);
|
||||
async execute(userId: string, projectId?: string): Promise<IssueStatsDto> {
|
||||
const counts = await this.issueRepository.countByStatus(userId, projectId);
|
||||
const countByStatus = new Map(counts.map((entry) => [entry.status, entry.count]));
|
||||
|
||||
// Каждый известный статус попадает в ответ, даже если по нему 0 задач
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface IIssueRepository {
|
||||
assigneeId?: string;
|
||||
projectId?: string;
|
||||
}): Promise<{ list: Issue[]; meta: PaginationMeta }>;
|
||||
countByStatus(userId: string): Promise<Array<{ status: string; count: number }>>;
|
||||
countByStatus(userId: string, projectId?: string): Promise<Array<{ status: string; count: number }>>;
|
||||
create(issue: Issue): Promise<Issue>;
|
||||
update(issue: Issue): Promise<void>;
|
||||
delete(id: number): Promise<void>;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const ISSUE_STATUSES = [
|
||||
{ name: 'Open', alias: 'OPEN', color: 'surface-light' },
|
||||
{ name: 'Open', alias: 'OPEN', color: 'default' },
|
||||
{ name: 'In progress', alias: 'IN_PROGRESS', color: 'warning' },
|
||||
{ name: 'Done', alias: 'DONE', color: 'success' },
|
||||
{ name: 'Cancelled', alias: 'CANCELLED', color: 'primary' },
|
||||
|
||||
+2
-2
@@ -85,10 +85,10 @@ export class IssuePrismaRepository implements IIssueRepository {
|
||||
};
|
||||
}
|
||||
|
||||
async countByStatus(userId: string): Promise<Array<{ status: string; count: number }>> {
|
||||
async countByStatus(userId: string, projectId?: string): Promise<Array<{ status: string; count: number }>> {
|
||||
const groups = await this.prisma.issue.groupBy({
|
||||
by: ['status'],
|
||||
where: { OR: ownedByUser(userId) },
|
||||
where: { AND: [{ OR: ownedByUser(userId) }, ...(projectId ? [{ projectId }] : [])] },
|
||||
_count: { _all: true },
|
||||
});
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import { UpdateIssueRequestDto } from '../dto/update-issue.dto.js';
|
||||
import { IssueNotFoundException } from '../../domain/exceptions/issue-not-found.exception.js';
|
||||
import { IssueProjectNotFoundException } from '../../domain/exceptions/issue-project-not-found.exception.js';
|
||||
import { FilterIssueDto } from '../dto/filter-issue.dto.js';
|
||||
import { IssueStatsQueryDto } from '../dto/issue-stats-query.dto.js';
|
||||
import { GetIssueByCkeyUseCase } from '../../application/use-cases/get-issue-by-ckey/get-issue-by-ckey.use-case.js';
|
||||
import { GetIssueByCkeyQuery } from '../../application/use-cases/get-issue-by-ckey/get-issue-by-ckey.query.js';
|
||||
import { IssueEventsService } from '../../application/services/issue-events.service.js';
|
||||
@@ -84,8 +85,8 @@ export class IssueController {
|
||||
}
|
||||
|
||||
@Get('stats')
|
||||
stats(@Session() session: UserSession) {
|
||||
return this.getIssueStatsUseCase.execute(session.user.id);
|
||||
stats(@Query() query: IssueStatsQueryDto, @Session() session: UserSession) {
|
||||
return this.getIssueStatsUseCase.execute(session.user.id, query.projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { StringDecorator } from '../../../../common/decorators/validators.js';
|
||||
|
||||
export class IssueStatsQueryDto {
|
||||
@StringDecorator({ isOptional: true })
|
||||
projectId?: string;
|
||||
}
|
||||
Reference in new issue
Block a user