Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] View and Resource Links Activity Logs #8409

Merged
merged 9 commits into from
Oct 14, 2024
2 changes: 2 additions & 0 deletions packages/contracts/src/base-entity.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ export enum EntityEnum {
OrganizationTeam = 'OrganizationTeam',
OrganizationProjectModule = 'OrganizationProjectModule',
OrganizationSprint = 'OrganizationSprint',
ResourceLink = 'ResourceLink',
Task = 'Task',
TaskView = 'TaskView',
User = 'User'
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
EntityEnum,
ActorTypeEnum,
FavoriteEntityEnum,
IActivityLogUpdatedValues,
ID,
IEmployee,
IOrganizationSprint,
Expand All @@ -20,7 +19,7 @@ import { OrganizationSprintEmployee } from '../core/entities/internal';
import { FavoriteService } from '../core/decorators';
// import { prepareSQLQuery as p } from './../database/database.helper';
import { ActivityLogEvent } from '../activity-log/events';
import { generateActivityLogDescription } from '../activity-log/activity-log.helper';
import { activityLogUpdatedFieldsAndValues, generateActivityLogDescription } from '../activity-log/activity-log.helper';
import { RoleService } from '../role/role.service';
import { EmployeeService } from '../employee/employee.service';
import { OrganizationSprint } from './organization-sprint.entity';
Expand Down Expand Up @@ -201,20 +200,10 @@ export class OrganizationSprintService extends TenantAwareCrudService<Organizati
);

// Compare values before and after update then add updates to fields
const updatedFields = [];
const previousValues: IActivityLogUpdatedValues[] = [];
const updatedValues: IActivityLogUpdatedValues[] = [];

for (const key of Object.keys(input)) {
if (organizationSprint[key] !== input[key]) {
// Add updated field
updatedFields.push(key);

// Add old and new values
previousValues.push({ [key]: organizationSprint[key] });
updatedValues.push({ [key]: updatedSprint[key] });
}
}
const { updatedFields, previousValues, updatedValues } = activityLogUpdatedFieldsAndValues(
updatedSprint,
input
);

