Ajusta endpoints

This commit is contained in:
LeoMortari
2025-08-21 17:47:05 -03:00
parent 44a1625631
commit 2ed4a9122d
9 changed files with 131 additions and 69 deletions

1
.gitignore vendored
View File

@@ -11,6 +11,7 @@ pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
yarn.lock
# OS
.DS_Store

View File

@@ -14,7 +14,7 @@ model videos {
datetime_download DateTime? @db.Timestamp(6)
datetime_convert DateTime? @db.Timestamp(6)
url String @db.VarChar(244)
situation EVideoSituation
situation video_situation
error_message String? @db.VarChar(244)
clips_quantity Int?
times Json?
@@ -25,7 +25,7 @@ model videos {
datetime_posted DateTime? @db.Timestamp(6)
}
enum EVideoSituation {
enum video_situation {
FILA
PROCESSANDO
ERRO

View File

@@ -1,8 +1,13 @@
import { NestFactory } from '@nestjs/core';
import { ClassSerializerInterceptor } from '@nestjs/common';
import { NestFactory, Reflector } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const reflector = app.get(Reflector);
app.useGlobalInterceptors(new ClassSerializerInterceptor(reflector));
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
void bootstrap();

View File

@@ -7,7 +7,7 @@ import {
IsUrl,
MaxLength,
} from 'class-validator';
import { EVideoSituation } from '../../../generated/prisma';
import { video_situation } from '../../../generated/prisma';
export class CreateVideoDto {
@IsUrl()
@@ -15,8 +15,8 @@ export class CreateVideoDto {
url!: string;
@IsOptional()
@IsEnum(EVideoSituation)
situation?: EVideoSituation;
@IsEnum(video_situation)
situation?: video_situation;
@IsOptional()
@IsString()

View File

@@ -1,5 +1,5 @@
import { Type } from 'class-transformer';
import { IsInt, IsOptional, Max, Min } from 'class-validator';
import { IsIn, IsInt, IsOptional, IsString, Max, Min } from 'class-validator';
export class PaginatedQueryDto {
@IsOptional()
@@ -14,12 +14,19 @@ export class PaginatedQueryDto {
@Min(1)
@Max(100)
perPage: number = 20;
@IsOptional()
@Type(() => String)
@IsString()
@IsIn(['asc', 'desc'])
direction: string = 'desc';
}
export type PaginatedResponse<T> = {
content: T[];
pagination: {
page: number;
direction: string;
perPage: number;
total: number;
totalPages: number;

View File

@@ -1,12 +1,41 @@
import { EVideoSituation } from 'generated/prisma';
import { Expose, Transform, TransformFnParams } from 'class-transformer';
import dayjs from 'dayjs';
import { video_situation } from 'generated/prisma';
export interface VideoResponseDto {
id: number;
title: string | null;
url: string;
situation: EVideoSituation;
clips_quantity: number;
videoid: string;
filename: string;
datetime_download: Date | string;
export class VideoResponseDto {
@Expose()
id!: number;
@Expose()
title!: string | null;
@Expose()
url!: string;
@Expose()
situation!: video_situation;
@Expose()
@Transform(({ value }: TransformFnParams) =>
typeof value === 'number' ? value : 0,
)
clips_quantity!: number;
@Expose()
@Transform(({ value }: TransformFnParams) =>
typeof value === 'string' ? value : '',
)
videoid!: string;
@Expose()
@Transform(({ value }: TransformFnParams) =>
typeof value === 'string' ? value : '',
)
filename!: string;
@Expose()
@Transform(({ value }: TransformFnParams) =>
value ? dayjs(value as Date | string).format('DD/MM/YYYY HH:mm:ss') : '',
)
datetime_download!: string;
}

View File

@@ -7,11 +7,12 @@ import {
Body,
Query,
} from '@nestjs/common';
import { videos, Prisma, EVideoSituation } from 'generated/prisma';
import { videos, Prisma, video_situation } from 'generated/prisma';
import { VideosService } from './videos.service';
import { VideoResponseDto } from './dto/video-response.dto';
import { PaginatedQueryDto, PaginatedResponse } from './dto/paginated.dto';
import { EBooleanPipe } from './videos.pipe';
@Controller('videos')
export class VideosController {
@@ -20,13 +21,21 @@ export class VideosController {
@Get()
async list(
@Query() query: PaginatedQueryDto,
@Query('situation') situation?: EVideoSituation,
): Promise<PaginatedResponse<VideoResponseDto>> {
return this.videosService.listPaginated(
query.page,
query.perPage,
situation,
);
@Query('situation') situation?: video_situation,
@Query('pageable', new EBooleanPipe(true)) pageable: boolean = true,
): Promise<PaginatedResponse<VideoResponseDto> | VideoResponseDto[]> {
const situacao = situation?.toLocaleUpperCase() as video_situation;
if (pageable || query.page || query.perPage) {
return this.videosService.listPaginated(
Number(query.page ?? 1),
Number(query.perPage ?? 10),
query.direction as 'asc' | 'desc',
situacao,
);
}
return this.videosService.list(situacao);
}
@Get(':id')
@@ -46,9 +55,4 @@ export class VideosController {
async delete(@Param('id') id: string): Promise<videos> {
return this.videosService.delete(Number(id));
}
@Get('situation/:s')
async listBySituation(@Param('s') s: EVideoSituation): Promise<videos[]> {
return this.videosService.listBySituation(s);
}
}

22
src/videos/videos.pipe.ts Normal file
View File

@@ -0,0 +1,22 @@
import { Injectable, PipeTransform, BadRequestException } from '@nestjs/common';
@Injectable()
export class EBooleanPipe
implements PipeTransform<string | undefined, boolean>
{
constructor(private readonly defaultValue?: boolean) {}
transform(value: string | undefined): boolean {
if (value === undefined || value === null || value === '') {
if (this.defaultValue !== undefined) return this.defaultValue;
throw new BadRequestException('Parâmetro esperado: "V" ou "F"');
}
const normalized = value.toString().trim().toUpperCase();
if (normalized === 'V') return true;
if (normalized === 'F') return false;
throw new BadRequestException('Valor inválido. Use "V" ou "F"');
}
}

View File

@@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import dayjs from 'dayjs';
import { plainToInstance } from 'class-transformer';
import { Prisma, videos, EVideoSituation } from 'generated/prisma';
import { Prisma, videos, video_situation } from 'generated/prisma';
import { PrismaService } from '../prisma/prisma.service';
import { VideoResponseDto } from './dto/video-response.dto';
@@ -11,37 +11,42 @@ import { PaginatedResponse } from './dto/paginated.dto';
export class VideosService {
constructor(private readonly prisma: PrismaService) {}
async list(): Promise<VideoResponseDto[]> {
const data = await this.prisma.videos.findMany({ orderBy: { id: 'desc' } });
async list(situation?: video_situation): Promise<VideoResponseDto[]> {
const data = await this.prisma.videos.findMany({
where: situation ? { situation } : {},
orderBy: { id: 'desc' },
select: {
id: true,
title: true,
url: true,
situation: true,
clips_quantity: true,
videoid: true,
filename: true,
datetime_download: true,
},
});
return data.map((video) => ({
id: video.id,
title: video.title,
url: video.url,
situation: video.situation,
clips_quantity: video.clips_quantity ?? 0,
videoid: video.videoid!,
filename: video.filename || '',
datetime_download: video.datetime_download
? dayjs(video.datetime_download).format('DD/MM/YYYY HH:mm:ss')
: '',
}));
return plainToInstance(VideoResponseDto, data, {
excludeExtraneousValues: true,
});
}
async listPaginated(
page: number,
perPage: number,
situation?: EVideoSituation,
direction: 'asc' | 'desc' = 'desc',
situation?: video_situation,
): Promise<PaginatedResponse<VideoResponseDto>> {
const skip = (page - 1) * perPage;
const where = situation ? { situation } : undefined;
const skip = page >= 1 ? page * perPage : 0;
const where = situation ? { situation } : {};
const [rows, total] = await Promise.all([
this.prisma.videos.findMany({
where,
orderBy: { id: 'asc' },
orderBy: { id: direction },
skip,
take: Number(perPage),
take: perPage ?? 1,
select: {
id: true,
title: true,
@@ -56,16 +61,11 @@ export class VideosService {
this.prisma.videos.count({ where }),
]);
const content: VideoResponseDto[] = rows.map((row) => ({
id: row.id,
title: row.title ?? null,
url: row.url,
situation: row.situation,
clips_quantity: row.clips_quantity ?? 0,
videoid: row.videoid ?? '',
filename: row.filename ?? '',
datetime_download: row.datetime_download ?? '',
}));
const content: VideoResponseDto[] = plainToInstance(
VideoResponseDto,
rows,
{ excludeExtraneousValues: true },
);
const totalPages = Math.max(1, Math.ceil(total / perPage));
@@ -73,6 +73,7 @@ export class VideosService {
content,
pagination: {
page,
direction,
perPage,
total,
totalPages,
@@ -100,11 +101,4 @@ export class VideosService {
where: { id },
});
}
async listBySituation(situation: EVideoSituation): Promise<videos[]> {
return this.prisma.videos.findMany({
where: { situation },
orderBy: { id: 'desc' },
});
}
}