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;
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.dom.asynciterable.d.ts","./node_modules/typescript/lib/lib.webworker.importscripts.d.ts","./node_modules/typescript/lib/lib.scripthost.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2025.float16.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/typescript/lib/lib.es2023.full.d.ts","./node_modules/reflect-metadata/index.d.ts","./node_modules/@nestjs/common/decorators/core/bind.decorator.d.ts","./node_modules/@nestjs/common/interfaces/abstract.interface.d.ts","./node_modules/@nestjs/common/interfaces/controllers/controller-metadata.interface.d.ts","./node_modules/@nestjs/common/interfaces/controllers/controller.interface.d.ts","./node_modules/@nestjs/common/interfaces/features/arguments-host.interface.d.ts","./node_modules/@nestjs/common/interfaces/exceptions/exception-filter.interface.d.ts","./node_modules/rxjs/dist/types/internal/subscription.d.ts","./node_modules/rxjs/dist/types/internal/subscriber.d.ts","./node_modules/rxjs/dist/types/internal/operator.d.ts","./node_modules/rxjs/dist/types/internal/observable.d.ts","./node_modules/rxjs/dist/types/internal/types.d.ts","./node_modules/rxjs/dist/types/internal/operators/audit.d.ts","./node_modules/rxjs/dist/types/internal/operators/audittime.d.ts","./node_modules/rxjs/dist/types/internal/operators/buffer.d.ts","./node_modules/rxjs/dist/types/internal/operators/buffercount.d.ts","./node_modules/rxjs/dist/types/internal/operators/buffertime.d.ts","./node_modules/rxjs/dist/types/internal/operators/buffertoggle.d.tsLine truncated
|
||||
{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.dom.asynciterable.d.ts","./node_modules/typescript/lib/lib.webworker.importscripts.d.ts","./node_modules/typescript/lib/lib.scripthost.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2025.float16.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/typescript/lib/lib.es2023.full.d.ts","./node_modules/reflect-metadata/index.d.ts","./node_modules/@nestjs/common/decorators/core/bind.decorator.d.ts","./node_modules/@nestjs/common/interfaces/abstract.interface.d.ts","./node_modules/@nestjs/common/interfaces/controllers/controller-metadata.interface.d.ts","./node_modules/@nestjs/common/interfaces/controllers/controller.interface.d.ts","./node_modules/@nestjs/common/interfaces/features/arguments-host.interface.d.ts","./node_modules/@nestjs/common/interfaces/exceptions/exception-filter.interface.d.ts","./node_modules/rxjs/dist/types/internal/subscription.d.ts","./node_modules/rxjs/dist/types/internal/subscriber.d.ts","./node_modules/rxjs/dist/types/internal/operator.d.ts","./node_modules/rxjs/dist/types/internal/observable.d.ts","./node_modules/rxjs/dist/types/internal/types.d.ts","./node_modules/rxjs/dist/types/internal/operators/audit.d.ts","./node_modules/rxjs/dist/types/internal/operators/audittime.d.ts","./node_modules/rxjs/dist/types/internal/operators/buffer.d.ts","./node_modules/rxjs/dist/types/internal/operators/buffercount.d.ts","./node_modules/rxjs/dist/types/internal/operators/buffertime.d.ts","./node_modules/rxjs/dist/types/internal/operators/buffertoggle.d.tsLine truncated
|
||||
Reference in new issue
Block a user