diff --git a/server/e2e/api/specs/library.e2e-spec.ts b/server/e2e/api/specs/library.e2e-spec.ts index 0ce192e479e4d..e704cb79e8ff0 100644 --- a/server/e2e/api/specs/library.e2e-spec.ts +++ b/server/e2e/api/specs/library.e2e-spec.ts @@ -98,6 +98,36 @@ describe(`${LibraryController.name} (e2e)`, () => { ); }); + it('should not create an external library with duplicate import paths', async () => { + const { status, body } = await request(server) + .post('/library') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ + type: LibraryType.EXTERNAL, + name: 'My Awesome Library', + importPaths: ['/path', '/path'], + exclusionPatterns: ['**/Raw/**'], + }); + + expect(status).toBe(400); + expect(body).toEqual(errorStub.badRequest(["All importPaths's elements must be unique"])); + }); + + it('should not create an external library with duplicate exclusion patterns', async () => { + const { status, body } = await request(server) + .post('/library') + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ + type: LibraryType.EXTERNAL, + name: 'My Awesome Library', + importPaths: ['/path/to/import'], + exclusionPatterns: ['**/Raw/**', '**/Raw/**'], + }); + + expect(status).toBe(400); + expect(body).toEqual(errorStub.badRequest(["All exclusionPatterns's elements must be unique"])); + }); + it('should create an upload library with defaults', async () => { const { status, body } = await request(server) .post('/library') @@ -229,7 +259,7 @@ describe(`${LibraryController.name} (e2e)`, () => { ); }); - it('should not allow an empty import path', async () => { + it('should reject an empty import path', async () => { const { status, body } = await request(server) .put(`/library/${library.id}`) .set('Authorization', `Bearer ${admin.accessToken}`) @@ -239,6 +269,16 @@ describe(`${LibraryController.name} (e2e)`, () => { expect(body).toEqual(errorStub.badRequest(['each value in importPaths should not be empty'])); }); + it('should reject duplicate import paths', async () => { + const { status, body } = await request(server) + .put(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ importPaths: ['/path', '/path'] }); + + expect(status).toBe(400); + expect(body).toEqual(errorStub.badRequest(["All importPaths's elements must be unique"])); + }); + it('should change the exclusion pattern', async () => { const { status, body } = await request(server) .put(`/library/${library.id}`) @@ -253,7 +293,17 @@ describe(`${LibraryController.name} (e2e)`, () => { ); }); - it('should not allow an empty exclusion pattern', async () => { + it('should reject duplicate exclusion patterns', async () => { + const { status, body } = await request(server) + .put(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send({ exclusionPatterns: ['**/*.jpg', '**/*.jpg'] }); + + expect(status).toBe(400); + expect(body).toEqual(errorStub.badRequest(["All exclusionPatterns's elements must be unique"])); + }); + + it('should reject an empty exclusion pattern', async () => { const { status, body } = await request(server) .put(`/library/${library.id}`) .set('Authorization', `Bearer ${admin.accessToken}`) diff --git a/server/src/domain/library/library.dto.ts b/server/src/domain/library/library.dto.ts index e12ca4c185d2d..638841ec63800 100644 --- a/server/src/domain/library/library.dto.ts +++ b/server/src/domain/library/library.dto.ts @@ -1,6 +1,6 @@ import { LibraryEntity, LibraryType } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; -import { IsBoolean, IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator'; +import { ArrayUnique, IsBoolean, IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator'; import { ValidateUUID } from '../domain.util'; export class CreateLibraryDto { @@ -20,11 +20,13 @@ export class CreateLibraryDto { @IsOptional() @IsString({ each: true }) @IsNotEmpty({ each: true }) + @ArrayUnique() importPaths?: string[]; @IsOptional() @IsString({ each: true }) @IsNotEmpty({ each: true }) + @ArrayUnique() exclusionPatterns?: string[]; @IsOptional() @@ -45,11 +47,13 @@ export class UpdateLibraryDto { @IsOptional() @IsString({ each: true }) @IsNotEmpty({ each: true }) + @ArrayUnique() importPaths?: string[]; @IsOptional() @IsNotEmpty({ each: true }) @IsString({ each: true }) + @ArrayUnique() exclusionPatterns?: string[]; }