fix: align admin profile request validation

This commit is contained in:
Bastian Wagner
2026-08-01 11:20:47 +02:00
parent d962200b79
commit 431eba86e7
4 changed files with 41 additions and 8 deletions

View File

@@ -69,6 +69,22 @@ describe('AdminUsersController', () => {
}); });
describe('admin user DTOs', () => { describe('admin user DTOs', () => {
it('accepts omitted and string profile names but rejects explicit nulls', async () => {
const omitted = plainToInstance(AdminUserProfileDto, {});
const names = plainToInstance(AdminUserProfileDto, {
firstName: 'Ada',
lastName: 'Admin',
});
const nulls = plainToInstance(AdminUserProfileDto, {
firstName: null,
lastName: null,
});
expect(await validate(omitted)).toEqual([]);
expect(await validate(names)).toEqual([]);
expect(await validate(nulls)).not.toEqual([]);
});
it('accepts only profile names in the profile DTO', async () => { it('accepts only profile names in the profile DTO', async () => {
const dto = plainToInstance(AdminUserProfileDto, { const dto = plainToInstance(AdminUserProfileDto, {
firstName: 'Ada', firstName: 'Ada',

View File

@@ -7,20 +7,21 @@ import {
Max, Max,
MaxLength, MaxLength,
Min, Min,
ValidateIf,
} from 'class-validator'; } from 'class-validator';
import { RoleEnum } from '../../roles/roles.enum'; import { RoleEnum } from '../../roles/roles.enum';
import { StatusEnum } from '../../statuses/statuses.enum'; import { StatusEnum } from '../../statuses/statuses.enum';
export class AdminUserProfileDto { export class AdminUserProfileDto {
@IsOptional() @ValidateIf((_, value) => value !== undefined)
@IsString() @IsString()
@MaxLength(255) @MaxLength(255)
firstName?: string | null; firstName?: string;
@IsOptional() @ValidateIf((_, value) => value !== undefined)
@IsString() @IsString()
@MaxLength(255) @MaxLength(255)
lastName?: string | null; lastName?: string;
} }
export class AdminUserRoleDto { export class AdminUserRoleDto {

View File

@@ -3,6 +3,7 @@ import { TestBed } from '@angular/core/testing';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { AdminUsersApi } from './admin-users-api'; import { AdminUsersApi } from './admin-users-api';
import { AdminUserProfileRequest } from '../../models/user-directory.model';
import { UsersApi } from './users-api'; import { UsersApi } from './users-api';
describe('UsersApi', () => { describe('UsersApi', () => {
@@ -32,6 +33,21 @@ describe('AdminUsersApi', () => {
let service: AdminUsersApi; let service: AdminUsersApi;
let httpMock: HttpTestingController; let httpMock: HttpTestingController;
it('accepts profile requests with omitted or string names only', () => {
const omitted: AdminUserProfileRequest = {};
const names: AdminUserProfileRequest = {
firstName: 'Ada',
lastName: 'Admin',
};
// @ts-expect-error Profile names cannot be explicitly cleared to null.
const nullName: AdminUserProfileRequest = { firstName: null };
expect(omitted).toEqual({});
expect(names).toEqual({ firstName: 'Ada', lastName: 'Admin' });
expect(nullName).toEqual({ firstName: null });
});
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ providers: [provideHttpClient(), provideHttpClientTesting()] }); TestBed.configureTestingModule({ providers: [provideHttpClient(), provideHttpClientTesting()] });
service = TestBed.inject(AdminUsersApi); service = TestBed.inject(AdminUsersApi);
@@ -41,10 +57,10 @@ describe('AdminUsersApi', () => {
afterEach(() => httpMock.verify()); afterEach(() => httpMock.verify());
it('uses the dedicated admin profile endpoint and payload', () => { it('uses the dedicated admin profile endpoint and payload', () => {
service.updateProfile(7, { firstName: 'Alex', lastName: null }).subscribe(); service.updateProfile(7, { firstName: 'Alex' }).subscribe();
const request = httpMock.expectOne(`${environment.apiUrl}admin/users/7/profile`); const request = httpMock.expectOne(`${environment.apiUrl}admin/users/7/profile`);
expect(request.request.method).toBe('PATCH'); expect(request.request.method).toBe('PATCH');
expect(request.request.body).toEqual({ firstName: 'Alex', lastName: null }); expect(request.request.body).toEqual({ firstName: 'Alex' });
request.flush({}); request.flush({});
}); });

View File

@@ -48,8 +48,8 @@ export interface UserDirectoryFilters {
} }
export interface AdminUserProfileRequest { export interface AdminUserProfileRequest {
firstName?: string | null; firstName?: string;
lastName?: string | null; lastName?: string;
} }
export type AdminUserRoleId = 1 | 2; export type AdminUserRoleId = 1 | 2;