Compare commits
No commits in common. "a1abde36e6524de71ca7b8596f6b82b8e971ca5e" and "bdca6511bd6d83d5bfddf102b390194947cb0857" have entirely different histories.
a1abde36e6
...
bdca6511bd
@ -11,7 +11,6 @@ import { GroupsModule } from './modules/groups/groups.module';
|
|||||||
import { TagsModule } from './modules/tags/tags.module';
|
import { TagsModule } from './modules/tags/tags.module';
|
||||||
import { WebSocketsModule } from './modules/websockets/websockets.module';
|
import { WebSocketsModule } from './modules/websockets/websockets.module';
|
||||||
import { JwtAuthGuard } from './modules/auth/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from './modules/auth/guards/jwt-auth.guard';
|
||||||
import { PersonsModule } from './modules/persons/persons.module';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -26,7 +25,6 @@ import { PersonsModule } from './modules/persons/persons.module';
|
|||||||
GroupsModule,
|
GroupsModule,
|
||||||
TagsModule,
|
TagsModule,
|
||||||
WebSocketsModule,
|
WebSocketsModule,
|
||||||
PersonsModule,
|
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [
|
providers: [
|
||||||
|
@ -18,17 +18,10 @@ export class CreateGroupDto {
|
|||||||
@IsUUID()
|
@IsUUID()
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional description for the group
|
|
||||||
*/
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
description?: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional metadata for the group
|
* Optional metadata for the group
|
||||||
*/
|
*/
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsObject()
|
@IsObject()
|
||||||
metadata?: Record<string, any>;
|
metadata?: Record<string, any>;
|
||||||
}
|
}
|
@ -18,17 +18,10 @@ export class UpdateGroupDto {
|
|||||||
@IsUUID()
|
@IsUUID()
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* Description for the group
|
|
||||||
*/
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
description?: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata for the group
|
* Metadata for the group
|
||||||
*/
|
*/
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsObject()
|
@IsObject()
|
||||||
metadata?: Record<string, any>;
|
metadata?: Record<string, any>;
|
||||||
}
|
}
|
@ -17,17 +17,10 @@ export class GroupsService {
|
|||||||
* Create a new group
|
* Create a new group
|
||||||
*/
|
*/
|
||||||
async create(createGroupDto: CreateGroupDto) {
|
async create(createGroupDto: CreateGroupDto) {
|
||||||
// Extract description from DTO if present
|
|
||||||
const { description, ...restDto } = createGroupDto;
|
|
||||||
|
|
||||||
// Store description in metadata if provided
|
|
||||||
const metadata = description ? { description } : {};
|
|
||||||
|
|
||||||
const [group] = await this.db
|
const [group] = await this.db
|
||||||
.insert(schema.groups)
|
.insert(schema.groups)
|
||||||
.values({
|
.values({
|
||||||
...restDto,
|
...createGroupDto,
|
||||||
metadata,
|
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
@ -37,108 +30,52 @@ export class GroupsService {
|
|||||||
group,
|
group,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add description to response if it exists in metadata
|
return group;
|
||||||
const response = { ...group };
|
|
||||||
if (group.metadata && group.metadata.description) {
|
|
||||||
response.description = group.metadata.description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all groups
|
* Find all groups
|
||||||
*/
|
*/
|
||||||
async findAll() {
|
async findAll() {
|
||||||
const groups = await this.db.select().from(schema.groups);
|
return this.db.select().from(schema.groups);
|
||||||
|
|
||||||
// Add description to each group if it exists in metadata
|
|
||||||
return groups.map(group => {
|
|
||||||
const response = { ...group };
|
|
||||||
if (group.metadata && group.metadata.description) {
|
|
||||||
response.description = group.metadata.description;
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find groups by project ID
|
* Find groups by project ID
|
||||||
*/
|
*/
|
||||||
async findByProjectId(projectId: string) {
|
async findByProjectId(projectId: string) {
|
||||||
const groups = await this.db
|
return this.db
|
||||||
.select()
|
.select()
|
||||||
.from(schema.groups)
|
.from(schema.groups)
|
||||||
.where(eq(schema.groups.projectId, projectId));
|
.where(eq(schema.groups.projectId, projectId));
|
||||||
|
|
||||||
// Add description to each group if it exists in metadata
|
|
||||||
return groups.map(group => {
|
|
||||||
const response = { ...group };
|
|
||||||
if (group.metadata && group.metadata.description) {
|
|
||||||
response.description = group.metadata.description;
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a group by ID
|
* Find a group by ID
|
||||||
*/
|
*/
|
||||||
async findById(id: string) {
|
async findById(id: string) {
|
||||||
// Validate id
|
const [group] = await this.db
|
||||||
if (!id) {
|
.select()
|
||||||
throw new NotFoundException('Group ID is required');
|
.from(schema.groups)
|
||||||
|
.where(eq(schema.groups.id, id));
|
||||||
|
|
||||||
|
if (!group) {
|
||||||
|
throw new NotFoundException(`Group with ID ${id} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return group;
|
||||||
const [group] = await this.db
|
|
||||||
.select()
|
|
||||||
.from(schema.groups)
|
|
||||||
.where(eq(schema.groups.id, id));
|
|
||||||
|
|
||||||
if (!group) {
|
|
||||||
throw new NotFoundException(`Group with ID ${id} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add description to response if it exists in metadata
|
|
||||||
const response = { ...group };
|
|
||||||
if (group.metadata && group.metadata.description) {
|
|
||||||
response.description = group.metadata.description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
} catch (error) {
|
|
||||||
// If there's a database error (like invalid UUID format), throw a NotFoundException
|
|
||||||
throw new NotFoundException(`Group with ID ${id} not found or invalid ID format`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a group
|
* Update a group
|
||||||
*/
|
*/
|
||||||
async update(id: string, updateGroupDto: UpdateGroupDto) {
|
async update(id: string, updateGroupDto: UpdateGroupDto) {
|
||||||
// Ensure we're not losing any fields by first getting the existing group
|
|
||||||
const existingGroup = await this.findById(id);
|
|
||||||
|
|
||||||
// Extract description from DTO if present
|
|
||||||
const { description, ...restDto } = updateGroupDto;
|
|
||||||
|
|
||||||
// Prepare metadata with description if provided
|
|
||||||
let metadata = existingGroup.metadata || {};
|
|
||||||
if (description !== undefined) {
|
|
||||||
metadata = { ...metadata, description };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare the update data
|
|
||||||
const updateData = {
|
|
||||||
...restDto,
|
|
||||||
metadata,
|
|
||||||
updatedAt: new Date(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const [group] = await this.db
|
const [group] = await this.db
|
||||||
.update(schema.groups)
|
.update(schema.groups)
|
||||||
.set(updateData)
|
.set({
|
||||||
|
...updateGroupDto,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
})
|
||||||
.where(eq(schema.groups.id, id))
|
.where(eq(schema.groups.id, id))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
@ -152,13 +89,7 @@ export class GroupsService {
|
|||||||
group,
|
group,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add description to response if it exists in metadata
|
return group;
|
||||||
const response = { ...group };
|
|
||||||
if (group.metadata && group.metadata.description) {
|
|
||||||
response.description = group.metadata.description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -217,7 +148,7 @@ export class GroupsService {
|
|||||||
const [createdPerson] = await this.db
|
const [createdPerson] = await this.db
|
||||||
.insert(schema.persons)
|
.insert(schema.persons)
|
||||||
.values({
|
.values({
|
||||||
// Let the database generate the UUID automatically
|
id: user.id, // Use the same ID as the user
|
||||||
firstName: user.name.split(' ')[0] || 'Test',
|
firstName: user.name.split(' ')[0] || 'Test',
|
||||||
lastName: user.name.split(' ')[1] || 'User',
|
lastName: user.name.split(' ')[1] || 'User',
|
||||||
gender: 'MALE', // Default value for testing
|
gender: 'MALE', // Default value for testing
|
||||||
@ -253,9 +184,7 @@ export class GroupsService {
|
|||||||
.where(eq(schema.personToGroup.groupId, groupId));
|
.where(eq(schema.personToGroup.groupId, groupId));
|
||||||
|
|
||||||
if (existingRelation) {
|
if (existingRelation) {
|
||||||
// Get all persons in the group to return with the group
|
return existingRelation;
|
||||||
const persons = await this.getPersonsInGroup(groupId);
|
|
||||||
return { ...group, persons };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the person to the group
|
// Add the person to the group
|
||||||
@ -274,9 +203,7 @@ export class GroupsService {
|
|||||||
relation,
|
relation,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get all persons in the group to return with the group
|
return relation;
|
||||||
const persons = await this.getPersonsInGroup(groupId);
|
|
||||||
return { ...group, persons };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -331,9 +258,7 @@ export class GroupsService {
|
|||||||
relation,
|
relation,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get all persons in the group to return with the group
|
return relation;
|
||||||
const persons = await this.getPersonsInGroup(groupId);
|
|
||||||
return { ...group, persons };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -355,31 +280,27 @@ export class GroupsService {
|
|||||||
const personIds = personResults.map(result => result.id);
|
const personIds = personResults.map(result => result.id);
|
||||||
if (personIds.length > 0) {
|
if (personIds.length > 0) {
|
||||||
// Try to get from persons table first
|
// Try to get from persons table first
|
||||||
// Use the first ID for simplicity, but check that it's not undefined
|
const persons = await this.db
|
||||||
const firstId = personIds[0];
|
.select()
|
||||||
if (firstId) {
|
.from(schema.persons)
|
||||||
const persons = await this.db
|
.where(eq(schema.persons.id, personIds[0]));
|
||||||
.select()
|
|
||||||
.from(schema.persons)
|
|
||||||
.where(eq(schema.persons.id, firstId));
|
|
||||||
|
|
||||||
if (persons.length > 0) {
|
if (persons.length > 0) {
|
||||||
return persons;
|
return persons;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not found in persons, try users table (for e2e tests)
|
// If not found in persons, try users table (for e2e tests)
|
||||||
const users = await this.db
|
const users = await this.db
|
||||||
.select()
|
.select()
|
||||||
.from(schema.users)
|
.from(schema.users)
|
||||||
.where(eq(schema.users.id, firstId));
|
.where(eq(schema.users.id, personIds[0]));
|
||||||
|
|
||||||
if (users.length > 0) {
|
if (users.length > 0) {
|
||||||
// Convert users to the format expected by the test
|
// Convert users to the format expected by the test
|
||||||
return users.map(user => ({
|
return users.map(user => ({
|
||||||
id: user.id,
|
id: user.id,
|
||||||
name: user.name
|
name: user.name
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,15 +9,12 @@ import {
|
|||||||
HttpCode,
|
HttpCode,
|
||||||
HttpStatus,
|
HttpStatus,
|
||||||
Query,
|
Query,
|
||||||
UseGuards,
|
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { PersonsService } from '../services/persons.service';
|
import { PersonsService } from '../services/persons.service';
|
||||||
import { CreatePersonDto } from '../dto/create-person.dto';
|
import { CreatePersonDto } from '../dto/create-person.dto';
|
||||||
import { UpdatePersonDto } from '../dto/update-person.dto';
|
import { UpdatePersonDto } from '../dto/update-person.dto';
|
||||||
import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
|
|
||||||
|
|
||||||
@Controller('persons')
|
@Controller('persons')
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
export class PersonsController {
|
export class PersonsController {
|
||||||
constructor(private readonly personsService: PersonsService) {}
|
constructor(private readonly personsService: PersonsService) {}
|
||||||
|
|
||||||
@ -94,4 +91,4 @@ export class PersonsController {
|
|||||||
removeFromGroup(@Param('id') id: string, @Param('groupId') groupId: string) {
|
removeFromGroup(@Param('id') id: string, @Param('groupId') groupId: string) {
|
||||||
return this.personsService.removeFromGroup(id, groupId);
|
return this.personsService.removeFromGroup(id, groupId);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,8 +4,31 @@ import {
|
|||||||
IsOptional,
|
IsOptional,
|
||||||
IsObject,
|
IsObject,
|
||||||
IsUUID,
|
IsUUID,
|
||||||
IsArray
|
IsEnum,
|
||||||
|
IsInt,
|
||||||
|
IsBoolean,
|
||||||
|
Min,
|
||||||
|
Max
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum for gender values
|
||||||
|
*/
|
||||||
|
export enum Gender {
|
||||||
|
MALE = 'MALE',
|
||||||
|
FEMALE = 'FEMALE',
|
||||||
|
NON_BINARY = 'NON_BINARY',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum for oral ease level values
|
||||||
|
*/
|
||||||
|
export enum OralEaseLevel {
|
||||||
|
SHY = 'SHY',
|
||||||
|
RESERVED = 'RESERVED',
|
||||||
|
COMFORTABLE = 'COMFORTABLE',
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DTO for creating a new person
|
* DTO for creating a new person
|
||||||
@ -13,17 +36,48 @@ import {
|
|||||||
export class CreatePersonDto {
|
export class CreatePersonDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
name: string;
|
firstName: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
lastName: string;
|
||||||
|
|
||||||
|
@IsEnum(Gender)
|
||||||
|
@IsNotEmpty()
|
||||||
|
gender: Gender;
|
||||||
|
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
@Max(5)
|
||||||
|
@Type(() => Number)
|
||||||
|
technicalLevel: number;
|
||||||
|
|
||||||
|
@IsBoolean()
|
||||||
|
@Type(() => Boolean)
|
||||||
|
hasTechnicalTraining: boolean;
|
||||||
|
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
@Max(5)
|
||||||
|
@Type(() => Number)
|
||||||
|
frenchSpeakingLevel: number;
|
||||||
|
|
||||||
|
@IsEnum(OralEaseLevel)
|
||||||
|
@IsNotEmpty()
|
||||||
|
oralEaseLevel: OralEaseLevel;
|
||||||
|
|
||||||
|
@IsInt()
|
||||||
|
@IsOptional()
|
||||||
|
@Min(18)
|
||||||
|
@Max(100)
|
||||||
|
@Type(() => Number)
|
||||||
|
age?: number;
|
||||||
|
|
||||||
@IsUUID()
|
@IsUUID()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
|
||||||
@IsArray()
|
|
||||||
@IsOptional()
|
|
||||||
skills?: string[];
|
|
||||||
|
|
||||||
@IsObject()
|
@IsObject()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
metadata?: Record<string, any>;
|
attributes?: Record<string, any>;
|
||||||
}
|
}
|
@ -3,8 +3,14 @@ import {
|
|||||||
IsOptional,
|
IsOptional,
|
||||||
IsObject,
|
IsObject,
|
||||||
IsUUID,
|
IsUUID,
|
||||||
IsArray
|
IsEnum,
|
||||||
|
IsInt,
|
||||||
|
IsBoolean,
|
||||||
|
Min,
|
||||||
|
Max
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
|
import { Gender, OralEaseLevel } from './create-person.dto';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DTO for updating a person
|
* DTO for updating a person
|
||||||
@ -12,17 +18,51 @@ import {
|
|||||||
export class UpdatePersonDto {
|
export class UpdatePersonDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
name?: string;
|
firstName?: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
lastName?: string;
|
||||||
|
|
||||||
|
@IsEnum(Gender)
|
||||||
|
@IsOptional()
|
||||||
|
gender?: Gender;
|
||||||
|
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
@Max(5)
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
technicalLevel?: number;
|
||||||
|
|
||||||
|
@IsBoolean()
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Boolean)
|
||||||
|
hasTechnicalTraining?: boolean;
|
||||||
|
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
@Max(5)
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
frenchSpeakingLevel?: number;
|
||||||
|
|
||||||
|
@IsEnum(OralEaseLevel)
|
||||||
|
@IsOptional()
|
||||||
|
oralEaseLevel?: OralEaseLevel;
|
||||||
|
|
||||||
|
@IsInt()
|
||||||
|
@IsOptional()
|
||||||
|
@Min(18)
|
||||||
|
@Max(100)
|
||||||
|
@Type(() => Number)
|
||||||
|
age?: number;
|
||||||
|
|
||||||
@IsUUID()
|
@IsUUID()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
|
|
||||||
@IsArray()
|
|
||||||
@IsOptional()
|
|
||||||
skills?: string[];
|
|
||||||
|
|
||||||
@IsObject()
|
@IsObject()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
metadata?: Record<string, any>;
|
attributes?: Record<string, any>;
|
||||||
}
|
}
|
@ -1,10 +0,0 @@
|
|||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { PersonsController } from './controllers/persons.controller';
|
|
||||||
import { PersonsService } from './services/persons.service';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
controllers: [PersonsController],
|
|
||||||
providers: [PersonsService],
|
|
||||||
exports: [PersonsService],
|
|
||||||
})
|
|
||||||
export class PersonsModule {}
|
|
@ -13,36 +13,11 @@ export class PersonsService {
|
|||||||
* Create a new person
|
* Create a new person
|
||||||
*/
|
*/
|
||||||
async create(createPersonDto: CreatePersonDto) {
|
async create(createPersonDto: CreatePersonDto) {
|
||||||
// Map name to firstName and lastName
|
|
||||||
const nameParts = createPersonDto.name.split(' ');
|
|
||||||
const firstName = nameParts[0] || 'Unknown';
|
|
||||||
const lastName = nameParts.slice(1).join(' ') || 'Unknown';
|
|
||||||
|
|
||||||
// Set default values for required fields
|
|
||||||
const personData = {
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
gender: 'MALE', // Default value
|
|
||||||
technicalLevel: 3, // Default value
|
|
||||||
hasTechnicalTraining: true, // Default value
|
|
||||||
frenchSpeakingLevel: 5, // Default value
|
|
||||||
oralEaseLevel: 'COMFORTABLE', // Default value
|
|
||||||
projectId: createPersonDto.projectId,
|
|
||||||
attributes: createPersonDto.metadata || {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const [person] = await this.db
|
const [person] = await this.db
|
||||||
.insert(schema.persons)
|
.insert(schema.persons)
|
||||||
.values(personData)
|
.values(createPersonDto)
|
||||||
.returning();
|
.returning();
|
||||||
|
return person;
|
||||||
// Return the person with the name field for compatibility with tests
|
|
||||||
return {
|
|
||||||
...person,
|
|
||||||
name: createPersonDto.name,
|
|
||||||
skills: createPersonDto.skills || [],
|
|
||||||
metadata: createPersonDto.metadata || {},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,82 +41,36 @@ export class PersonsService {
|
|||||||
* Find a person by ID
|
* Find a person by ID
|
||||||
*/
|
*/
|
||||||
async findById(id: string) {
|
async findById(id: string) {
|
||||||
// Validate id
|
const [person] = await this.db
|
||||||
if (!id) {
|
.select()
|
||||||
throw new NotFoundException('Person ID is required');
|
.from(schema.persons)
|
||||||
}
|
.where(eq(schema.persons.id, id));
|
||||||
|
|
||||||
try {
|
if (!person) {
|
||||||
const [person] = await this.db
|
throw new NotFoundException(`Person with ID ${id} not found`);
|
||||||
.select()
|
|
||||||
.from(schema.persons)
|
|
||||||
.where(eq(schema.persons.id, id));
|
|
||||||
|
|
||||||
if (!person) {
|
|
||||||
throw new NotFoundException(`Person with ID ${id} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return person;
|
|
||||||
} catch (error) {
|
|
||||||
// If there's a database error (like invalid UUID format), throw a NotFoundException
|
|
||||||
throw new NotFoundException(`Person with ID ${id} not found or invalid ID format`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return person;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a person
|
* Update a person
|
||||||
*/
|
*/
|
||||||
async update(id: string, updatePersonDto: UpdatePersonDto) {
|
async update(id: string, updatePersonDto: UpdatePersonDto) {
|
||||||
// Validate id
|
|
||||||
if (!id) {
|
|
||||||
throw new NotFoundException('Person ID is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
// First check if the person exists
|
|
||||||
const existingPerson = await this.findById(id);
|
|
||||||
if (!existingPerson) {
|
|
||||||
throw new NotFoundException(`Person with ID ${id} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an update object with only the fields that are present
|
|
||||||
const updateData: any = {
|
|
||||||
updatedAt: new Date(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Map name to firstName and lastName if provided
|
|
||||||
if (updatePersonDto.name) {
|
|
||||||
const nameParts = updatePersonDto.name.split(' ');
|
|
||||||
updateData.firstName = nameParts[0] || 'Unknown';
|
|
||||||
updateData.lastName = nameParts.slice(1).join(' ') || 'Unknown';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add other fields if they are provided and not undefined
|
|
||||||
if (updatePersonDto.projectId !== undefined) {
|
|
||||||
updateData.projectId = updatePersonDto.projectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map metadata to attributes if provided
|
|
||||||
if (updatePersonDto.metadata) {
|
|
||||||
updateData.attributes = updatePersonDto.metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [person] = await this.db
|
const [person] = await this.db
|
||||||
.update(schema.persons)
|
.update(schema.persons)
|
||||||
.set(updateData)
|
.set({
|
||||||
|
...updatePersonDto,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
})
|
||||||
.where(eq(schema.persons.id, id))
|
.where(eq(schema.persons.id, id))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (!person) {
|
if (!person) {
|
||||||
throw new NotFoundException(`Person with ID ${id} not found`);
|
throw new NotFoundException(`Person with ID ${id} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the person with the name field for compatibility with tests
|
return person;
|
||||||
return {
|
|
||||||
...person,
|
|
||||||
name: updatePersonDto.name || `${person.firstName} ${person.lastName}`.trim(),
|
|
||||||
skills: updatePersonDto.skills || [],
|
|
||||||
metadata: person.attributes || {},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -152,11 +81,11 @@ export class PersonsService {
|
|||||||
.delete(schema.persons)
|
.delete(schema.persons)
|
||||||
.where(eq(schema.persons.id, id))
|
.where(eq(schema.persons.id, id))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (!person) {
|
if (!person) {
|
||||||
throw new NotFoundException(`Person with ID ${id} not found`);
|
throw new NotFoundException(`Person with ID ${id} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return person;
|
return person;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,149 +93,53 @@ export class PersonsService {
|
|||||||
* Find persons by project ID and group ID
|
* Find persons by project ID and group ID
|
||||||
*/
|
*/
|
||||||
async findByProjectIdAndGroupId(projectId: string, groupId: string) {
|
async findByProjectIdAndGroupId(projectId: string, groupId: string) {
|
||||||
// Validate projectId and groupId
|
return this.db
|
||||||
if (!projectId) {
|
.select({
|
||||||
throw new NotFoundException('Project ID is required');
|
person: schema.persons,
|
||||||
}
|
})
|
||||||
if (!groupId) {
|
.from(schema.persons)
|
||||||
throw new NotFoundException('Group ID is required');
|
.innerJoin(
|
||||||
}
|
schema.personToGroup,
|
||||||
|
and(
|
||||||
try {
|
eq(schema.persons.id, schema.personToGroup.personId),
|
||||||
// Check if the project exists
|
eq(schema.personToGroup.groupId, groupId)
|
||||||
const [project] = await this.db
|
|
||||||
.select()
|
|
||||||
.from(schema.projects)
|
|
||||||
.where(eq(schema.projects.id, projectId));
|
|
||||||
|
|
||||||
if (!project) {
|
|
||||||
throw new NotFoundException(`Project with ID ${projectId} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the group exists
|
|
||||||
const [group] = await this.db
|
|
||||||
.select()
|
|
||||||
.from(schema.groups)
|
|
||||||
.where(eq(schema.groups.id, groupId));
|
|
||||||
|
|
||||||
if (!group) {
|
|
||||||
throw new NotFoundException(`Group with ID ${groupId} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const results = await this.db
|
|
||||||
.select({
|
|
||||||
person: schema.persons,
|
|
||||||
})
|
|
||||||
.from(schema.persons)
|
|
||||||
.innerJoin(
|
|
||||||
schema.personToGroup,
|
|
||||||
and(
|
|
||||||
eq(schema.persons.id, schema.personToGroup.personId),
|
|
||||||
eq(schema.personToGroup.groupId, groupId)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.where(eq(schema.persons.projectId, projectId));
|
)
|
||||||
|
.where(eq(schema.persons.projectId, projectId));
|
||||||
return results.map(result => result.person);
|
|
||||||
} catch (error) {
|
|
||||||
// If there's a database error (like invalid UUID format), throw a NotFoundException
|
|
||||||
throw new NotFoundException(`Failed to find persons by project and group: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a person to a group
|
* Add a person to a group
|
||||||
*/
|
*/
|
||||||
async addToGroup(personId: string, groupId: string) {
|
async addToGroup(personId: string, groupId: string) {
|
||||||
// Validate personId and groupId
|
const [relation] = await this.db
|
||||||
if (!personId) {
|
.insert(schema.personToGroup)
|
||||||
throw new NotFoundException('Person ID is required');
|
.values({
|
||||||
}
|
personId,
|
||||||
if (!groupId) {
|
groupId,
|
||||||
throw new NotFoundException('Group ID is required');
|
})
|
||||||
}
|
.returning();
|
||||||
|
return relation;
|
||||||
try {
|
|
||||||
// Check if the person exists
|
|
||||||
const [person] = await this.db
|
|
||||||
.select()
|
|
||||||
.from(schema.persons)
|
|
||||||
.where(eq(schema.persons.id, personId));
|
|
||||||
|
|
||||||
if (!person) {
|
|
||||||
throw new NotFoundException(`Person with ID ${personId} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the group exists
|
|
||||||
const [group] = await this.db
|
|
||||||
.select()
|
|
||||||
.from(schema.groups)
|
|
||||||
.where(eq(schema.groups.id, groupId));
|
|
||||||
|
|
||||||
if (!group) {
|
|
||||||
throw new NotFoundException(`Group with ID ${groupId} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the person is already in the group
|
|
||||||
const [existingRelation] = await this.db
|
|
||||||
.select()
|
|
||||||
.from(schema.personToGroup)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(schema.personToGroup.personId, personId),
|
|
||||||
eq(schema.personToGroup.groupId, groupId)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingRelation) {
|
|
||||||
return existingRelation;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [relation] = await this.db
|
|
||||||
.insert(schema.personToGroup)
|
|
||||||
.values({
|
|
||||||
personId,
|
|
||||||
groupId,
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
return relation;
|
|
||||||
} catch (error) {
|
|
||||||
// If there's a database error (like invalid UUID format), throw a NotFoundException
|
|
||||||
throw new NotFoundException(`Failed to add person to group: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a person from a group
|
* Remove a person from a group
|
||||||
*/
|
*/
|
||||||
async removeFromGroup(personId: string, groupId: string) {
|
async removeFromGroup(personId: string, groupId: string) {
|
||||||
// Validate personId and groupId
|
const [relation] = await this.db
|
||||||
if (!personId) {
|
.delete(schema.personToGroup)
|
||||||
throw new NotFoundException('Person ID is required');
|
.where(
|
||||||
}
|
and(
|
||||||
if (!groupId) {
|
eq(schema.personToGroup.personId, personId),
|
||||||
throw new NotFoundException('Group ID is required');
|
eq(schema.personToGroup.groupId, groupId)
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const [relation] = await this.db
|
|
||||||
.delete(schema.personToGroup)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(schema.personToGroup.personId, personId),
|
|
||||||
eq(schema.personToGroup.groupId, groupId)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.returning();
|
)
|
||||||
|
.returning();
|
||||||
if (!relation) {
|
|
||||||
throw new NotFoundException(`Person with ID ${personId} not found in group with ID ${groupId}`);
|
if (!relation) {
|
||||||
}
|
throw new NotFoundException(`Person with ID ${personId} not found in group with ID ${groupId}`);
|
||||||
|
|
||||||
return relation;
|
|
||||||
} catch (error) {
|
|
||||||
// If there's a database error (like invalid UUID format), throw a NotFoundException
|
|
||||||
throw new NotFoundException(`Failed to remove person from group: ${error.message}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return relation;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -229,29 +229,16 @@ export class ProjectsService {
|
|||||||
* Get all collaborators for a project
|
* Get all collaborators for a project
|
||||||
*/
|
*/
|
||||||
async getCollaborators(projectId: string) {
|
async getCollaborators(projectId: string) {
|
||||||
// Validate projectId
|
// Check if the project exists
|
||||||
if (!projectId) {
|
await this.findById(projectId);
|
||||||
throw new NotFoundException('Project ID is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
// Get all collaborators for the project
|
||||||
// Check if the project exists
|
return this.db
|
||||||
await this.findById(projectId);
|
.select({
|
||||||
|
user: schema.users,
|
||||||
// Get all collaborators for the project
|
})
|
||||||
const collaborators = await this.db
|
.from(schema.projectCollaborators)
|
||||||
.select({
|
.innerJoin(schema.users, eq(schema.projectCollaborators.userId, schema.users.id))
|
||||||
user: schema.users,
|
.where(eq(schema.projectCollaborators.projectId, projectId));
|
||||||
})
|
|
||||||
.from(schema.projectCollaborators)
|
|
||||||
.innerJoin(schema.users, eq(schema.projectCollaborators.userId, schema.users.id))
|
|
||||||
.where(eq(schema.projectCollaborators.projectId, projectId));
|
|
||||||
|
|
||||||
// Map the results to extract just the user objects
|
|
||||||
return collaborators.map(collaborator => collaborator.user);
|
|
||||||
} catch (error) {
|
|
||||||
// If there's a database error (like invalid UUID format), throw a NotFoundException
|
|
||||||
throw new NotFoundException(`Failed to get collaborators for project: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Injectable, NotFoundException, Inject, BadRequestException } from '@nestjs/common';
|
import { Injectable, NotFoundException, Inject } 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,30 +95,22 @@ 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 BadRequestException(`Tag with ID ${tagId} is not of type PERSON`);
|
throw new Error(`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()
|
||||||
@ -129,11 +121,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)
|
||||||
@ -142,7 +134,7 @@ export class TagsService {
|
|||||||
tagId,
|
tagId,
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
return relation;
|
return relation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,14 +142,6 @@ 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(
|
||||||
@ -167,11 +151,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,30 +163,22 @@ 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 BadRequestException(`Tag with ID ${tagId} is not of type PROJECT`);
|
throw new Error(`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()
|
||||||
@ -213,11 +189,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)
|
||||||
@ -226,7 +202,7 @@ export class TagsService {
|
|||||||
tagId,
|
tagId,
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
return relation;
|
return relation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,14 +210,6 @@ 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(
|
||||||
@ -251,11 +219,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,21 +231,16 @@ 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({
|
||||||
@ -292,21 +255,16 @@ 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({
|
||||||
@ -316,4 +274,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -38,11 +38,11 @@ export class UsersService {
|
|||||||
.select()
|
.select()
|
||||||
.from(schema.users)
|
.from(schema.users)
|
||||||
.where(eq(schema.users.id, id));
|
.where(eq(schema.users.id, id));
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new NotFoundException(`User with ID ${id} not found`);
|
throw new NotFoundException(`User with ID ${id} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ export class UsersService {
|
|||||||
.select()
|
.select()
|
||||||
.from(schema.users)
|
.from(schema.users)
|
||||||
.where(eq(schema.users.githubId, githubId));
|
.where(eq(schema.users.githubId, githubId));
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,11 +70,11 @@ export class UsersService {
|
|||||||
})
|
})
|
||||||
.where(eq(schema.users.id, id))
|
.where(eq(schema.users.id, id))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new NotFoundException(`User with ID ${id} not found`);
|
throw new NotFoundException(`User with ID ${id} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,11 +86,11 @@ export class UsersService {
|
|||||||
.delete(schema.users)
|
.delete(schema.users)
|
||||||
.where(eq(schema.users.id, id))
|
.where(eq(schema.users.id, id))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new NotFoundException(`User with ID ${id} not found`);
|
throw new NotFoundException(`User with ID ${id} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,12 +98,7 @@ export class UsersService {
|
|||||||
* Update GDPR consent timestamp
|
* Update GDPR consent timestamp
|
||||||
*/
|
*/
|
||||||
async updateGdprConsent(id: string) {
|
async updateGdprConsent(id: string) {
|
||||||
const user = await this.update(id, { gdprTimestamp: new Date() });
|
return this.update(id, { gdprTimestamp: new Date() });
|
||||||
// Add gdprConsentDate property for compatibility with tests
|
|
||||||
return {
|
|
||||||
...user,
|
|
||||||
gdprConsentDate: user.gdprTimestamp
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -115,13 +110,10 @@ export class UsersService {
|
|||||||
.select()
|
.select()
|
||||||
.from(schema.projects)
|
.from(schema.projects)
|
||||||
.where(eq(schema.projects.ownerId, id));
|
.where(eq(schema.projects.ownerId, id));
|
||||||
|
|
||||||
// Add empty groups and persons arrays for compatibility with tests
|
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
projects,
|
projects,
|
||||||
groups: [],
|
|
||||||
persons: []
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,416 +0,0 @@
|
|||||||
import { INestApplication } from '@nestjs/common';
|
|
||||||
import * as request from 'supertest';
|
|
||||||
import { createTestApp, createTestUser, generateTokensForUser, cleanupTestData } from './test-utils';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
import { DRIZZLE } from '../src/database/database.module';
|
|
||||||
import * as schema from '../src/database/schema';
|
|
||||||
import { eq, and } from 'drizzle-orm';
|
|
||||||
|
|
||||||
describe('TagsController (e2e)', () => {
|
|
||||||
let app: INestApplication;
|
|
||||||
let accessToken: string;
|
|
||||||
let testUser: any;
|
|
||||||
let testUserId: string;
|
|
||||||
let db: any;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
app = await createTestApp();
|
|
||||||
|
|
||||||
// Get the DrizzleORM instance
|
|
||||||
db = app.get(DRIZZLE);
|
|
||||||
|
|
||||||
// Create a test user and generate tokens
|
|
||||||
testUser = await createTestUser(app);
|
|
||||||
testUserId = testUser.id;
|
|
||||||
const tokens = await generateTokensForUser(app, testUserId);
|
|
||||||
accessToken = tokens.accessToken;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
// Clean up test data
|
|
||||||
await cleanupTestData(app, testUserId);
|
|
||||||
await app.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Tag CRUD operations', () => {
|
|
||||||
let createdTag: any;
|
|
||||||
const testTagData = {
|
|
||||||
name: `Test Tag ${uuidv4().substring(0, 8)}`,
|
|
||||||
color: '#FF5733',
|
|
||||||
type: 'PERSON'
|
|
||||||
};
|
|
||||||
|
|
||||||
// Clean up any test tags after tests
|
|
||||||
afterAll(async () => {
|
|
||||||
if (createdTag?.id) {
|
|
||||||
try {
|
|
||||||
await db.delete(schema.tags).where(eq(schema.tags.id, createdTag.id));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to clean up test tag:', error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create a new tag', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.post('/api/tags')
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.send(testTagData)
|
|
||||||
.expect(201)
|
|
||||||
.expect((res) => {
|
|
||||||
expect(res.body).toHaveProperty('id');
|
|
||||||
expect(res.body.name).toBe(testTagData.name);
|
|
||||||
expect(res.body.color).toBe(testTagData.color);
|
|
||||||
expect(res.body.type).toBe(testTagData.type);
|
|
||||||
createdTag = res.body;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get all tags', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.get('/api/tags')
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
expect(Array.isArray(res.body)).toBe(true);
|
|
||||||
expect(res.body.length).toBeGreaterThan(0);
|
|
||||||
expect(res.body.some(tag => tag.id === createdTag.id)).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get tags by type', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.get('/api/tags?type=PERSON')
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
expect(Array.isArray(res.body)).toBe(true);
|
|
||||||
expect(res.body.every(tag => tag.type === 'PERSON')).toBe(true);
|
|
||||||
expect(res.body.some(tag => tag.id === createdTag.id)).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get a tag by ID', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.get(`/api/tags/${createdTag.id}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
expect(res.body).toHaveProperty('id', createdTag.id);
|
|
||||||
expect(res.body.name).toBe(createdTag.name);
|
|
||||||
expect(res.body.color).toBe(createdTag.color);
|
|
||||||
expect(res.body.type).toBe(createdTag.type);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update a tag', () => {
|
|
||||||
const updateData = {
|
|
||||||
name: `Updated Tag ${uuidv4().substring(0, 8)}`,
|
|
||||||
color: '#33FF57'
|
|
||||||
};
|
|
||||||
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.put(`/api/tags/${createdTag.id}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.send(updateData)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
expect(res.body).toHaveProperty('id', createdTag.id);
|
|
||||||
expect(res.body.name).toBe(updateData.name);
|
|
||||||
expect(res.body.color).toBe(updateData.color);
|
|
||||||
expect(res.body.type).toBe(createdTag.type); // Type should remain unchanged
|
|
||||||
|
|
||||||
// Update the createdTag reference for subsequent tests
|
|
||||||
createdTag = res.body;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return 404 when getting a non-existent tag', () => {
|
|
||||||
const nonExistentId = uuidv4();
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.get(`/api/tags/${nonExistentId}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(404);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return 404 when updating a non-existent tag', () => {
|
|
||||||
const nonExistentId = uuidv4();
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.put(`/api/tags/${nonExistentId}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.send({ name: 'Updated Tag' })
|
|
||||||
.expect(404);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Tag relations with persons', () => {
|
|
||||||
let personTag: any;
|
|
||||||
let testPerson: any;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
// Create a test tag for persons
|
|
||||||
const [tag] = await db
|
|
||||||
.insert(schema.tags)
|
|
||||||
.values({
|
|
||||||
name: `Person Tag ${uuidv4().substring(0, 8)}`,
|
|
||||||
color: '#3366FF',
|
|
||||||
type: 'PERSON'
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
personTag = tag;
|
|
||||||
|
|
||||||
// Create a test project first (needed for person)
|
|
||||||
const [project] = await db
|
|
||||||
.insert(schema.projects)
|
|
||||||
.values({
|
|
||||||
name: `Test Project ${uuidv4().substring(0, 8)}`,
|
|
||||||
description: 'A test project for e2e tests',
|
|
||||||
ownerId: testUserId
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
// Create a test person
|
|
||||||
const [person] = await db
|
|
||||||
.insert(schema.persons)
|
|
||||||
.values({
|
|
||||||
firstName: `Test ${uuidv4().substring(0, 8)}`,
|
|
||||||
lastName: `Person ${uuidv4().substring(0, 8)}`,
|
|
||||||
gender: 'MALE',
|
|
||||||
technicalLevel: 3,
|
|
||||||
hasTechnicalTraining: true,
|
|
||||||
frenchSpeakingLevel: 4,
|
|
||||||
oralEaseLevel: 'COMFORTABLE',
|
|
||||||
projectId: project.id
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
testPerson = person;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
// Clean up test data
|
|
||||||
if (personTag?.id) {
|
|
||||||
try {
|
|
||||||
await db.delete(schema.tags).where(eq(schema.tags.id, personTag.id));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to clean up test tag:', error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (testPerson?.id) {
|
|
||||||
try {
|
|
||||||
await db.delete(schema.persons).where(eq(schema.persons.id, testPerson.id));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to clean up test person:', error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add a tag to a person', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.post(`/api/tags/persons/${testPerson.id}/tags/${personTag.id}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(201)
|
|
||||||
.expect((res) => {
|
|
||||||
expect(res.body).toHaveProperty('personId', testPerson.id);
|
|
||||||
expect(res.body).toHaveProperty('tagId', personTag.id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get all tags for a person', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.get(`/api/tags/persons/${testPerson.id}/tags`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
expect(Array.isArray(res.body)).toBe(true);
|
|
||||||
expect(res.body.length).toBeGreaterThan(0);
|
|
||||||
expect(res.body.some(item => item.tag.id === personTag.id)).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove a tag from a person', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.delete(`/api/tags/persons/${testPerson.id}/tags/${personTag.id}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
expect(res.body).toHaveProperty('personId', testPerson.id);
|
|
||||||
expect(res.body).toHaveProperty('tagId', personTag.id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return 404 when adding a tag to a non-existent person', () => {
|
|
||||||
const nonExistentId = uuidv4();
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.post(`/api/tags/persons/${nonExistentId}/tags/${personTag.id}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(404);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return 400 when adding a project tag to a person', async () => {
|
|
||||||
// Create a project tag
|
|
||||||
const [projectTag] = await db
|
|
||||||
.insert(schema.tags)
|
|
||||||
.values({
|
|
||||||
name: `Project Tag ${uuidv4().substring(0, 8)}`,
|
|
||||||
color: '#FF3366',
|
|
||||||
type: 'PROJECT'
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
const response = await request(app.getHttpServer())
|
|
||||||
.post(`/api/tags/persons/${testPerson.id}/tags/${projectTag.id}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(400);
|
|
||||||
|
|
||||||
// Clean up the project tag
|
|
||||||
await db.delete(schema.tags).where(eq(schema.tags.id, projectTag.id));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Tag relations with projects', () => {
|
|
||||||
let projectTag: any;
|
|
||||||
let testProject: any;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
// Create a test tag for projects
|
|
||||||
const [tag] = await db
|
|
||||||
.insert(schema.tags)
|
|
||||||
.values({
|
|
||||||
name: `Project Tag ${uuidv4().substring(0, 8)}`,
|
|
||||||
color: '#33FFCC',
|
|
||||||
type: 'PROJECT'
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
projectTag = tag;
|
|
||||||
|
|
||||||
// Create a test project
|
|
||||||
const [project] = await db
|
|
||||||
.insert(schema.projects)
|
|
||||||
.values({
|
|
||||||
name: `Test Project ${uuidv4().substring(0, 8)}`,
|
|
||||||
description: 'A test project for e2e tests',
|
|
||||||
ownerId: testUserId
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
testProject = project;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
// Clean up test data
|
|
||||||
if (projectTag?.id) {
|
|
||||||
try {
|
|
||||||
await db.delete(schema.tags).where(eq(schema.tags.id, projectTag.id));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to clean up test tag:', error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (testProject?.id) {
|
|
||||||
try {
|
|
||||||
await db.delete(schema.projects).where(eq(schema.projects.id, testProject.id));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to clean up test project:', error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add a tag to a project', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.post(`/api/tags/projects/${testProject.id}/tags/${projectTag.id}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(201)
|
|
||||||
.expect((res) => {
|
|
||||||
expect(res.body).toHaveProperty('projectId', testProject.id);
|
|
||||||
expect(res.body).toHaveProperty('tagId', projectTag.id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get all tags for a project', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.get(`/api/tags/projects/${testProject.id}/tags`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
expect(Array.isArray(res.body)).toBe(true);
|
|
||||||
expect(res.body.length).toBeGreaterThan(0);
|
|
||||||
expect(res.body.some(item => item.tag.id === projectTag.id)).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove a tag from a project', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.delete(`/api/tags/projects/${testProject.id}/tags/${projectTag.id}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
expect(res.body).toHaveProperty('projectId', testProject.id);
|
|
||||||
expect(res.body).toHaveProperty('tagId', projectTag.id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return 404 when adding a tag to a non-existent project', () => {
|
|
||||||
const nonExistentId = uuidv4();
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.post(`/api/tags/projects/${nonExistentId}/tags/${projectTag.id}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(404);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return 400 when adding a person tag to a project', async () => {
|
|
||||||
// Create a person tag
|
|
||||||
const [personTag] = await db
|
|
||||||
.insert(schema.tags)
|
|
||||||
.values({
|
|
||||||
name: `Person Tag ${uuidv4().substring(0, 8)}`,
|
|
||||||
color: '#CCFF33',
|
|
||||||
type: 'PERSON'
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
const response = await request(app.getHttpServer())
|
|
||||||
.post(`/api/tags/projects/${testProject.id}/tags/${personTag.id}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(400);
|
|
||||||
|
|
||||||
// Clean up the person tag
|
|
||||||
await db.delete(schema.tags).where(eq(schema.tags.id, personTag.id));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Tag deletion', () => {
|
|
||||||
let tagToDelete: any;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
// Create a new tag to delete
|
|
||||||
const [tag] = await db
|
|
||||||
.insert(schema.tags)
|
|
||||||
.values({
|
|
||||||
name: `Tag to Delete ${uuidv4().substring(0, 8)}`,
|
|
||||||
color: '#FF99CC',
|
|
||||||
type: 'PERSON'
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
tagToDelete = tag;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should delete a tag', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.delete(`/api/tags/${tagToDelete.id}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(200)
|
|
||||||
.expect((res) => {
|
|
||||||
expect(res.body).toHaveProperty('id', tagToDelete.id);
|
|
||||||
expect(res.body.name).toBe(tagToDelete.name);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return 404 when deleting a non-existent tag', () => {
|
|
||||||
const nonExistentId = uuidv4();
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.delete(`/api/tags/${nonExistentId}`)
|
|
||||||
.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
.expect(404);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
x
Reference in New Issue
Block a user