// Emit event to log activity
this._eventBus.publish(
Expand Down
76 changes: 72 additions & 4 deletions packages/core/src/resource-link/resource-link.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { EventBus } from '@nestjs/cqrs';
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { UpdateResult } from 'typeorm';
import {
IResourceLink,
IResourceLinkCreateInput,
IResourceLinkUpdateInput,
ID,
ActionTypeEnum,
EntityEnum,
ActorTypeEnum
} from '@gauzy/contracts';
import { TenantAwareCrudService } from './../core/crud';
import { RequestContext } from '../core/context';
import { IResourceLink, IResourceLinkCreateInput, IResourceLinkUpdateInput, ID } from '@gauzy/contracts';
import { ActivityLogEvent } from '../activity-log/events';
import { activityLogUpdatedFieldsAndValues, generateActivityLogDescription } from '../activity-log/activity-log.helper';
import { UserService } from '../user/user.service';
import { ResourceLink } from './resource-link.entity';
import { TypeOrmResourceLinkRepository } from './repository/type-orm-resource-link.repository';
Expand All @@ -13,7 +24,8 @@ export class ResourceLinkService extends TenantAwareCrudService<ResourceLink> {
constructor(
readonly typeOrmResourceLinkRepository: TypeOrmResourceLinkRepository,
readonly mikroOrmResourceLinkRepository: MikroOrmResourceLinkRepository,
private readonly userService: UserService
private readonly userService: UserService,
private readonly _eventBus: EventBus
) {
super(typeOrmResourceLinkRepository, mikroOrmResourceLinkRepository);
}
Expand All @@ -37,11 +49,34 @@ export class ResourceLinkService extends TenantAwareCrudService<ResourceLink> {
}

// return created resource link
return await super.create({
const resourceLink = await super.create({
...entity,
tenantId,
creatorId: user.id
});

// Generate the activity log description.
const description = generateActivityLogDescription(
ActionTypeEnum.Created,
EntityEnum.ResourceLink,
`${resourceLink.title} for ${resourceLink.entity}`
);

// Emit an event to log the activity
this._eventBus.publish(
new ActivityLogEvent({
entity: EntityEnum.ResourceLink,
entityId: resourceLink.id,
action: ActionTypeEnum.Created,
actorType: ActorTypeEnum.User,
description,
data: resourceLink,
GloireMutaliko21 marked this conversation as resolved.
Show resolved Hide resolved
organizationId: resourceLink.organizationId,
tenantId
})
);

GloireMutaliko21 marked this conversation as resolved.
Show resolved Hide resolved
return resourceLink;
} catch (error) {
console.log(error); // Debug Logging
throw new BadRequestException('Resource Link creation failed', error);
Expand All @@ -62,10 +97,43 @@ export class ResourceLinkService extends TenantAwareCrudService<ResourceLink> {
throw new BadRequestException('Resource Link not found');
}

return await super.create({
const updatedResourceLink = await super.create({
...input,
id
});

GloireMutaliko21 marked this conversation as resolved.
Show resolved Hide resolved
// Generate the activity log description.
const description = generateActivityLogDescription(
ActionTypeEnum.Updated,
EntityEnum.ResourceLink,
`${resourceLink.title} for ${resourceLink.entity}`
);

// Compare values before and after update then add updates to fields
const { updatedFields, previousValues, updatedValues } = activityLogUpdatedFieldsAndValues(
updatedResourceLink,
input
);

// Emit event to log activity
this._eventBus.publish(
new ActivityLogEvent({
entity: EntityEnum.ResourceLink,
entityId: updatedResourceLink.id,
action: ActionTypeEnum.Updated,
actorType: ActorTypeEnum.User,
description,
updatedFields,
updatedValues,
previousValues,
data: updatedResourceLink,
organizationId: updatedResourceLink.organizationId,
tenantId: updatedResourceLink.tenantId
})
);

// return updated Resource Link
return updatedResourceLink;
} catch (error) {
console.log(error); // Debug Logging
throw new BadRequestException('Resource Link update failed', error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ export class TaskViewUpdateHandler implements ICommandHandler<TaskViewUpdateComm
public async execute(command: TaskViewUpdateCommand): Promise<ITaskView> {
const { id, input } = command;

return await this.taskViewService.create({
...input,
id
});
return await this.taskViewService.update(id, input);
}
}
118 changes: 115 additions & 3 deletions packages/core/src/tasks/views/view.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import { Injectable } from '@nestjs/common';
import { EventBus } from '@nestjs/cqrs';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { FavoriteEntityEnum } from '@gauzy/contracts';
import {
ActionTypeEnum,
ActorTypeEnum,
EntityEnum,
FavoriteEntityEnum,
ID,
ITaskView,
ITaskViewCreateInput,
ITaskViewUpdateInput
} from '@gauzy/contracts';
import { FavoriteService } from '../../core/decorators';
import { TenantAwareCrudService } from '../../core/crud';
import { RequestContext } from '../../core/context';
import { ActivityLogEvent } from '../../activity-log/events';
import {
activityLogUpdatedFieldsAndValues,
generateActivityLogDescription
} from '../../activity-log/activity-log.helper';
import { TaskView } from './view.entity';
import { TypeOrmTaskViewRepository } from './repository/type-orm-task-view.repository';
import { MikroOrmTaskViewRepository } from './repository/mikro-orm-task-view.repository';
Expand All @@ -14,8 +30,104 @@
@InjectRepository(TaskView)
typeOrmTaskViewRepository: TypeOrmTaskViewRepository,

mikroOrmTaskViewRepository: MikroOrmTaskViewRepository
mikroOrmTaskViewRepository: MikroOrmTaskViewRepository,

private readonly _eventBus: EventBus
) {
super(typeOrmTaskViewRepository, mikroOrmTaskViewRepository);
}

/**
* @description Creates a Task View based on provided input
* @param {ITaskViewCreateInput} entity - Input data for creating the task view
* @returns A promise resolving to the created Task View
* @throws BadRequestException if there is an error in the creation process.
* @memberof TaskViewService
*/
async create(entity: ITaskViewCreateInput): Promise<ITaskView> {
const tenantId = RequestContext.currentTenantId() || entity.tenantId;
const { organizationId } = entity;
try {
const view = await super.create({ ...entity, tenantId });

// Generate the activity log description.
const description = generateActivityLogDescription(ActionTypeEnum.Created, EntityEnum.TaskView, view.name);

// Emit an event to log the activity
this._eventBus.publish(
new ActivityLogEvent({
entity: EntityEnum.TaskView,
entityId: view.id,
action: ActionTypeEnum.Created,
actorType: ActorTypeEnum.User,
description,
data: view,
organizationId,
tenantId
})
);

return view;
} catch (error) {
// Handle errors and return an appropriate error response
throw new HttpException(`Failed to create view : ${error.message}`, HttpStatus.BAD_REQUEST);
}
}

/**
* @description Update a Task View
* @author GloireMutaliko

Check warning on line 79 in packages/core/src/tasks/views/view.service.ts

View workflow job for this annotation

GitHub Actions / Cspell

Unknown word (Gloire)

Check warning on line 79 in packages/core/src/tasks/views/view.service.ts

View workflow job for this annotation

GitHub Actions / Cspell

Unknown word (Mutaliko)
* @param {ID} id - The ID of the Task View to be updated
* @param {ITaskViewUpdateInput} input - The updated information for the Task View
* @throws BadRequestException if there's an error during the update process.
* @returns {Promise<ITaskView>} A Promise resolving to the updated Task View
* @memberof TaskViewService
*/
async update(id: ID, input: ITaskViewUpdateInput): Promise<ITaskView> {
const tenantId = RequestContext.currentTenantId() || input.tenantId;

try {
const updatedTaskView = await super.create({
...input,
tenantId,
id
});
GloireMutaliko21 marked this conversation as resolved.
Show resolved Hide resolved

// Generate the activity log description.
const description = generateActivityLogDescription(
ActionTypeEnum.Updated,
EntityEnum.TaskView,
updatedTaskView.name
);

// Compare values before and after update then add updates to fields
const { updatedFields, previousValues, updatedValues } = activityLogUpdatedFieldsAndValues(
updatedTaskView,
input
);

// Emit event to log activity
this._eventBus.publish(
new ActivityLogEvent({
entity: EntityEnum.TaskView,
entityId: updatedTaskView.id,
action: ActionTypeEnum.Updated,
actorType: ActorTypeEnum.User,
description,
updatedFields,
updatedValues,
previousValues,
data: updatedTaskView,
organizationId: updatedTaskView.organizationId,
tenantId
})
);

// return updated view
return updatedTaskView;
} catch (error) {
// Handle errors and return an appropriate error response
throw new HttpException(`Failed to update view: ${error.message}`, HttpStatus.BAD_REQUEST);
}
}
}
Loading