feat(F-048): completed feature
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import type { FastifySchema } from 'fastify';
|
||||
import type pg from 'pg';
|
||||
import { z } from 'zod';
|
||||
import type { Authenticate } from '../../../shared/auth.js';
|
||||
import { requireRole } from '../../../shared/auth.js';
|
||||
import { AppError } from '../../../shared/errors.js';
|
||||
import { errorSchema } from '../../../shared/swagger.js';
|
||||
import { parseJson } from '../../../shared/http-input.js';
|
||||
import { ReviewsService } from '../application/reviews-service.js';
|
||||
import {
|
||||
@@ -47,7 +49,14 @@ export async function registerReviewsRoutes(
|
||||
const verifier = new PgOrderItemVerifier(deps.pool);
|
||||
const service = new ReviewsService(repository, verifier);
|
||||
|
||||
app.post('/reviews', async (request, reply) => {
|
||||
const submitReviewSchema: FastifySchema = {
|
||||
tags: ['Reviews'],
|
||||
summary: 'Submit review',
|
||||
description: 'Crea una reseña. Requiere compra verificada.',
|
||||
body: { type: 'object' },
|
||||
response: { 201: { type: 'object' }, 401: errorSchema },
|
||||
};
|
||||
app.post('/reviews', { schema: submitReviewSchema }, async (request, reply) => {
|
||||
const user = await deps.authenticate(request);
|
||||
const input = parseJson(submitSchema, request.body);
|
||||
try {
|
||||
@@ -58,7 +67,18 @@ export async function registerReviewsRoutes(
|
||||
}
|
||||
});
|
||||
|
||||
app.patch('/reviews/:id/moderate', async (request, reply) => {
|
||||
const moderateSwaggerSchema: FastifySchema = {
|
||||
tags: ['Reviews'],
|
||||
summary: 'Moderate review (admin)',
|
||||
params: {
|
||||
type: 'object',
|
||||
required: ['id'],
|
||||
properties: { id: { type: 'string', format: 'uuid' } },
|
||||
},
|
||||
body: { type: 'object' },
|
||||
response: { 401: errorSchema, 403: errorSchema },
|
||||
};
|
||||
app.patch('/reviews/:id/moderate', { schema: moderateSwaggerSchema }, async (request, reply) => {
|
||||
const user = await deps.authenticate(request);
|
||||
requireRole(user, 'admin');
|
||||
const { id } = moderateParamSchema.parse(request.params);
|
||||
@@ -71,13 +91,35 @@ export async function registerReviewsRoutes(
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/reviews', async (request, reply) => {
|
||||
const listReviewsSchema: FastifySchema = {
|
||||
tags: ['Reviews'],
|
||||
summary: 'List reviews (público)',
|
||||
querystring: {
|
||||
type: 'object',
|
||||
required: ['productId'],
|
||||
properties: { productId: { type: 'string', format: 'uuid' } },
|
||||
},
|
||||
};
|
||||
app.get('/reviews', { schema: listReviewsSchema }, async (request, reply) => {
|
||||
const { productId } = productIdQuerySchema.parse(request.query);
|
||||
const result = await service.listPublishedByProduct(productId);
|
||||
return reply.send(result);
|
||||
});
|
||||
|
||||
app.get('/reviews/admin', async (request, reply) => {
|
||||
const adminReviewsSchema: FastifySchema = {
|
||||
tags: ['Reviews'],
|
||||
summary: 'List reviews (admin)',
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
status: { type: 'string', enum: ['pending', 'published', 'rejected'] },
|
||||
limit: { type: 'integer', default: 20 },
|
||||
offset: { type: 'integer', default: 0 },
|
||||
},
|
||||
},
|
||||
response: { 401: errorSchema, 403: errorSchema },
|
||||
};
|
||||
app.get('/reviews/admin', { schema: adminReviewsSchema }, async (request, reply) => {
|
||||
const user = await deps.authenticate(request);
|
||||
requireRole(user, 'admin');
|
||||
const { status, limit, offset } = adminListSchema.parse(request.query ?? {});
|
||||
|
||||
@@ -9,7 +9,11 @@ export interface ReviewsRepository {
|
||||
insert(review: SubmitReviewCommand): Promise<Review | undefined>;
|
||||
setStatus(id: string, status: 'published' | 'rejected'): Promise<Review | undefined>;
|
||||
listPublishedByProduct(productId: string): Promise<PublishedReview[]>;
|
||||
listAll(params?: { status?: string; limit?: number; offset?: number }): Promise<{ items: Review[]; total: number }>;
|
||||
listAll(params?: {
|
||||
status?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}): Promise<{ items: Review[]; total: number }>;
|
||||
aggregate(productId: string): Promise<ProductReviewAggregate>;
|
||||
findByOrderItemId(orderItemId: string): Promise<Review | undefined>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user