feat(backend): activate endpoint for deactivated users
Symmetrisch zu Modul-27/28-Fix fuer Colony/Apiary: kein Weg, einen versehentlich deaktivierten Benutzer wieder zu aktivieren. Neuer POST :id/activate-Endpunkt setzt isActive zurueck auf true und loescht deactivatedAt; ConflictException wenn bereits aktiv.
This commit is contained in:
1 parent
6346bfab1d
commit
57979f0371
4 files changed
+68
No files matched your search
@@ -43,4 +43,9 @@ export class UsersController {
|
|||||||
deactivate(@Param('id') id: string, @CurrentUser() actor: AuthenticatedUser) {
|
deactivate(@Param('id') id: string, @CurrentUser() actor: AuthenticatedUser) {
|
||||||
return this.usersService.deactivate(id, actor.id);
|
return this.usersService.deactivate(id, actor.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post(':id/activate')
|
||||||
|
activate(@Param('id') id: string, @CurrentUser() actor: AuthenticatedUser) {
|
||||||
|
return this.usersService.activate(id, actor.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -122,4 +122,32 @@ describe('UsersService', () => {
|
|||||||
expect.objectContaining({ entityType: 'User', action: 'DEACTIVATE' }),
|
expect.objectContaining({ entityType: 'User', action: 'DEACTIVATE' }),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('activate throws ConflictException when already active', async () => {
|
||||||
|
prisma.user.findUnique.mockResolvedValue(existingUser);
|
||||||
|
await expect(usersService.activate(existingUser.id)).rejects.toBeInstanceOf(
|
||||||
|
ConflictException,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('activate sets isActive to true and clears deactivatedAt', async () => {
|
||||||
|
prisma.user.findUnique.mockResolvedValue({
|
||||||
|
...existingUser,
|
||||||
|
isActive: false,
|
||||||
|
deactivatedAt: new Date(),
|
||||||
|
});
|
||||||
|
prisma.user.update.mockResolvedValue({
|
||||||
|
...existingUser,
|
||||||
|
isActive: true,
|
||||||
|
deactivatedAt: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await usersService.activate(existingUser.id);
|
||||||
|
|
||||||
|
expect(result.isActive).toBe(true);
|
||||||
|
expect(result.deactivatedAt).toBeNull();
|
||||||
|
expect(auditLog.record).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ entityType: 'User', action: 'UPDATE' }),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -116,4 +116,24 @@ export class UsersService {
|
|||||||
});
|
});
|
||||||
return this.toSummary(user);
|
return this.toSummary(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async activate(id: string, actingUserId?: string): Promise<UserSummary> {
|
||||||
|
const before = await this.findById(id);
|
||||||
|
if (before.isActive) {
|
||||||
|
throw new ConflictException('User is already active');
|
||||||
|
}
|
||||||
|
const user = await this.prisma.user.update({
|
||||||
|
where: { id },
|
||||||
|
data: { isActive: true, deactivatedAt: null },
|
||||||
|
});
|
||||||
|
await this.auditLog.record({
|
||||||
|
entityType: 'User',
|
||||||
|
entityId: id,
|
||||||
|
action: 'UPDATE',
|
||||||
|
userId: actingUserId,
|
||||||
|
beforeData: this.toSummary(before),
|
||||||
|
afterData: this.toSummary(user),
|
||||||
|
});
|
||||||
|
return this.toSummary(user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -99,4 +99,19 @@ describe('Users (e2e)', () => {
|
|||||||
expect(deactivated.isActive).toBe(false);
|
expect(deactivated.isActive).toBe(false);
|
||||||
expect(deactivated.deactivatedAt).not.toBeNull();
|
expect(deactivated.deactivatedAt).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('lets an admin reactivate a deactivated user, and rejects reactivating an already-active one', async () => {
|
||||||
|
const activated = await request(app.getHttpServer())
|
||||||
|
.post(`/api/v1/users/${createdUserId}/activate`)
|
||||||
|
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||||
|
.expect(201);
|
||||||
|
const body = activated.body as UserBody;
|
||||||
|
expect(body.isActive).toBe(true);
|
||||||
|
expect(body.deactivatedAt).toBeNull();
|
||||||
|
|
||||||
|
await request(app.getHttpServer())
|
||||||
|
.post(`/api/v1/users/${createdUserId}/activate`)
|
||||||
|
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||||
|
.expect(409);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user