This commit is contained in:
1 parent
5607e97533
commit
f44590b8ca
5 files changed
+73
-6
No files matched your search
@@ -4,7 +4,7 @@ export class ListIssuesQuery {
|
||||
public readonly page: number,
|
||||
public readonly userId: string,
|
||||
public readonly projectId?: string,
|
||||
public readonly status?: string,
|
||||
public readonly status?: string[],
|
||||
public readonly text?: string,
|
||||
public readonly assigneeId?: string,
|
||||
) {}
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface IIssueRepository {
|
||||
limit: number;
|
||||
page: number;
|
||||
userId: string;
|
||||
status?: string;
|
||||
status?: string[];
|
||||
text?: string;
|
||||
assigneeId?: string;
|
||||
projectId?: string;
|
||||
|
||||
+2
-2
@@ -40,7 +40,7 @@ export class IssuePrismaRepository implements IIssueRepository {
|
||||
limit: number;
|
||||
page: number;
|
||||
userId: string;
|
||||
status?: string;
|
||||
status?: string[];
|
||||
text?: string;
|
||||
assigneeId?: string;
|
||||
projectId?: string;
|
||||
@@ -52,7 +52,7 @@ export class IssuePrismaRepository implements IIssueRepository {
|
||||
where: {
|
||||
AND: [
|
||||
{ OR: ownedByUser(params.userId) },
|
||||
...(params.status ? [{ status: params.status }] : []),
|
||||
...(params.status?.length ? [{ status: { in: params.status } }] : []),
|
||||
...(params.assigneeId ? [{ assigneeId: params.assigneeId }] : []),
|
||||
...(params.projectId ? [{ projectId: params.projectId }] : []),
|
||||
...(params.text
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
import { validateSync } from 'class-validator';
|
||||
import { FilterIssueDto } from './filter-issue.dto.js';
|
||||
|
||||
const parse = (query: Record<string, unknown>) => {
|
||||
const dto = plainToInstance(FilterIssueDto, query);
|
||||
return { dto, errors: validateSync(dto) };
|
||||
};
|
||||
|
||||
describe('FilterIssueDto.status', () => {
|
||||
it('leaves status undefined when absent', () => {
|
||||
const { dto, errors } = parse({});
|
||||
|
||||
expect(dto.status).toBeUndefined();
|
||||
expect(errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('wraps a single status into an array', () => {
|
||||
const { dto, errors } = parse({ status: 'OPEN' });
|
||||
|
||||
expect(dto.status).toEqual(['OPEN']);
|
||||
expect(errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('splits a comma-separated list', () => {
|
||||
const { dto, errors } = parse({ status: 'OPEN, IN_PROGRESS' });
|
||||
|
||||
expect(dto.status).toEqual(['OPEN', 'IN_PROGRESS']);
|
||||
expect(errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('accepts a repeated query param', () => {
|
||||
const { dto, errors } = parse({ status: ['OPEN', 'DONE'] });
|
||||
|
||||
expect(dto.status).toEqual(['OPEN', 'DONE']);
|
||||
expect(errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('rejects an unknown status', () => {
|
||||
const { errors } = parse({ status: 'OPEN,UNKNOWN' });
|
||||
|
||||
expect(errors.map((el) => el.property)).toEqual(['status']);
|
||||
});
|
||||
|
||||
it('rejects an empty value', () => {
|
||||
const { errors } = parse({ status: '' });
|
||||
|
||||
expect(errors.map((el) => el.property)).toEqual(['status']);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,7 @@
|
||||
import { Transform } from 'class-transformer';
|
||||
import { ArrayNotEmpty, IsArray, IsIn, IsOptional } from 'class-validator';
|
||||
import { IntDecorator, StringDecorator } from '../../../../common/decorators/validators.js';
|
||||
import { ISSUE_STATUSES } from '../../domain/value-objects/issue-status.vo.js';
|
||||
|
||||
export class FilterIssueDto {
|
||||
@IntDecorator({ isOptional: true })
|
||||
@@ -13,8 +16,21 @@ export class FilterIssueDto {
|
||||
@StringDecorator({ isOptional: true })
|
||||
assigneeId?: string;
|
||||
|
||||
@StringDecorator({ isOptional: true })
|
||||
status?: string;
|
||||
/** Comma-separated (`?status=OPEN,IN_PROGRESS`) or repeated (`?status=OPEN&status=IN_PROGRESS`) */
|
||||
@IsOptional()
|
||||
@Transform(({ value }) =>
|
||||
value === undefined || value === null
|
||||
? undefined
|
||||
: ([] as unknown[])
|
||||
.concat(value)
|
||||
.flatMap((el) => String(el).split(','))
|
||||
.map((el) => el.trim())
|
||||
.filter(Boolean),
|
||||
)
|
||||
@IsArray()
|
||||
@ArrayNotEmpty()
|
||||
@IsIn(ISSUE_STATUSES.map((status) => status.alias), { each: true })
|
||||
status?: string[];
|
||||
|
||||
@StringDecorator({ isOptional: true })
|
||||
projectId?: string;
|
||||
|
||||
Reference in new issue
Block a user