feat(tags): add input validation for tag and entity operations

Added validation checks for tagId, personId, and projectId across tag-related operations. Introduced `BadRequestException` for invalid or missing inputs. Replaced generic errors with more descriptive exceptions.
This commit is contained in:
Mathis HERRIOT 2025-05-16 23:52:30 +02:00
parent d48b6fa48b
commit 5abd33e648
No known key found for this signature in database
GPG Key ID: E7EB4A211D8D4907

View File

@ -1,4 +1,4 @@
import { Injectable, NotFoundException, Inject } from '@nestjs/common'; import { Injectable, NotFoundException, Inject, BadRequestException } from '@nestjs/common';
import { eq, and } from 'drizzle-orm'; import { eq, and } from 'drizzle-orm';
import { DRIZZLE } from '../../../database/database.module'; import { DRIZZLE } from '../../../database/database.module';
import * as schema from '../../../database/schema'; import * as schema from '../../../database/schema';
@ -47,11 +47,11 @@ export class TagsService {
.select() .select()
.from(schema.tags) .from(schema.tags)
.where(eq(schema.tags.id, id)); .where(eq(schema.tags.id, id));
if (!tag) { if (!tag) {
throw new NotFoundException(`Tag with ID ${id} not found`); throw new NotFoundException(`Tag with ID ${id} not found`);
} }
return tag; return tag;
} }
@ -67,11 +67,11 @@ export class TagsService {
}) })
.where(eq(schema.tags.id, id)) .where(eq(schema.tags.id, id))
.returning(); .returning();
if (!tag) { if (!tag) {
throw new NotFoundException(`Tag with ID ${id} not found`); throw new NotFoundException(`Tag with ID ${id} not found`);
} }
return tag; return tag;
} }
@ -83,11 +83,11 @@ export class TagsService {
.delete(schema.tags) .delete(schema.tags)
.where(eq(schema.tags.id, id)) .where(eq(schema.tags.id, id))
.returning(); .returning();
if (!tag) { if (!tag) {
throw new NotFoundException(`Tag with ID ${id} not found`); throw new NotFoundException(`Tag with ID ${id} not found`);
} }
return tag; return tag;
} }
@ -95,22 +95,30 @@ export class TagsService {
* Add a tag to a person * Add a tag to a person
*/ */
async addTagToPerson(tagId: string, personId: string) { async addTagToPerson(tagId: string, personId: string) {
// Validate tagId and personId
if (!tagId) {
throw new BadRequestException('Tag ID is required');
}
if (!personId) {
throw new BadRequestException('Person ID is required');
}
// Check if the tag exists and is of type PERSON // Check if the tag exists and is of type PERSON
const tag = await this.findById(tagId); const tag = await this.findById(tagId);
if (tag.type !== 'PERSON') { if (tag.type !== 'PERSON') {
throw new Error(`Tag with ID ${tagId} is not of type PERSON`); throw new BadRequestException(`Tag with ID ${tagId} is not of type PERSON`);
} }
// Check if the person exists // Check if the person exists
const [person] = await this.db const [person] = await this.db
.select() .select()
.from(schema.persons) .from(schema.persons)
.where(eq(schema.persons.id, personId)); .where(eq(schema.persons.id, personId));
if (!person) { if (!person) {
throw new NotFoundException(`Person with ID ${personId} not found`); throw new NotFoundException(`Person with ID ${personId} not found`);
} }
// Check if the tag is already associated with the person // Check if the tag is already associated with the person
const [existingRelation] = await this.db const [existingRelation] = await this.db
.select() .select()
@ -121,11 +129,11 @@ export class TagsService {
eq(schema.personToTag.tagId, tagId) eq(schema.personToTag.tagId, tagId)
) )
); );
if (existingRelation) { if (existingRelation) {
return existingRelation; return existingRelation;
} }
// Add the tag to the person // Add the tag to the person
const [relation] = await this.db const [relation] = await this.db
.insert(schema.personToTag) .insert(schema.personToTag)
@ -134,7 +142,7 @@ export class TagsService {
tagId, tagId,
}) })
.returning(); .returning();
return relation; return relation;
} }
@ -142,6 +150,14 @@ export class TagsService {
* Remove a tag from a person * Remove a tag from a person
*/ */
async removeTagFromPerson(tagId: string, personId: string) { async removeTagFromPerson(tagId: string, personId: string) {
// Validate tagId and personId
if (!tagId) {
throw new BadRequestException('Tag ID is required');
}
if (!personId) {
throw new BadRequestException('Person ID is required');
}
const [relation] = await this.db const [relation] = await this.db
.delete(schema.personToTag) .delete(schema.personToTag)
.where( .where(
@ -151,11 +167,11 @@ export class TagsService {
) )
) )
.returning(); .returning();
if (!relation) { if (!relation) {
throw new NotFoundException(`Tag with ID ${tagId} is not associated with person with ID ${personId}`); throw new NotFoundException(`Tag with ID ${tagId} is not associated with person with ID ${personId}`);
} }
return relation; return relation;
} }
@ -163,22 +179,30 @@ export class TagsService {
* Add a tag to a project * Add a tag to a project
*/ */
async addTagToProject(tagId: string, projectId: string) { async addTagToProject(tagId: string, projectId: string) {
// Validate tagId and projectId
if (!tagId) {
throw new BadRequestException('Tag ID is required');
}
if (!projectId) {
throw new BadRequestException('Project ID is required');
}
// Check if the tag exists and is of type PROJECT // Check if the tag exists and is of type PROJECT
const tag = await this.findById(tagId); const tag = await this.findById(tagId);
if (tag.type !== 'PROJECT') { if (tag.type !== 'PROJECT') {
throw new Error(`Tag with ID ${tagId} is not of type PROJECT`); throw new BadRequestException(`Tag with ID ${tagId} is not of type PROJECT`);
} }
// Check if the project exists // Check if the project exists
const [project] = await this.db const [project] = await this.db
.select() .select()
.from(schema.projects) .from(schema.projects)
.where(eq(schema.projects.id, projectId)); .where(eq(schema.projects.id, projectId));
if (!project) { if (!project) {
throw new NotFoundException(`Project with ID ${projectId} not found`); throw new NotFoundException(`Project with ID ${projectId} not found`);
} }
// Check if the tag is already associated with the project // Check if the tag is already associated with the project
const [existingRelation] = await this.db const [existingRelation] = await this.db
.select() .select()
@ -189,11 +213,11 @@ export class TagsService {
eq(schema.projectToTag.tagId, tagId) eq(schema.projectToTag.tagId, tagId)
) )
); );
if (existingRelation) { if (existingRelation) {
return existingRelation; return existingRelation;
} }
// Add the tag to the project // Add the tag to the project
const [relation] = await this.db const [relation] = await this.db
.insert(schema.projectToTag) .insert(schema.projectToTag)
@ -202,7 +226,7 @@ export class TagsService {
tagId, tagId,
}) })
.returning(); .returning();
return relation; return relation;
} }
@ -210,6 +234,14 @@ export class TagsService {
* Remove a tag from a project * Remove a tag from a project
*/ */
async removeTagFromProject(tagId: string, projectId: string) { async removeTagFromProject(tagId: string, projectId: string) {
// Validate tagId and projectId
if (!tagId) {
throw new BadRequestException('Tag ID is required');
}
if (!projectId) {
throw new BadRequestException('Project ID is required');
}
const [relation] = await this.db const [relation] = await this.db
.delete(schema.projectToTag) .delete(schema.projectToTag)
.where( .where(
@ -219,11 +251,11 @@ export class TagsService {
) )
) )
.returning(); .returning();
if (!relation) { if (!relation) {
throw new NotFoundException(`Tag with ID ${tagId} is not associated with project with ID ${projectId}`); throw new NotFoundException(`Tag with ID ${tagId} is not associated with project with ID ${projectId}`);
} }
return relation; return relation;
} }
@ -231,16 +263,21 @@ export class TagsService {
* Get all tags for a person * Get all tags for a person
*/ */
async getTagsForPerson(personId: string) { async getTagsForPerson(personId: string) {
// Validate personId
if (!personId) {
throw new BadRequestException('Person ID is required');
}
// Check if the person exists // Check if the person exists
const [person] = await this.db const [person] = await this.db
.select() .select()
.from(schema.persons) .from(schema.persons)
.where(eq(schema.persons.id, personId)); .where(eq(schema.persons.id, personId));
if (!person) { if (!person) {
throw new NotFoundException(`Person with ID ${personId} not found`); throw new NotFoundException(`Person with ID ${personId} not found`);
} }
// Get all tags for the person // Get all tags for the person
return this.db return this.db
.select({ .select({
@ -255,16 +292,21 @@ export class TagsService {
* Get all tags for a project * Get all tags for a project
*/ */
async getTagsForProject(projectId: string) { async getTagsForProject(projectId: string) {
// Validate projectId
if (!projectId) {
throw new BadRequestException('Project ID is required');
}
// Check if the project exists // Check if the project exists
const [project] = await this.db const [project] = await this.db
.select() .select()
.from(schema.projects) .from(schema.projects)
.where(eq(schema.projects.id, projectId)); .where(eq(schema.projects.id, projectId));
if (!project) { if (!project) {
throw new NotFoundException(`Project with ID ${projectId} not found`); throw new NotFoundException(`Project with ID ${projectId} not found`);
} }
// Get all tags for the project // Get all tags for the project
return this.db return this.db
.select({ .select({
@ -274,4 +316,4 @@ export class TagsService {
.innerJoin(schema.tags, eq(schema.projectToTag.tagId, schema.tags.id)) .innerJoin(schema.tags, eq(schema.projectToTag.tagId, schema.tags.id))
.where(eq(schema.projectToTag.projectId, projectId)); .where(eq(schema.projectToTag.projectId, projectId));
} }
} }