From 954c1c2ef4b9db4fca26f6a5c2b56a28f777de03 Mon Sep 17 00:00:00 2001 From: Jonathan Jogenfors Date: Fri, 9 Feb 2024 19:02:11 +0100 Subject: [PATCH] feat(server): server-side checking of duplicate import paths and exclusion patterns (#6993) validate path and pattern --- server/e2e/api/specs/library.e2e-spec.ts | 54 +++++++++++++++++++++++- server/src/domain/library/library.dto.ts | 6 ++- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/server/e2e/api/specs/library.e2e-spec.ts b/server/e2e/api/specs/library.e2e-spec.ts index 0ce192e479..e704cb79e8 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 e12ca4c185..638841ec63 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[]; }