Skip to content

Commit

Permalink
Merge pull request #8201 from ever-co/798-activities-history
Browse files Browse the repository at this point in the history
[Feature] Activities Logs entity and model for User / Organization / Team, etc.
  • Loading branch information
rahul-rocket authored Sep 22, 2024
2 parents 2ae5096 + e1bfa0d commit c61f9ae
Show file tree
Hide file tree
Showing 9 changed files with 424 additions and 0 deletions.
49 changes: 49 additions & 0 deletions packages/contracts/src/activity-log.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { ActorTypeEnum, IBasePerTenantAndOrganizationEntityModel, ID } from './base-entity.model';
import { IUser } from './user.model';

export interface IActivityLog extends IBasePerTenantAndOrganizationEntityModel {
entity: ActivityLogEntityEnum; // Entity / Table name concerned by activity log
entityId: ID; // The ID of the element we are interacting with (a task, an organization, an employee, ...)
action: ActionTypeEnum;
actorType?: ActorTypeEnum;
description?: string; // A short sentence describing the action performed. (E.g John Doe created this on 22.09.2024)
updatedFields?: string[]; // In case of update actions, which entity fields was modified simultaneously. Avoid multiple records. (E.g For task : ['name', 'members', 'projectId'])
previousValues?: IActivityLogUpdatedValues[]; // Values before update (E.g For task : {title: ' First Task', members: ['Member1Name', 'Member2Name'], projectId: 'project1UUId'})
updatedValues?: IActivityLogUpdatedValues[]; // Values after update (E.g For task : {title: ' First Task Updated', members: ['Member4Name', 'Member3Name'], projectId: 'project2UUId'})
previousEntities?: IActivityLogUpdatedValues[]; // Stores previous IDs or other values for related entities. Eg : {members: ['member_1_ID', 'member_2_ID']}
updatedEntities?: IActivityLogUpdatedValues[]; // Stores updated IDs, or other values for related entities. Eg : {members: ['member_1_ID', 'member_2_ID']},
creator?: IUser;
creatorId?: ID;
data?: Record<string, any>;
}

export enum ActionTypeEnum {
CREATED = 'Created',
UPDATED = 'Updated',
DELETED = 'Deleted'
}

export interface IActivityLogUpdatedValues {
[x: string]: any;
}

