feat(F-048): completed feature
This commit is contained in:
@@ -3,12 +3,14 @@
|
||||
* checks, so a non-owner always gets 403 regardless of resource existence.
|
||||
*/
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import type { FastifySchema } from 'fastify';
|
||||
import { z } from 'zod';
|
||||
import type pg from 'pg';
|
||||
import { parseJson } from '../../../shared/http-input.js';
|
||||
import { AppError } from '../../../shared/errors.js';
|
||||
import { errorSchema } from '../../../shared/swagger.js';
|
||||
import { requireOwnerOrAdmin, requireRole, type Authenticate } from '../../../shared/auth.js';
|
||||
import { GetCustomer, GetProfile, ListCustomers, ListProfiles, UpdateProfile } from '../application/profile-use-cases.js';
|
||||
import { GetCustomer, ListCustomers, UpdateProfile } from '../application/profile-use-cases.js';
|
||||
import {
|
||||
CreateAddress,
|
||||
DeleteAddress,
|
||||
@@ -60,17 +62,28 @@ export async function registerUsersRoutes(
|
||||
): Promise<void> {
|
||||
const profiles = new PgProfileRepository(deps.pool);
|
||||
const addresses = new PgAddressRepository(deps.pool);
|
||||
const getProfile = new GetProfile(profiles);
|
||||
const getCustomer = new GetCustomer(profiles);
|
||||
const updateProfile = new UpdateProfile(profiles);
|
||||
const listProfiles = new ListProfiles(profiles);
|
||||
const listCustomers = new ListCustomers(profiles);
|
||||
const listAddresses = new ListAddresses(addresses);
|
||||
const createAddress = new CreateAddress(addresses);
|
||||
const updateAddress = new UpdateAddress(addresses);
|
||||
const deleteAddress = new DeleteAddress(addresses);
|
||||
|
||||
app.get('/users', async (request, reply) => {
|
||||
const listUsersSchema: FastifySchema = {
|
||||
tags: ['Users'],
|
||||
summary: 'List users (admin)',
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
q: { type: 'string' },
|
||||
limit: { type: 'integer', default: 20 },
|
||||
offset: { type: 'integer', default: 0 },
|
||||
},
|
||||
},
|
||||
response: { 401: errorSchema, 403: errorSchema },
|
||||
};
|
||||
app.get('/users', { schema: listUsersSchema }, async (request, reply) => {
|
||||
const user = await deps.authenticate(request);
|
||||
requireRole(user, 'admin');
|
||||
const query = request.query as Record<string, string | undefined>;
|
||||
@@ -84,7 +97,17 @@ export async function registerUsersRoutes(
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/users/:id', async (request, reply) => {
|
||||
const getUserSchema: FastifySchema = {
|
||||
tags: ['Users'],
|
||||
summary: 'Get user',
|
||||
params: {
|
||||
type: 'object',
|
||||
required: ['id'],
|
||||
properties: { id: { type: 'string', format: 'uuid' } },
|
||||
},
|
||||
response: { 401: errorSchema, 403: errorSchema, 404: errorSchema },
|
||||
};
|
||||
app.get('/users/:id', { schema: getUserSchema }, async (request, reply) => {
|
||||
const user = await deps.authenticate(request);
|
||||
const { id } = parseJson(uuidParamSchema, request.params);
|
||||
requireOwnerOrAdmin(user, id);
|
||||
@@ -95,7 +118,18 @@ export async function registerUsersRoutes(
|
||||
return reply.send(serializeCustomer(customer));
|
||||
});
|
||||
|
||||
app.patch('/users/:id', async (request, reply) => {
|
||||
const patchUserSchema: FastifySchema = {
|
||||
tags: ['Users'],
|
||||
summary: 'Update user profile',
|
||||
params: {
|
||||
type: 'object',
|
||||
required: ['id'],
|
||||
properties: { id: { type: 'string', format: 'uuid' } },
|
||||
},
|
||||
body: { type: 'object' },
|
||||
response: { 401: errorSchema, 403: errorSchema, 404: errorSchema },
|
||||
};
|
||||
app.patch('/users/:id', { schema: patchUserSchema }, async (request, reply) => {
|
||||
const user = await deps.authenticate(request);
|
||||
const { id } = parseJson(uuidParamSchema, request.params);
|
||||
requireOwnerOrAdmin(user, id);
|
||||
@@ -104,7 +138,17 @@ export async function registerUsersRoutes(
|
||||
return reply.send(serializeProfile(profile));
|
||||
});
|
||||
|
||||
app.get('/users/:id/addresses', async (request, reply) => {
|
||||
const addressesSchema: FastifySchema = {
|
||||
tags: ['Users'],
|
||||
summary: 'List user addresses',
|
||||
params: {
|
||||
type: 'object',
|
||||
required: ['id'],
|
||||
properties: { id: { type: 'string', format: 'uuid' } },
|
||||
},
|
||||
response: { 401: errorSchema, 403: errorSchema },
|
||||
};
|
||||
app.get('/users/:id/addresses', { schema: addressesSchema }, async (request, reply) => {
|
||||
const user = await deps.authenticate(request);
|
||||
const { id } = parseJson(uuidParamSchema, request.params);
|
||||
requireOwnerOrAdmin(user, id);
|
||||
@@ -112,7 +156,18 @@ export async function registerUsersRoutes(
|
||||
return reply.send({ items: items.map(serializeAddress) });
|
||||
});
|
||||
|
||||
app.post('/users/:id/addresses', async (request, reply) => {
|
||||
const createAddressSchema: FastifySchema = {
|
||||
tags: ['Users'],
|
||||
summary: 'Create address',
|
||||
params: {
|
||||
type: 'object',
|
||||
required: ['id'],
|
||||
properties: { id: { type: 'string', format: 'uuid' } },
|
||||
},
|
||||
body: { type: 'object' },
|
||||
response: { 201: { type: 'object' }, 401: errorSchema, 403: errorSchema },
|
||||
};
|
||||
app.post('/users/:id/addresses', { schema: createAddressSchema }, async (request, reply) => {
|
||||
const user = await deps.authenticate(request);
|
||||
const { id } = parseJson(uuidParamSchema, request.params);
|
||||
requireOwnerOrAdmin(user, id);
|
||||
@@ -121,28 +176,63 @@ export async function registerUsersRoutes(
|
||||
return reply.code(201).send(serializeAddress(address));
|
||||
});
|
||||
|
||||
app.patch('/users/:id/addresses/:addressId', async (request, reply) => {
|
||||
const user = await deps.authenticate(request);
|
||||
const { id, addressId } = parseJson(addressIdParamSchema, request.params);
|
||||
requireOwnerOrAdmin(user, id);
|
||||
const patch = parseJson(addressPatchSchema, request.body);
|
||||
const address = await updateAddress.execute(id, addressId, patch);
|
||||
if (!address) {
|
||||
throw new AppError(404, 'NOT_FOUND', 'Address not found');
|
||||
}
|
||||
return reply.send(serializeAddress(address));
|
||||
});
|
||||
const updateAddressSchema: FastifySchema = {
|
||||
tags: ['Users'],
|
||||
summary: 'Update address',
|
||||
params: {
|
||||
type: 'object',
|
||||
required: ['id', 'addressId'],
|
||||
properties: {
|
||||
id: { type: 'string', format: 'uuid' },
|
||||
addressId: { type: 'string', format: 'uuid' },
|
||||
},
|
||||
},
|
||||
body: { type: 'object' },
|
||||
response: { 401: errorSchema, 403: errorSchema, 404: errorSchema },
|
||||
};
|
||||
app.patch(
|
||||
'/users/:id/addresses/:addressId',
|
||||
{ schema: updateAddressSchema },
|
||||
async (request, reply) => {
|
||||
const user = await deps.authenticate(request);
|
||||
const { id, addressId } = parseJson(addressIdParamSchema, request.params);
|
||||
requireOwnerOrAdmin(user, id);
|
||||
const patch = parseJson(addressPatchSchema, request.body);
|
||||
const address = await updateAddress.execute(id, addressId, patch);
|
||||
if (!address) {
|
||||
throw new AppError(404, 'NOT_FOUND', 'Address not found');
|
||||
}
|
||||
return reply.send(serializeAddress(address));
|
||||
},
|
||||
);
|
||||
|
||||
app.delete('/users/:id/addresses/:addressId', async (request, reply) => {
|
||||
const user = await deps.authenticate(request);
|
||||
const { id, addressId } = parseJson(addressIdParamSchema, request.params);
|
||||
requireOwnerOrAdmin(user, id);
|
||||
const deleted = await deleteAddress.execute(id, addressId);
|
||||
if (!deleted) {
|
||||
throw new AppError(404, 'NOT_FOUND', 'Address not found');
|
||||
}
|
||||
return reply.code(204).send();
|
||||
});
|
||||
const deleteAddressSchema: FastifySchema = {
|
||||
tags: ['Users'],
|
||||
summary: 'Delete address',
|
||||
params: {
|
||||
type: 'object',
|
||||
required: ['id', 'addressId'],
|
||||
properties: {
|
||||
id: { type: 'string', format: 'uuid' },
|
||||
addressId: { type: 'string', format: 'uuid' },
|
||||
},
|
||||
},
|
||||
response: { 204: { type: 'null' }, 401: errorSchema, 403: errorSchema, 404: errorSchema },
|
||||
};
|
||||
app.delete(
|
||||
'/users/:id/addresses/:addressId',
|
||||
{ schema: deleteAddressSchema },
|
||||
async (request, reply) => {
|
||||
const user = await deps.authenticate(request);
|
||||
const { id, addressId } = parseJson(addressIdParamSchema, request.params);
|
||||
requireOwnerOrAdmin(user, id);
|
||||
const deleted = await deleteAddress.execute(id, addressId);
|
||||
if (!deleted) {
|
||||
throw new AppError(404, 'NOT_FOUND', 'Address not found');
|
||||
}
|
||||
return reply.code(204).send();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function serializeProfile(profile: Profile) {
|
||||
|
||||
Reference in New Issue
Block a user