-
Notifications
You must be signed in to change notification settings - Fork 559
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8230 from ever-co/feat/activity-log-api
[Feat] Activity Log Events / APIs
- Loading branch information
Showing
23 changed files
with
791 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { Controller, Get, Query, UseGuards } from '@nestjs/common'; | ||
import { IActivityLog, IPagination } from '@gauzy/contracts'; | ||
import { Permissions } from '../shared/decorators'; | ||
import { PermissionGuard, TenantPermissionGuard } from '../shared/guards'; | ||
import { UseValidationPipe } from '../shared/pipes'; | ||
import { GetActivityLogsDTO } from './dto/get-activity-logs.dto'; | ||
import { ActivityLogService } from './activity-log.service'; | ||
|
||
@UseGuards(TenantPermissionGuard, PermissionGuard) | ||
@Permissions() | ||
@Controller('/activity-log') | ||
export class ActivityLogController { | ||
constructor(readonly _activityLogService: ActivityLogService) {} | ||
|
||
/** | ||
* Retrieves activity logs based on query parameters. | ||
* Supports filtering, pagination, sorting, and ordering. | ||
* | ||
* @param query Query parameters for filtering, pagination, and ordering. | ||
* @returns A list of activity logs. | ||
*/ | ||
@Get('/') | ||
@UseValidationPipe() | ||
async getActivityLogs( | ||
@Query() query: GetActivityLogsDTO | ||
): Promise<IPagination<IActivityLog>> { | ||
return await this._activityLogService.findActivityLogs(query); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { ActionTypeEnum, ActivityLogEntityEnum } from "@gauzy/contracts"; | ||
|
||
const ActivityTemplates = { | ||
[ActionTypeEnum.Created]: `{action} a new {entity} called "{entityName}"`, | ||
[ActionTypeEnum.Updated]: `{action} {entity} "{entityName}"`, | ||
[ActionTypeEnum.Deleted]: `{action} {entity} "{entityName}"`, | ||
}; | ||
|
||
/** | ||
* Generates an activity description based on the action type, entity, and entity name. | ||
* @param action - The action performed (e.g., CREATED, UPDATED, DELETED). | ||
* @param entity - The type of entity involved in the action (e.g., Project, User). | ||
* @param entityName - The name of the specific entity instance. | ||
* @returns A formatted description string. | ||
*/ | ||
export function generateActivityLogDescription( | ||
action: ActionTypeEnum, | ||
entity: ActivityLogEntityEnum, | ||
entityName: string | ||
): string { | ||
// Get the template corresponding to the action | ||
const template = ActivityTemplates[action] || '{action} {entity} "{entityName}"'; | ||
|
||
// Replace placeholders in the template with actual values | ||
return template.replace(/\{(\w+)\}/g, (_, key) => { | ||
switch (key) { | ||
case 'action': | ||
return action; | ||
case 'entity': | ||
return entity; | ||
case 'entityName': | ||
return entityName; | ||
default: | ||
return ''; | ||
} | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { CqrsModule } from '@nestjs/cqrs'; | ||
import { Module } from '@nestjs/common'; | ||
import { MikroOrmModule } from '@mikro-orm/nestjs'; | ||
import { TypeOrmModule } from '@nestjs/typeorm'; | ||
import { RolePermissionModule } from '../role-permission/role-permission.module'; | ||
import { ActivityLogController } from './activity-log.controller'; | ||
import { ActivityLog } from './activity-log.entity'; | ||
import { ActivityLogService } from './activity-log.service'; | ||
import { EventHandlers } from './events/handlers'; | ||
import { TypeOrmActivityLogRepository } from './repository/type-orm-activity-log.repository'; | ||
|
||
@Module({ | ||
imports: [ | ||
TypeOrmModule.forFeature([ActivityLog]), | ||
MikroOrmModule.forFeature([ActivityLog]), | ||
CqrsModule, | ||
RolePermissionModule | ||
], | ||
controllers: [ActivityLogController], | ||
providers: [ActivityLogService, TypeOrmActivityLogRepository, ...EventHandlers], | ||
exports: [TypeOrmModule, MikroOrmModule, ActivityLogService, TypeOrmActivityLogRepository] | ||
}) | ||
export class ActivityLogModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { BadRequestException, Injectable } from '@nestjs/common'; | ||
import { FindManyOptions, FindOptionsOrder, FindOptionsWhere } from 'typeorm'; | ||
import { IActivityLog, IActivityLogInput, IPagination } from '@gauzy/contracts'; | ||
import { TenantAwareCrudService } from './../core/crud'; | ||
import { RequestContext } from '../core/context'; | ||
import { GetActivityLogsDTO, allowedOrderDirections, allowedOrderFields } from './dto/get-activity-logs.dto'; | ||
import { ActivityLog } from './activity-log.entity'; | ||
import { MikroOrmActivityLogRepository, TypeOrmActivityLogRepository } from './repository'; | ||
|
||
@Injectable() | ||
export class ActivityLogService extends TenantAwareCrudService<ActivityLog> { | ||
constructor( | ||
readonly typeOrmActivityLogRepository: TypeOrmActivityLogRepository, | ||
readonly mikroOrmActivityLogRepository: MikroOrmActivityLogRepository | ||
) { | ||
super(typeOrmActivityLogRepository, mikroOrmActivityLogRepository); | ||
} | ||
|
||
/** | ||
* Finds and retrieves activity logs based on the given filter criteria. | ||
* | ||
* @param {GetActivityLogsDTO} filter - Filter criteria to find activity logs, including entity, entityId, action, actorType, isActive, isArchived, orderBy, and order. | ||
* @returns {Promise<IPagination<IActivityLog>>} - A promise that resolves to a paginated list of activity logs. | ||
* | ||
* Example usage: | ||
* ``` | ||
* const logs = await findActivityLogs({ | ||
* entity: 'User', | ||
* action: 'CREATE', | ||
* orderBy: 'updatedAt', | ||
* order: 'ASC' | ||
* }); | ||
* ``` | ||
*/ | ||
public async findActivityLogs(filter: GetActivityLogsDTO): Promise<IPagination<IActivityLog>> { | ||
const { | ||
entity, | ||
entityId, | ||
action, | ||
actorType, | ||
isActive = true, | ||
isArchived = false, | ||
orderBy = 'createdAt', | ||
order = 'DESC', | ||
relations = [], | ||
skip, | ||
take | ||
} = filter; | ||
|
||
// Build the 'where' condition using concise syntax | ||
const where: FindOptionsWhere<ActivityLog> = { | ||
...(entity && { entity }), | ||
...(entityId && { entityId }), | ||
...(action && { action }), | ||
...(actorType && { actorType }), | ||
isActive, | ||
isArchived | ||
}; | ||
|
||
// Fallback to default if invalid orderBy/order values are provided | ||
const orderField = allowedOrderFields.includes(orderBy) ? orderBy : 'createdAt'; | ||
const orderDirection = allowedOrderDirections.includes(order.toUpperCase()) ? order.toUpperCase() : 'DESC'; | ||
|
||
// Define order option | ||
const orderOption: FindOptionsOrder<ActivityLog> = { [orderField]: orderDirection }; | ||
|
||
// Define find options | ||
const findOptions: FindManyOptions<ActivityLog> = { | ||
where, | ||
order: orderOption, | ||
...(skip && { skip }), | ||
...(take && { take }), | ||
...(relations && { relations }) | ||
}; | ||
|
||
// Retrieve activity logs using the base class method | ||
return await super.findAll(findOptions); | ||
} | ||
|
||
/** | ||
* Creates a new activity log entry with the provided input, while associating it with the current user and tenant. | ||
* | ||
* @param input - The data required to create an activity log entry. | ||
* @returns The created activity log entry. | ||
* @throws BadRequestException when the log creation fails. | ||
*/ | ||
async logActivity(input: IActivityLogInput): Promise<IActivityLog> { | ||
try { | ||
const creatorId = RequestContext.currentUserId(); // Retrieve the current user's ID from the request context | ||
// Create the activity log entry using the provided input along with the tenantId and creatorId | ||
return await super.create({ ...input, creatorId }); | ||
} catch (error) { | ||
console.log('Error while creating activity log:', error); | ||
throw new BadRequestException('Error while creating activity log', error); | ||
} | ||
} | ||
} |
Oops, something went wrong.