feat: add traveler entity distinct from trip membership

This commit is contained in:
Bastian Wagner
2026-08-17 15:28:35 +02:00
parent 6954024622
commit ee7ec94ea0
9 changed files with 355 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
import { TravelersController } from './travelers.controller';
import type { AuthenticatedUser } from '../../../../libs/auth/src';
describe('TravelersController', () => {
const currentUser: AuthenticatedUser = {
id: 'u1',
externalSubjectId: 'sub-1',
displayName: 'Alex',
email: 'a@example.com',
};
it('GET /trips/:tripId/travelers lists travelers', async () => {
const travelersService = { listTravelers: jest.fn().mockResolvedValue([]) };
const controller = new TravelersController(travelersService as never);
await expect(controller.list('t1')).resolves.toEqual([]);
expect(travelersService.listTravelers).toHaveBeenCalledWith('t1');
});
it('POST /trips/:tripId/travelers creates a traveler recorded by the current member', async () => {
const created = {
id: 'trav-1',
tripId: 't1',
displayName: 'Mila',
travelerType: 'CHILD',
};
const travelersService = {
createTraveler: jest.fn().mockResolvedValue(created),
};
const controller = new TravelersController(travelersService as never);
const dto = { displayName: 'Mila', travelerType: 'CHILD' as const };
await expect(controller.create('t1', currentUser, dto)).resolves.toEqual(
created,
);
expect(travelersService.createTraveler).toHaveBeenCalledWith(
't1',
dto,
'u1',
);
});
it('PATCH /trips/:tripId/travelers/:travelerId updates a traveler', async () => {
const updated = { id: 'trav-1', tripId: 't1', displayName: 'Mila Renamed' };
const travelersService = {
updateTraveler: jest.fn().mockResolvedValue(updated),
};
const controller = new TravelersController(travelersService as never);
await expect(
controller.update('t1', 'trav-1', { displayName: 'Mila Renamed' }),
).resolves.toEqual(updated);
expect(travelersService.updateTraveler).toHaveBeenCalledWith(
't1',
'trav-1',
{ displayName: 'Mila Renamed' },
);
});
it('DELETE /trips/:tripId/travelers/:travelerId removes a traveler', async () => {
const travelersService = {
removeTraveler: jest.fn().mockResolvedValue(undefined),
};
const controller = new TravelersController(travelersService as never);
await controller.remove('t1', 'trav-1');
expect(travelersService.removeTraveler).toHaveBeenCalledWith(
't1',
'trav-1',
);
});
});

View File

@@ -0,0 +1,59 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
Post,
UseGuards,
} from '@nestjs/common';
import { OidcAuthGuard } from '../../../../libs/auth/src';
import type { AuthenticatedUser } from '../../../../libs/auth/src';
import {
TravelersService,
TripMembershipGuard,
} from '../../../../libs/trips/src';
import type {
CreateTravelerDto,
Traveler,
UpdateTravelerDto,
} from '../../../../libs/trips/src';
import { CurrentUser } from '../auth/current-user.decorator';
@Controller('trips/:tripId/travelers')
@UseGuards(OidcAuthGuard, TripMembershipGuard)
export class TravelersController {
constructor(private readonly travelersService: TravelersService) {}
@Get()
list(@Param('tripId') tripId: string): Promise<Traveler[]> {
return this.travelersService.listTravelers(tripId);
}
@Post()
create(
@Param('tripId') tripId: string,
@CurrentUser() currentUser: AuthenticatedUser,
@Body() dto: CreateTravelerDto,
): Promise<Traveler> {
return this.travelersService.createTraveler(tripId, dto, currentUser.id);
}
@Patch(':travelerId')
update(
@Param('tripId') tripId: string,
@Param('travelerId') travelerId: string,
@Body() dto: UpdateTravelerDto,
): Promise<Traveler> {
return this.travelersService.updateTraveler(tripId, travelerId, dto);
}
@Delete(':travelerId')
remove(
@Param('tripId') tripId: string,
@Param('travelerId') travelerId: string,
): Promise<void> {
return this.travelersService.removeTraveler(tripId, travelerId);
}
}

View File

@@ -4,6 +4,7 @@ import { TripsLibModule } from '../../../../libs/trips/src';
import { TripsController } from './trips.controller';
import { TripMembersController } from './trip-members.controller';
import { TripInvitationsController } from './trip-invitations.controller';
import { TravelersController } from './travelers.controller';
@Module({
imports: [AuthModule, TripsLibModule],
@@ -11,6 +12,7 @@ import { TripInvitationsController } from './trip-invitations.controller';
TripsController,
TripMembersController,
TripInvitationsController,
TravelersController,
],
})
export class TripsApiModule {}