feat: add traveler entity distinct from trip membership
This commit is contained in:
72
backend/apps/api/src/trips/travelers.controller.spec.ts
Normal file
72
backend/apps/api/src/trips/travelers.controller.spec.ts
Normal 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',
|
||||
);
|
||||
});
|
||||
});
|
||||
59
backend/apps/api/src/trips/travelers.controller.ts
Normal file
59
backend/apps/api/src/trips/travelers.controller.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
|
||||
Reference in New Issue
Block a user