export enum ActivityLogEntityEnum {
Candidate = 'Candidate',
Contact = 'Contact',
Employee = 'Employee',
Expense = 'Expense',
DailyPlan = 'DailyPlan',
Invoice = 'Invoice',
Income = 'Income',
Organization = 'Organization',
OrganizationContact = 'OrganizationContact',
OrganizationDepartment = 'OrganizationDepartment',
OrganizationDocument = 'OrganizationDocument',
OrganizationProject = 'OrganizationProject',
OrganizationTeam = 'OrganizationTeam',
OrganizationProjectModule = 'OrganizationProjectModule',
OrganizationSprint = 'OrganizationSprint',
Task = 'Task',
User = 'User'
// Add other entities as we can to use them for activity history
}
1 change: 1 addition & 0 deletions packages/contracts/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './accounting-template.model';
/** App Setting Model */
export * from './activity-log.model';
export * from './app.model';
export * from './appointment-employees.model';
export * from './approval-policy.model';
Expand Down
121 changes: 121 additions & 0 deletions packages/core/src/activity-log/activity-log.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { EntityRepositoryType } from '@mikro-orm/core';
import { IsArray, IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator';
import { isMySQL, isPostgres } from '@gauzy/config';
import {
ActivityLogEntityEnum,
ActionTypeEnum,
ActorTypeEnum,
IActivityLog,
IActivityLogUpdatedValues,
ID,
IUser
} from '@gauzy/contracts';
import { TenantOrganizationBaseEntity, User } from '../core/entities/internal';
import { ColumnIndex, MultiORMColumn, MultiORMEntity, MultiORMManyToOne } from '../core/decorators/entity';
import { MikroOrmActivityLogRepository } from './repository/mikro-orm-activity-log.repository';
import { JoinColumn, RelationId } from 'typeorm';

@MultiORMEntity('activity_log', { mikroOrmRepository: () => MikroOrmActivityLogRepository })
export class ActivityLog extends TenantOrganizationBaseEntity implements IActivityLog {
[EntityRepositoryType]?: MikroOrmActivityLogRepository;

@ApiProperty({ type: () => String, enum: ActivityLogEntityEnum })
@IsNotEmpty()
@IsEnum(ActivityLogEntityEnum)
@ColumnIndex()
@MultiORMColumn()
entity: ActivityLogEntityEnum;

@ApiProperty({ type: () => String })
@IsNotEmpty()
@IsUUID()
@ColumnIndex()
@MultiORMColumn()
entityId: string;

@ApiProperty({ type: () => String, enum: ActionTypeEnum })
@IsNotEmpty()
@IsEnum(ActionTypeEnum)
@ColumnIndex()
@MultiORMColumn()
action: ActionTypeEnum;

@ApiPropertyOptional({ type: () => String, enum: ActorTypeEnum })
@IsOptional()
@IsEnum(ActorTypeEnum)
@ColumnIndex()
@MultiORMColumn({ nullable: true })
actorType?: ActorTypeEnum;

@ApiPropertyOptional({ type: () => String })
@IsOptional()
@IsString()
@MultiORMColumn({ type: 'text', nullable: true })
description?: string;

@ApiPropertyOptional({ type: () => Array })
@IsOptional()
@IsArray()
@MultiORMColumn({ type: isPostgres() ? 'jsonb' : isMySQL() ? 'json' : 'text', nullable: true })
updatedFields?: string[];

@ApiPropertyOptional({ type: () => Array })
@IsOptional()
@IsArray()
@MultiORMColumn({ type: isPostgres() ? 'jsonb' : isMySQL() ? 'json' : 'text', nullable: true })
previousValues?: IActivityLogUpdatedValues[];

@ApiPropertyOptional({ type: () => Array })
@IsOptional()
@IsArray()
@MultiORMColumn({ type: isPostgres() ? 'jsonb' : isMySQL() ? 'json' : 'text', nullable: true })
updatedValues?: IActivityLogUpdatedValues[];

@ApiPropertyOptional({ type: () => Array })
@IsOptional()
@IsArray()
@MultiORMColumn({ type: isPostgres() ? 'jsonb' : isMySQL() ? 'json' : 'text', nullable: true })
previousEntities?: IActivityLogUpdatedValues[];

@ApiPropertyOptional({ type: () => Array })
@IsOptional()
@IsArray()
@MultiORMColumn({ type: isPostgres() ? 'jsonb' : isMySQL() ? 'json' : 'text', nullable: true })
updatedEntities?: IActivityLogUpdatedValues[];

@ApiPropertyOptional({ type: () => Object })
@IsOptional()
@IsArray()
@MultiORMColumn({ type: isPostgres() ? 'jsonb' : isMySQL() ? 'json' : 'text', nullable: true })
data?: Record<string, any>;

/*
|--------------------------------------------------------------------------
| @ManyToOne
|--------------------------------------------------------------------------
*/

/**
* User performed action
*/
@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'
})
@JoinColumn()
creator?: IUser;

@ApiPropertyOptional({ type: () => String })
@IsOptional()
@IsUUID()
@RelationId((it: ActivityLog) => it.creator)
@ColumnIndex()
@MultiORMColumn({ nullable: true, relationId: true })
creatorId?: ID;
}
1 change: 1 addition & 0 deletions packages/core/src/activity-log/repository/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './mikro-orm-activity-log.repository';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { MikroOrmBaseEntityRepository } from '../../core/repository/mikro-orm-base-entity.repository';
import { ActivityLog } from '../activity-log.entity';

export class MikroOrmActivityLogRepository extends MikroOrmBaseEntityRepository<ActivityLog> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ActivityLog } from '../activity-log.entity';

@Injectable()
export class TypeOrmActivityLogRepository extends Repository<ActivityLog> {
constructor(@InjectRepository(ActivityLog) readonly repository: Repository<ActivityLog>) {
super(repository.target, repository.manager, repository.queryRunner);
}
}
2 changes: 2 additions & 0 deletions packages/core/src/core/entities/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
AccountingTemplate,
Activity,
ActivityLog,
AppointmentEmployee,
ApprovalPolicy,
AvailabilitySlot,
Expand Down Expand Up @@ -144,6 +145,7 @@ import {
export const coreEntities = [
AccountingTemplate,
Activity,
ActivityLog,
AppointmentEmployee,
ApprovalPolicy,
AvailabilitySlot,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/core/entities/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './translate-base';

//core entities
export * from '../../accounting-template/accounting-template.entity';
export * from '../../activity-log/activity-log.entity';
export * from '../../appointment-employees/appointment-employees.entity';
export * from '../../approval-policy/approval-policy.entity';
export * from '../../availability-slots/availability-slots.entity';
Expand Down
Loading

0 comments on commit c61f9ae

Please sign in to comment.