Skip to content

Commit

Permalink
Merge pull request #8808 from ever-co/fix/alter-mention-entity
Browse files Browse the repository at this point in the history
[Fix] Alter "Mention" Entity Table
  • Loading branch information
rahul-rocket authored Feb 27, 2025
2 parents c17a13e + 1227af9 commit 590cf1c
Show file tree
Hide file tree
Showing 14 changed files with 537 additions and 114 deletions.
13 changes: 8 additions & 5 deletions packages/contracts/src/lib/employee-notification.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ export interface IEmployeeNotification extends IBasePerEntityType {
title?: string;
message?: string;
type?: EmployeeNotificationTypeEnum;
sentById?: ID;
sentBy?: IEmployee;
isRead?: boolean;
readAt?: Date;
onHoldUntil?: Date;
receiverId?: ID;
receiver?: IEmployee;
sentByEmployeeId?: ID;
sentByEmployee?: IEmployee;
receiverEmployeeId?: ID;
receiverEmployee?: IEmployee;
}

/**
Expand Down Expand Up @@ -56,7 +56,10 @@ export interface IEmployeeNotificationCreateInput extends OmitFields<IEmployeeNo
* to prevent altering relationships after creation.
*/
export interface IEmployeeNotificationUpdateInput
extends OmitFields<IEmployeeNotification, 'receiverId' | 'receiver' | 'sentById' | 'sentBy'> {}
extends OmitFields<
IEmployeeNotification,
'receiverEmployee' | 'receiverEmployeeId' | 'sentByEmployee' | 'sentByEmployeeId'
> {}

/**
* Interface representing the result of marking notifications as read.
Expand Down
15 changes: 7 additions & 8 deletions packages/contracts/src/lib/mention.model.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { BaseEntityEnum, IBasePerEntityType, ID } from './base-entity.model';
import { IUser } from './user.model';
import { ActorTypeEnum, BaseEntityEnum, IBasePerEntityType, ID, OmitFields } from './base-entity.model';
import { IEmployee, IEmployeeEntityInput as IMentionAuthor } from './employee.model';

export interface IMention extends IBasePerEntityType {
mentionedUserId: ID;
mentionedUser?: IUser;
mentionById: ID;
mentionBy?: IUser;
export interface IMention extends IBasePerEntityType, IMentionAuthor {
actorType?: ActorTypeEnum;
parentEntityId?: ID; // E.g : If the mention is in a comment, we need this for subscription and notifications purpose (It could be the task ID concerned by comment, then the user will be subscribed to that task instead of to a comment itself)
parentEntityType?: BaseEntityEnum;
mentionedEmployee?: IEmployee;
mentionedEmployeeId?: ID;
}

export interface IMentionCreateInput extends Omit<IMention, 'mentionBy'> {
export interface IMentionCreateInput extends OmitFields<IMention> {
entityName?: string;
}

Expand Down
5 changes: 2 additions & 3 deletions packages/core/src/lib/comment/comment.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,14 @@ export class CommentService extends TenantAwareCrudService<Comment> {

// Publish mentions for each mentioned employee, if any.
await Promise.all(
mentionEmployeeIds.map((mentionedUserId) =>
mentionEmployeeIds.map((mentionedEmployeeId: ID) =>
this._mentionService.publishMention({
entity: BaseEntityEnum.Comment,
entityId: comment.id,
entityName: input.entityName,
mentionedUserId,
mentionById: employee.id,
parentEntityId: comment.entityId,
parentEntityType: comment.entity,
mentionedEmployeeId,
organizationId: comment.organizationId,
tenantId: comment.tenantId
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ export class DashboardWidgetService extends TenantAwareCrudService<DashboardWidg
*/
async create(input: IDashboardWidgetCreateInput): Promise<DashboardWidget> {
try {
const employeeId = input.employeeId || RequestContext.currentEmployeeId();
const tenantId = RequestContext.currentTenantId();
const { organizationId } = input;
const user = RequestContext.currentUser();
const tenantId = RequestContext.currentTenantId() ?? input.tenantId;
const { organizationId, employeeId = user?.employeeId } = input;

// create dashboard widget
const dashboardWidget = await super.create({
Expand Down Expand Up @@ -72,7 +72,7 @@ export class DashboardWidgetService extends TenantAwareCrudService<DashboardWidg
* @returns {Promise<DashboardWidget>} The updated dashboard widget
*/
async update(id: ID, input: IDashboardWidgetUpdateInput): Promise<DashboardWidget> {
const tenantId = RequestContext.currentTenantId() || input.tenantId;
const tenantId = RequestContext.currentTenantId() ?? input.tenantId;

try {
const { organizationId } = input;
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ export class EmployeeNotificationSettingService extends TenantAwareCrudService<E
*/
async create(input: IEmployeeNotificationSettingCreateInput): Promise<EmployeeNotificationSetting> {
try {
const tenantId = RequestContext.currentTenantId() || input.tenantId;
const employeeId = input.employeeId || RequestContext.currentEmployeeId();
const user = RequestContext.currentUser();
const tenantId = RequestContext.currentTenantId() ?? input.tenantId;
const employeeId = input.employeeId ?? user?.employeeId;

return super.create({ ...input, employeeId, tenantId });
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ export class EmployeeNotificationService extends TenantAwareCrudService<Employee
async create(input: IEmployeeNotificationCreateInput): Promise<IEmployeeNotification | undefined> {
try {
// Retrieve the current tenant ID from the request context or use the provided tenantId
const tenantId = RequestContext.currentTenantId() || input.tenantId;
const tenantId = RequestContext.currentTenantId() ?? input.tenantId;
const organizationId = input.organizationId;
const employeeId = input.receiverId;
const employeeId = input.receiverEmployeeId;

// Search for the receiver notification setting
let employeeNotificationSetting: IEmployeeNotificationSetting;
Expand All @@ -57,14 +57,14 @@ export class EmployeeNotificationService extends TenantAwareCrudService<Employee
} catch (error) {
if (error instanceof NotFoundException) {
employeeNotificationSetting = await this._employeeNotificationSettingService.create({
employeeId,
assignment: true,
comment: true,
invitation: true,
mention: true,
message: true,
payment: true,
preferences: { email: true, inApp: true },
employeeId,
organizationId,
tenantId
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Logger } from '@nestjs/common';
import { BadRequestException, Logger } from '@nestjs/common';
import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
import { EmployeeCreateNotificationEvent } from '../employee-notification.event';
import { EmployeeNotificationService } from '../../employee-notification.service';
Expand All @@ -18,11 +18,11 @@ export class EmployeeCreateNotificationEventHandler implements IEventHandler<Emp
*/
async handle(event: EmployeeCreateNotificationEvent) {
try {
this.logger.debug(`Creating notification for employee: ${event.input.receiverId}`);
this.logger.debug(`Creating notification for employee: ${event.input.receiverEmployeeId}`);
return await this.employeeNotificationService.create(event.input);
} catch (error) {
this.logger.error(`Failed to create notification: ${error.message}`, error.stack);
throw new Error(`Failed to create employee notification: ${error.message}`);
throw new BadRequestException(`Failed to create employee notification: ${error.message}`, error);
}
}
}
12 changes: 9 additions & 3 deletions packages/core/src/lib/mention/events/handlers/mention.handler.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { BadRequestException } from '@nestjs/common';
import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
import { IMention } from '@gauzy/contracts';
import { CreateMentionEvent } from '../mention.event';
Expand All @@ -15,8 +16,13 @@ export class CreateMentionEventHandler implements IEventHandler<CreateMentionEve
*
*/
async handle(event: CreateMentionEvent): Promise<IMention> {
// Extract the input from the event.
const { input } = event;
return await this.mentionService.create(input);
try {
// Extract the input from the event.
const { input } = event;
return await this.mentionService.create(input);
} catch (error) {
console.log(`Error while creating mention: ${error.message}`, error);
throw new BadRequestException('Error while creating mention', error);
}
}
}
69 changes: 40 additions & 29 deletions packages/core/src/lib/mention/mention.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,41 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { EntityRepositoryType } from '@mikro-orm/core';
import { JoinColumn, RelationId } from 'typeorm';
import { IsEnum, IsNotEmpty, IsOptional, IsUUID } from 'class-validator';
import { BaseEntityEnum, ID, IMention, IUser } from '@gauzy/contracts';
import { BasePerEntityType, User } from '../core/entities/internal';
import { IsEnum, IsNotEmpty, IsObject, IsOptional, IsUUID } from 'class-validator';
import { ActorTypeEnum, BaseEntityEnum, ID, IEmployee, IMention } from '@gauzy/contracts';
import { BasePerEntityType, Employee } from '../core/entities/internal';
import { ColumnIndex, MultiORMColumn, MultiORMEntity, MultiORMManyToOne } from '../core/decorators/entity';
import { ActorTypeTransformer } from '../shared/pipes';
import { MikroOrmMentionRepository } from './repository/mikro-orm-mention.repository';

@MultiORMEntity('mention', { mikroOrmRepository: () => MikroOrmMentionRepository })
export class Mention extends BasePerEntityType implements IMention {
[EntityRepositoryType]?: MikroOrmMentionRepository;

// Indicate the actor type
@ApiPropertyOptional({ enum: ActorTypeEnum })
@IsOptional()
@IsEnum(ActorTypeEnum)
@ColumnIndex()
@MultiORMColumn({ type: 'int', nullable: true, transformer: new ActorTypeTransformer() })
actorType?: ActorTypeEnum; // Will be stored as 0 or 1 in DB

/**
* The parent entity ID
*
* E.g : If the user was mentioned is in a comment, we need this for subscription and notifications purpose (It could be the `task ID` concerned by comment, then the user will be subscribed to that task instead of to a comment itself because in this case, `entityId` will store the comment ID)
*/
@ApiProperty({ type: () => String })
@ApiPropertyOptional({ type: () => String })
@IsOptional()
@IsUUID()
@ColumnIndex()
@MultiORMColumn({ nullable: true })
parentEntityId?: ID;

@ApiProperty({ type: () => String, enum: BaseEntityEnum })
/**
* The type of the parent entity (optional)
*/
@ApiPropertyOptional({ type: () => String, enum: BaseEntityEnum })
@IsOptional()
@IsEnum(BaseEntityEnum)
@ColumnIndex()
Expand All @@ -36,50 +48,49 @@ export class Mention extends BasePerEntityType implements IMention {
| @ManyToOne
|--------------------------------------------------------------------------
*/

/**
* Mentioned User
* The employee whom to be mentioned
*/
@ApiPropertyOptional({ type: () => Object })
@IsOptional()
@MultiORMManyToOne(() => User, {
/** Indicates if relation column value can be nullable or not. */
nullable: true,

/** Database cascade action on delete. */
onDelete: 'CASCADE'
@IsObject()
@MultiORMManyToOne(() => Employee, {
onDelete: 'CASCADE' // Database cascade action on delete.
})
@JoinColumn()
mentionedUser?: IUser;
mentionedEmployee?: IEmployee;

/**
* The ID of the employee who is mentioned
*/
@ApiProperty({ type: () => String })
@IsNotEmpty()
@IsUUID()
@RelationId((it: Mention) => it.mentionedUser)
@RelationId((it: Mention) => it.mentionedEmployee)
@ColumnIndex()
@MultiORMColumn({ relationId: true })
mentionedUserId: ID;
mentionedEmployeeId?: ID;

/**
* The User that mentioned another
* The employee who mentioned the other employee
*/
@ApiPropertyOptional({ type: () => Object })
@IsOptional()
@MultiORMManyToOne(() => User, {
/** Indicates if relation column value can be nullable or not. */
nullable: true,

/** Database cascade action on delete. */
onDelete: 'CASCADE'
@MultiORMManyToOne(() => Employee, {
nullable: true, // Indicates if relation column value can be nullable or not.
onDelete: 'CASCADE' // Database cascade action on delete.
})
@JoinColumn()
mentionBy?: IUser;
employee?: IEmployee;

@ApiProperty({ type: () => String })
@IsNotEmpty()
/**
* The ID of the employee who mentioned another employee
*/
@ApiPropertyOptional({ type: () => String })
@IsOptional()
@IsUUID()
@RelationId((it: Mention) => it.mentionBy)
@RelationId((it: Mention) => it.employee)
@ColumnIndex()
@MultiORMColumn({ relationId: true })
mentionById: ID;
@MultiORMColumn({ nullable: true, relationId: true })
employeeId?: ID;
}
Loading

0 comments on commit 590cf1c

Please sign in to comment.