diff --git a/iac/lib/iac_stack.ts b/iac/lib/iac_stack.ts index 90555f0..bbf91a5 100644 --- a/iac/lib/iac_stack.ts +++ b/iac/lib/iac_stack.ts @@ -19,8 +19,7 @@ export class IacStack extends cdk.Stack { allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowHeaders: ["*"], } - } - ); + }); const bucket = new Bucket(this, "Coil_Bucket", { bucketName: "coil-bucket", diff --git a/iac/lib/lambda_stack.ts b/iac/lib/lambda_stack.ts index 3794fa8..1f2b1c9 100644 --- a/iac/lib/lambda_stack.ts +++ b/iac/lib/lambda_stack.ts @@ -6,14 +6,16 @@ export class LambdaStack extends Construct { private get_user: lambda_js.NodejsFunction; private auth_user: lambda_js.NodejsFunction; private create_moderator: lambda_js.NodejsFunction; - + private get_all_moderators: lambda_js.NodejsFunction; + private delete_moderator: lambda_js.NodejsFunction; + private get_institution: lambda_js.NodejsFunction; private create_institution: lambda_js.NodejsFunction; private update_institution: lambda_js.NodejsFunction; private get_all_institutions: lambda_js.NodejsFunction; private get_institution_requirements: lambda_js.NodejsFunction; - private assign_user: lambda_js.NodejsFunction; + private assign_user: lambda_js.NodejsFunction; private get_activity: lambda_js.NodejsFunction; private create_activity: lambda_js.NodejsFunction; private update_activity: lambda_js.NodejsFunction; @@ -21,9 +23,11 @@ export class LambdaStack extends Construct { private update_users_activity: lambda_js.NodejsFunction; private update_activity_event: lambda_js.NodejsFunction; private get_activity_requirements: lambda_js.NodejsFunction; + private get_catalog: lambda_js.NodejsFunction; private get_all_activities_enrolled: lambda_js.NodejsFunction; public functions_need_s3_access: lambda.Function[] = []; + public function_need_event_bridge_access: lambda.Function[] = []; public functions_need_event_bridge_access: lambda.Function[] = []; private create_lambda( @@ -57,9 +61,9 @@ export class LambdaStack extends Construct { restapi_resource.addResource(function_name.replace(/_/g, "-"), { defaultCorsPreflightOptions: { - allowOrigins: origins, - allowMethods: [method], - allowHeaders: ["*"], + allowOrigins: origins, + allowMethods: [method], + allowHeaders: ["*"], } }).addMethod(method, new apigw.LambdaIntegration(function_lambda)); @@ -104,6 +108,14 @@ export class LambdaStack extends Construct { origins ); + this.delete_moderator = this.create_lambda( + "delete_moderator", + environment_variables, + "GET", + restapi_resource, + origins + ); + this.create_activity = this.create_lambda( "create_activity", environment_variables, @@ -222,6 +234,22 @@ export class LambdaStack extends Construct { origins ) + this.get_catalog = this.create_lambda( + "get_catalog", + environment_variables, + "GET", + restapi_resource, + origins + ) + + this.get_all_moderators = this.create_lambda( + "get_all_moderators", + environment_variables, + "GET", + restapi_resource, + origins + ) + this.functions_need_s3_access = [ this.create_institution, this.update_institution, @@ -230,6 +258,25 @@ export class LambdaStack extends Construct { this.functions_need_event_bridge_access = [ this.create_activity, this.update_activity, + this.update_activity_event, ] + + this.functions_need_event_bridge_access.forEach((function_lambda) => { + function_lambda.addToRolePolicy( + new iam.PolicyStatement({ + actions: [ + "events:PutRule", + "events:PutTargets", + "events:RemoveTargets", + "events:DeleteRule", + ], + resources: ["*"], + })) + function_lambda.addPermission("EventBridgePermission", { + principal: new iam.ServicePrincipal("events.amazonaws.com"), + sourceArn: "arn:aws:events:us-east-1:123456789012:rule/*", + action: "lambda:InvokeFunction", + }); + }); } } diff --git a/populate_database.ts b/populate_database.ts index 776d7cc..270ffaf 100644 --- a/populate_database.ts +++ b/populate_database.ts @@ -269,6 +269,39 @@ async function createOrUpdateUser(user: UserEntity): Promise { } } +async function createOrUpdateStatusActivity(status: ActivityStatusEnum) { + try { + let existing = await ActivityStatus.findOne({ where: { id: status } }); + let name: string; + switch (status) { + case ActivityStatusEnum.ACTIVE: + name = "Apply Now"; + break; + case ActivityStatusEnum.CANCELED: + name = "Canceled"; + break; + case ActivityStatusEnum.ENDED: + name = "Ended"; + break; + case ActivityStatusEnum.ON_HOLD: + name = "Under Analysis"; + break; + case ActivityStatusEnum.TO_START: + name = "Coming Soon"; + break; + } + if (!existing) { + await ActivityStatus.create({ name: name }); + console.log(`Activity status ${status} created`); + } else { + await ActivityStatus.update({ name: name }, { where: { id: status } }); + console.log(`Activity status ${status} updated`); + } + } catch (error) { + console.error(`Error creating or updating activity status ${status}:`, error); + } +} + (async () => { try { await handleDatabaseCreation(); @@ -278,12 +311,17 @@ async function createOrUpdateUser(user: UserEntity): Promise { await handleLanguagesCreation(); await handleCountriesCreation(); - + await handleCriteriasCreation(); - await createOrUpdateSocialMedia(); + await createOrUpdateSocialMedia(); + + await createOrUpdateStatusActivity(ActivityStatusEnum.ACTIVE); + await createOrUpdateStatusActivity(ActivityStatusEnum.CANCELED); + await createOrUpdateStatusActivity(ActivityStatusEnum.ENDED); + await createOrUpdateStatusActivity(ActivityStatusEnum.ON_HOLD); + await createOrUpdateStatusActivity(ActivityStatusEnum.TO_START); - await createOrUpdateEnumItems(ActivityStatus, activityStatuses, ActivityStatusEnum); await createOrUpdateEnumItems(ActivityType, activityTypes, ActivityTypeEnum); await createOrUpdateEnumItems(UserType, userTypes, UserTypeEnum); diff --git a/src/core/helpers/functions/event_bridge.ts b/src/core/helpers/functions/event_bridge.ts index 6c42d9f..2177441 100644 --- a/src/core/helpers/functions/event_bridge.ts +++ b/src/core/helpers/functions/event_bridge.ts @@ -5,16 +5,12 @@ dotenv.config(); export class EventBridgeManager { - private event: EventBridge; private lambda: Lambda; - private aws_region: string; - private aws_account_id: string; + private event: EventBridge; constructor() { - this.event = new EventBridge(); this.lambda = new Lambda(); - this.aws_region = process.env.AWS_REGION as string; - this.aws_account_id = process.env.AWS_ACCOUNT_ID as string; + this.event = new EventBridge(); } private async get_rule(rule_name: string): Promise { @@ -40,20 +36,11 @@ export class EventBridgeManager { }).promise(); const lambda_arn = lambda_response.Configuration?.FunctionArn as string; - // Add permission - await this.lambda.addPermission({ - Action: "lambda:InvokeFunction", - FunctionName: lambda_function, - Principal: "events.amazonaws.com", - StatementId: rule_name, - SourceArn: "arn:aws:events:" + this.aws_region + ":" + this.aws_account_id + ":rule/" + rule_name, - }).promise(); - // Create CloudWatch Events rule await this.event.putRule({ Name: rule_name, ScheduleExpression: "cron(" + date.getMinutes() + " " + date.getHours() + " " + date.getDate() + " " + (date.getMonth() + 1) + " ? " + date.getFullYear() + ")", - State: "ENABLED", + State: "ENABLED" }).promise(); // Add target to the rule @@ -72,27 +59,34 @@ export class EventBridgeManager { }, ], }).promise(); + + // Add permission to Lambda function + await this.lambda.addPermission({ + FunctionName: lambda_function, + StatementId: rule_name, + Action: "lambda:InvokeFunction", + Principal: "events.amazonaws.com", + SourceArn: "arn:aws:events:" + process.env.AWS_REGION + ":" + process.env.AWS_ACCOUNT_ID + ":rule/" + rule_name, + }).promise(); + + return true; } public async delete_trigger(rule_name: string, lambda_function: string): Promise { const has_rule = await this.get_rule(rule_name); lambda_function = lambda_function + "_Coil"; - + if (has_rule) { - await this.lambda.removePermission({ - FunctionName: lambda_function, - StatementId: rule_name, - }).promise(); - + await this.event.removeTargets({ Rule: rule_name, Ids: [rule_name], }).promise(); - + await this.event.deleteRule({ Name: rule_name, }).promise(); - + return true; } else { return true; diff --git a/src/core/repositories/database/repositories/ActivityRepo.ts b/src/core/repositories/database/repositories/ActivityRepo.ts index 96f55ad..aa33be3 100644 --- a/src/core/repositories/database/repositories/ActivityRepo.ts +++ b/src/core/repositories/database/repositories/ActivityRepo.ts @@ -25,6 +25,7 @@ import { Country, SocialMedia } from "../models/Models"; +import { a } from "vitest/dist/suite-IbNSsUWN"; export class ActivityRepo implements IActivityRepo { @@ -158,8 +159,6 @@ export class ActivityRepo implements IActivityRepo { return null; } - console.log(activities.map((activity) => activity.toJSON())); - return activities.map((activity) => this.ActivityDTO.to_entity(activity.toJSON())); } @@ -354,7 +353,7 @@ export class ActivityRepo implements IActivityRepo { id: activity.id } }); - + await ActivityPartnerInstitution.destroy({ where: { activity_id: activity.id @@ -438,19 +437,96 @@ export class ActivityRepo implements IActivityRepo { } async update_users_activity_status(activity_id: string, users: { user_id: string, status: boolean }[]): Promise { - for (let user of users) { - const response = await ActivityApplication.update({ - status: user.status + let response = true; + + users.forEach(async user => { + const response_user = await ActivityApplication.update({ + status: !user.status }, { where: { activity_id: activity_id, user_id: user.user_id } }); - if (response[0] === 0) { - return false; + if (response_user[0] === 0) { + response = false; } - } - return true; + }); + + return response; + } + + async get_all_activities_catalog(): Promise<{ title: string; logo: string; type_activity: ActivityTypeEnum; }[]> { + const response_project = await ActivityDB.findAll({ + include: [{ + model: ActivityPartnerInstitution, as: 'partner_institutions', + include: [{ + model: Institution, + as: 'institution', + include: [{ + model: InstitutionImageDB, as: 'images', + limit: 1, + order: [['id', 'ASC']] + }] + }] + }], + where: { + status_id: { + [Op.or]: [ActivityStatusEnum.ACTIVE, ActivityStatusEnum.TO_START] + }, + type_id: ActivityTypeEnum.PROJECT + }, + order: [ + ['start_date', 'ASC'] + ], + limit: 5 + }); + + const response_mobility = await ActivityDB.findAll({ + include: [{ + model: ActivityPartnerInstitution, as: 'partner_institutions', + include: [{ + model: Institution, + as: 'institution', + include: [{ + model: InstitutionImageDB, as: 'images', + limit: 1, + order: [['id', 'ASC']] + }] + }] + }], + where: { + status_id: { + [Op.or]: [ActivityStatusEnum.ACTIVE, ActivityStatusEnum.TO_START] + }, + type_id: ActivityTypeEnum.ACADEMIC_MOBILITY + }, + order: [ + ['start_date', 'ASC'] + ], + limit: 5 + }); + + let activities = response_project.concat(response_mobility); + + let activities_json: { + title: string; + partner_institutions: { + institution: { + images: { image: string }[]; + }; + }[]; + type_id: ActivityTypeEnum; + }[] = activities.map(activity => activity.toJSON()); + + activities_json = activities_json.map(activity => ( + activity = activity.partner_institutions.length > 0 ? activity : { ...activity, partner_institutions: [{ institution: { images: [{ image: "" }] } }] } + )); + + return activities_json.map(activity => ({ + title: activity.title, + logo: activity.partner_institutions[0].institution.images[0].image, + type_activity: activity.type_id + })); } } \ No newline at end of file diff --git a/src/core/repositories/database/repositories/UserRepo.ts b/src/core/repositories/database/repositories/UserRepo.ts index 894c73e..bbbe7d7 100644 --- a/src/core/repositories/database/repositories/UserRepo.ts +++ b/src/core/repositories/database/repositories/UserRepo.ts @@ -2,6 +2,7 @@ import { UserDTO } from "../dtos/UserDTO"; import { IUserRepo } from "../../interfaces/IUserRepo"; import { User } from "../../../structure/entities/User"; import { User as UserDB, UserType as UserTypeDB } from "../models/Models"; +import { UserTypeEnum } from "../../../helpers/enums/UserTypeEnum"; export class UserRepo implements IUserRepo { private user_dto: UserDTO = new UserDTO(); @@ -68,4 +69,28 @@ export class UserRepo implements IUserRepo { return user_updated ? true : false; } + + public async get_all_moderators(): Promise { + let moderators_found = await UserDB.findAll({ + where: { + user_type_id: UserTypeEnum.MODERATOR, + }, + include: [ + { model: UserTypeDB, as: 'user_type' } + ] + }); + + return moderators_found.map((moderator) => this.user_dto.to_entity(moderator.toJSON())); + } + + public async delete_moderator(id: string): Promise { + let moderator_deleted = await UserDB.destroy({ + where: { + id: id, + user_type_id: UserTypeEnum.MODERATOR, + }, + }); + + return moderator_deleted ? true : false; + } } diff --git a/src/core/repositories/interfaces/IActivityRepo.ts b/src/core/repositories/interfaces/IActivityRepo.ts index 67dbc3f..4432011 100644 --- a/src/core/repositories/interfaces/IActivityRepo.ts +++ b/src/core/repositories/interfaces/IActivityRepo.ts @@ -11,10 +11,11 @@ export interface IActivityRepo { check_activity_by_title(title: string): Promise check_activity_enrolled_by_user(user_id: string, activity_id: string): Promise + get_all_activities_catalog(): Promise<{ title: string; logo: string; type_activity: ActivityTypeEnum; }[]> get_activity(id: string, applicants?: boolean): Promise + get_activities_by_user_id(user_id: string, type: ActivityTypeEnum): Promise get_activity_applicant(activity_id: string, user_id: string): Promise<{ user_id: string, status: boolean } | null> get_activity_applicants(activity_id: string, applicants: string[]): Promise<{ user_id: string, status: boolean }[]> - get_activities_by_user_id(user_id: string, type: ActivityTypeEnum): Promise get_all_activities_by_status(status: ActivityStatusEnum | ActivityStatusEnum[], type: ActivityTypeEnum): Promise assign_user_to_activity(activity_id: string, user_id: string): Promise<{ assign: boolean }> diff --git a/src/core/repositories/interfaces/IUserRepo.ts b/src/core/repositories/interfaces/IUserRepo.ts index 2744f92..4c3e5d9 100644 --- a/src/core/repositories/interfaces/IUserRepo.ts +++ b/src/core/repositories/interfaces/IUserRepo.ts @@ -5,4 +5,6 @@ export interface IUserRepo { update_user(user: User): Promise; get_user(id: string): Promise; get_user_by_email(email: string): Promise; + get_all_moderators(): Promise; + delete_moderator(id: string): Promise; } diff --git a/src/core/repositories/mocks/ActivityRepoMock.ts b/src/core/repositories/mocks/ActivityRepoMock.ts index c050781..292503e 100644 --- a/src/core/repositories/mocks/ActivityRepoMock.ts +++ b/src/core/repositories/mocks/ActivityRepoMock.ts @@ -165,4 +165,12 @@ export class ActivityRepoMock implements IActivityRepo { }); } + async get_all_activities_catalog(): Promise<{ title: string; logo: string; type_activity: ActivityTypeEnum; }[]> { + return this.activity_mock.activities.map(activity => ({ + title: activity.title, + logo: activity.partner_institutions[0].institution?.images[0] || "", + type_activity: activity.type_activity + })); + } + } diff --git a/src/core/repositories/mocks/UserRepoMock.ts b/src/core/repositories/mocks/UserRepoMock.ts index eee3013..507684a 100644 --- a/src/core/repositories/mocks/UserRepoMock.ts +++ b/src/core/repositories/mocks/UserRepoMock.ts @@ -1,6 +1,7 @@ import { IUserRepo } from "../interfaces/IUserRepo"; import { User } from "../../structure/entities/User"; import { UserMock } from "../../structure/mocks/UserMock"; +import { UserTypeEnum } from "../../helpers/enums/UserTypeEnum"; export class UserRepoMock implements IUserRepo { public user_mock: UserMock; @@ -35,4 +36,19 @@ export class UserRepoMock implements IUserRepo { resolve(user || null); }); } + + public get_all_moderators(): Promise { + return new Promise((resolve, reject) => { + const moderators = this.user_mock.users.filter((user) => user.user_type === UserTypeEnum.MODERATOR); + resolve(moderators); + }); + } + + public delete_moderator(id: string): Promise { + return new Promise((resolve, reject) => { + const userIndex = this.user_mock.users.findIndex((user) => user.id === id); + this.user_mock.users.splice(userIndex, 1); + resolve(true); + }); + } } \ No newline at end of file diff --git a/src/core/structure/mocks/LanguageMock.ts b/src/core/structure/mocks/LanguageMock.ts index 2e9ef9d..ddeeac0 100644 --- a/src/core/structure/mocks/LanguageMock.ts +++ b/src/core/structure/mocks/LanguageMock.ts @@ -13,7 +13,7 @@ export class LanguageMock { new Language({ id: 2, language: "English", - language_code: "en" + language_code: "gb" }), new Language({ id: 3, @@ -38,22 +38,22 @@ export class LanguageMock { new Language({ id: 7, language: "Mandarin", - language_code: "zh" + language_code: "cn" }), new Language({ id: 8, language: "Japanese", - language_code: "ja" + language_code: "jp" }), new Language({ id: 9, language: "Korean", - language_code: "ko" + language_code: "kr" }), new Language({ id: 10, language: "Arabic", - language_code: "ar" + language_code: "sa" }), new Language({ id: 11, @@ -68,12 +68,12 @@ export class LanguageMock { new Language({ id: 13, language: "Swedish", - language_code: "sv" + language_code: "se" }), new Language({ id: 14, language: "Danish", - language_code: "da" + language_code: "dk" }), new Language({ id: 15, diff --git a/src/modules/create_activity/app/create_activity_usecase.ts b/src/modules/create_activity/app/create_activity_usecase.ts index 39b36e5..5208261 100644 --- a/src/modules/create_activity/app/create_activity_usecase.ts +++ b/src/modules/create_activity/app/create_activity_usecase.ts @@ -73,7 +73,11 @@ export class CreateActivityUsecase { if (!body.type_activity) { throw new MissingParameter("Type Activity"); } - if (new Date(body.start_date) < new Date()) { + + let time_now: Date = new Date(); + time_now.setHours(time_now.getHours() - 3); + + if (new Date(body.start_date) < time_now) { throw new InvalidParameter("StartDate", "Start Date must be in the future"); } if (new Date(body.start_date) >= new Date(body.end_date)) { @@ -206,10 +210,14 @@ export class CreateActivityUsecase { await this.activity_repo.create_activity(activity).then(async (response) => { if (response && process.env.STAGE !== 'test') { + let start_date = activity.start_date; + start_date.setHours(start_date.getHours() + 3); + let end_date = activity.end_date; + end_date.setHours(end_date.getHours() + 3); await this.event_bridge.create_trigger( - "START_ACTIVITY_" + activity.id, + "START_COIL_" + activity.id.substring(0, 8), "Update_Activity_Event", - activity.start_date, + start_date, { "body": { activity_id: activity.id, @@ -219,9 +227,9 @@ export class CreateActivityUsecase { ); await this.event_bridge.create_trigger( - "END_ACTIVITY_" + activity.id, + "END_COIL_" + activity.id.substring(0, 8), "Update_Activity_Event", - activity.end_date, + end_date, { "body": { activity_id: activity.id, diff --git a/src/modules/create_moderator/app/create_moderator_usecase.ts b/src/modules/create_moderator/app/create_moderator_usecase.ts index c3ee7ee..6d91810 100644 --- a/src/modules/create_moderator/app/create_moderator_usecase.ts +++ b/src/modules/create_moderator/app/create_moderator_usecase.ts @@ -5,7 +5,7 @@ import { User } from '../../../core/structure/entities/User'; import { TokenAuth } from '../../../core/helpers/functions/token_auth'; import { UserTypeEnum } from '../../../core/helpers/enums/UserTypeEnum'; import { IUserRepo } from "../../../core/repositories/interfaces/IUserRepo"; -import { InvalidRequest, MissingParameter, UserNotAllowed, UserNotAuthenticated } from '../../../core/helpers/errors/ModuleError'; +import { InvalidParameter, InvalidRequest, MissingParameter, UserNotAllowed, UserNotAuthenticated } from '../../../core/helpers/errors/ModuleError'; export class CreateModeratorUsecase { @@ -27,6 +27,9 @@ export class CreateModeratorUsecase { if (!headers.Authorization) { throw new MissingParameter("Authorization"); } + if (!body.name) { + throw new MissingParameter("Name"); + } if (!body.email) { throw new MissingParameter("Email"); } @@ -46,15 +49,27 @@ export class CreateModeratorUsecase { throw new UserNotAllowed(); } + if (user_admin.email === body.email) { + throw new UserNotAllowed("Admin can't be a moderator"); + } + + const padrao: RegExp = /@maua\.br$/; + if (!padrao.test(body.email)) { + throw new InvalidParameter("Email", "must be a maua.br domain."); + } + const user = await this.database_repo.get_user_by_email(body.email); if (user) { + if (user.user_type === UserTypeEnum.ADMIN) { + throw new UserNotAllowed("Admin can't be a moderator"); + } user.user_type = UserTypeEnum.MODERATOR; - this.database_repo.update_user(user); + await this.database_repo.update_user(user); } else { const moderator = new User({ id: randomUUID(), - name: null, + name: body.name, email: body.email, user_type: UserTypeEnum.MODERATOR, created_at: new Date(), diff --git a/src/modules/delete_moderator/app/delete_moderator_controller.ts b/src/modules/delete_moderator/app/delete_moderator_controller.ts new file mode 100644 index 0000000..4a668de --- /dev/null +++ b/src/modules/delete_moderator/app/delete_moderator_controller.ts @@ -0,0 +1,67 @@ +import { + InvalidParameter, + InvalidRequest, + MissingParameter, + NotfoundError, + UserNotAllowed, + UserNotAuthenticated, +} from "../../../core/helpers/errors/ModuleError"; +import { + BadRequest, + Forbidden, + HttpRequest, + HttpResponse, + InternalServerError, + NotFound, + OK, + ParameterError, + Unauthorized, +} from "../../../core/helpers/http/http_codes"; +import { DeleteModeratorUsecase } from "./delete_moderator_usecase"; + +export class DeleteModeratorController { + public usecase: DeleteModeratorUsecase; + + constructor(usecase: DeleteModeratorUsecase) { + this.usecase = usecase; + } + + public async execute(request: HttpRequest): Promise { + try { + if (!request) { + throw new InvalidRequest(); + } + if (!request.headers) { + throw new InvalidRequest("Headers"); + } + if (!request.body) { + throw new InvalidRequest("Body"); + } + + const body = request.body.body; + + let response = await this.usecase.execute(request.headers, body); + return new OK(response, "Moderator deleted successfully"); + } catch (error: any) { + if (error instanceof InvalidRequest) { + return new BadRequest(error.message); + } + if (error instanceof InvalidParameter) { + return new ParameterError(error.message); + } + if (error instanceof UserNotAuthenticated) { + return new Unauthorized(error.message); + } + if (error instanceof UserNotAllowed) { + return new Forbidden(error.message); + } + if (error instanceof MissingParameter) { + return new ParameterError(error.message); + } + if (error instanceof NotfoundError) { + return new NotFound(error.message); + } + return new InternalServerError(error.message); + } + } +} diff --git a/src/modules/delete_moderator/app/delete_moderator_presenter.ts b/src/modules/delete_moderator/app/delete_moderator_presenter.ts new file mode 100644 index 0000000..ab00e04 --- /dev/null +++ b/src/modules/delete_moderator/app/delete_moderator_presenter.ts @@ -0,0 +1,17 @@ +import { DeleteModeratorController } from "./delete_moderator_controller"; +import { DeleteModeratorUsecase } from "./delete_moderator_usecase"; + +import { Repository } from "../../../core/repositories/Repository"; +import { HttpRequest } from "../../../core/helpers/http/http_codes"; + +const repository = new Repository({user_repo: true}); + +const usecase = new DeleteModeratorUsecase(repository.UserRepo); +const controller = new DeleteModeratorController(usecase); + + +export const handler = async (event: any, context: any) => { + let request = new HttpRequest(event); + let response = await controller.execute(request); + return response.to_json(); +} diff --git a/src/modules/delete_moderator/app/delete_moderator_usecase.ts b/src/modules/delete_moderator/app/delete_moderator_usecase.ts new file mode 100644 index 0000000..4afc867 --- /dev/null +++ b/src/modules/delete_moderator/app/delete_moderator_usecase.ts @@ -0,0 +1,68 @@ +import { UserTypeEnum } from "../../../core/helpers/enums/UserTypeEnum"; +import { + InvalidRequest, + MissingParameter, + NotfoundError, + UserNotAuthenticated, +} from "../../../core/helpers/errors/ModuleError"; +import { TokenAuth } from "../../../core/helpers/functions/token_auth"; +import { IUserRepo } from "../../../core/repositories/interfaces/IUserRepo"; + +export class DeleteModeratorUsecase { + public token_auth: TokenAuth; + public database_repo: IUserRepo; + + constructor(database_repo: IUserRepo) { + this.token_auth = new TokenAuth(); + this.database_repo = database_repo; + } + + async execute( + headers: { [key: string]: any }, + body: { [key: string]: any } + ) { + if (!headers) { + throw new InvalidRequest("Headers"); + } + if (!headers.Authorization) { + throw new MissingParameter("Authorization"); + } + if (!body) { + throw new InvalidRequest("Body"); + } + + const user_id = await this.token_auth + .decode_token(headers.Authorization) + .then((response) => { + return response; + }) + .catch((error) => { + throw new UserNotAuthenticated("Invalid or expired token"); + }); + + const user = await this.database_repo.get_user(user_id); + if (!user) { + throw new UserNotAuthenticated(); + } + + if (user.user_type !== UserTypeEnum.ADMIN) { + throw new UserNotAuthenticated(); + } + + if (!body.moderator_id) { + throw new MissingParameter("moderator_id"); + } + + const moderator = await this.database_repo.get_user(body.moderator_id); + if (!moderator) { + throw new NotfoundError("Moderator"); + } + + if (moderator.user_type !== UserTypeEnum.MODERATOR) { + throw new InvalidRequest("User is not a moderator"); + } + + await this.database_repo.delete_moderator(body.moderator_id); + return { message: "Moderator deleted successfully" }; + } +} diff --git a/src/modules/get_all_moderators/app/get_all_moderators_controller.ts b/src/modules/get_all_moderators/app/get_all_moderators_controller.ts new file mode 100644 index 0000000..a2fbeac --- /dev/null +++ b/src/modules/get_all_moderators/app/get_all_moderators_controller.ts @@ -0,0 +1,57 @@ +import { GetAllModeratorsUsecase } from "./get_all_moderators_usecase"; + +import { UniqueConstraintError } from "sequelize"; +import { EntityError } from '../../../core/helpers/errors/EntityError'; +import { BadRequest, ParameterError, InternalServerError, OK } from '../../../core/helpers/http/http_codes'; +import { Conflict, Forbidden, HttpRequest, HttpResponse, Unauthorized, Unprocessable_Entity } from '../../../core/helpers/http/http_codes'; +import { ConflictError, InvalidParameter, InvalidRequest, MissingParameter, UserNotAllowed, UserNotAuthenticated } from '../../../core/helpers/errors/ModuleError'; + + +export class GetAllModeratorsController { + public usecase: GetAllModeratorsUsecase; + + constructor(usecase: GetAllModeratorsUsecase) { + this.usecase = usecase; + } + + public async execute(request: HttpRequest): Promise { + try { + if (!request) { + throw new InvalidRequest(); + } + if (!request.headers) { + throw new InvalidRequest("Headers"); + } + + let response = await this.usecase.execute(request.headers); + return new OK(response, "Moderators found successfully"); + + } catch (error: any) { + if (error instanceof InvalidRequest) { + return new BadRequest(error.message); + } + if (error instanceof UserNotAuthenticated) { + return new Unauthorized(error.message); + } + if (error instanceof UserNotAllowed) { + return new Forbidden(error.message); + } + if (error instanceof ConflictError) { + return new Conflict(error.message); + } + if (error instanceof EntityError) { + return new ParameterError(error.message); + } + if (error instanceof InvalidParameter) { + return new ParameterError(error.message); + } + if (error instanceof UniqueConstraintError) { + return new Unprocessable_Entity(error.message); + } + if (error instanceof MissingParameter) { + return new ParameterError(error.message); + } + return new InternalServerError(error.message); + } + } +} \ No newline at end of file diff --git a/src/modules/get_all_moderators/app/get_all_moderators_presenter.ts b/src/modules/get_all_moderators/app/get_all_moderators_presenter.ts new file mode 100644 index 0000000..b5fc9f6 --- /dev/null +++ b/src/modules/get_all_moderators/app/get_all_moderators_presenter.ts @@ -0,0 +1,17 @@ +import { GetAllModeratorsUsecase } from "./get_all_moderators_usecase"; +import { GetAllModeratorsController } from "./get_all_moderators_controller"; + +import { Repository } from "../../../core/repositories/Repository"; +import { HttpRequest } from "../../../core/helpers/http/http_codes"; + +const repository = new Repository({ user_repo: true }); + +const usecase = new GetAllModeratorsUsecase(repository.UserRepo); +const controller = new GetAllModeratorsController(usecase); + + +export const handler = async (event: any, context: any) => { + let request = new HttpRequest(event); + let response = await controller.execute(request); + return response.to_json(); +} diff --git a/src/modules/get_all_moderators/app/get_all_moderators_usecase.ts b/src/modules/get_all_moderators/app/get_all_moderators_usecase.ts new file mode 100644 index 0000000..d41bc12 --- /dev/null +++ b/src/modules/get_all_moderators/app/get_all_moderators_usecase.ts @@ -0,0 +1,46 @@ +import { randomUUID } from "crypto"; +import { UniqueConstraintError } from "sequelize"; + +import { User } from '../../../core/structure/entities/User'; +import { TokenAuth } from '../../../core/helpers/functions/token_auth'; +import { UserTypeEnum } from '../../../core/helpers/enums/UserTypeEnum'; +import { IUserRepo } from "../../../core/repositories/interfaces/IUserRepo"; +import { InvalidRequest, MissingParameter, UserNotAllowed, UserNotAuthenticated } from '../../../core/helpers/errors/ModuleError'; + + +export class GetAllModeratorsUsecase { + public token_auth: TokenAuth; + public database_repo: IUserRepo; + + constructor(database_repo: IUserRepo) { + this.token_auth = new TokenAuth(); + this.database_repo = database_repo; + } + + public async execute(headers: { [key: string]: any }) { + if (!headers) { + throw new InvalidRequest("Headers"); + } + if (!headers.Authorization) { + throw new MissingParameter("Authorization"); + } + + const user_id = await this.token_auth + .decode_token(headers.Authorization) + .then((response) => { + return response; + }) + .catch((error) => { + throw new UserNotAuthenticated("Invalid or expired token"); + }); + + const user = await this.database_repo.get_user(user_id); + if (!user) { + throw new UserNotAuthenticated(); + } + + const moderators = await this.database_repo.get_all_moderators(); + + return moderators; + } +} \ No newline at end of file diff --git a/src/modules/get_catalog/app/get_catalog_controller.ts b/src/modules/get_catalog/app/get_catalog_controller.ts new file mode 100644 index 0000000..0468885 --- /dev/null +++ b/src/modules/get_catalog/app/get_catalog_controller.ts @@ -0,0 +1,33 @@ +import { + HttpRequest, + HttpResponse, + OK, + InternalServerError, +} from "../../../core/helpers/http/http_codes"; +import { + InvalidRequest, +} from "../../../core/helpers/errors/ModuleError"; +import { GetCatalogUsecase } from "./get_catalog_usecase"; + + +export class GetCatalogController { + public usecase: GetCatalogUsecase; + + constructor(usecase: GetCatalogUsecase) { + this.usecase = usecase; + } + + public async execute(request: HttpRequest): Promise { + try { + if (!request) { + throw new InvalidRequest(); + } + + const response = await this.usecase.execute(); + return new OK(response, "Activities found successfully"); + + } catch (error: any) { + return new InternalServerError(error.message); + } + } +} diff --git a/src/modules/get_catalog/app/get_catalog_presenter.ts b/src/modules/get_catalog/app/get_catalog_presenter.ts new file mode 100644 index 0000000..c55e4a9 --- /dev/null +++ b/src/modules/get_catalog/app/get_catalog_presenter.ts @@ -0,0 +1,18 @@ +import { Repository } from "../../../core/repositories/Repository"; +import { HttpRequest } from "../../../core/helpers/http/http_codes"; +import { GetCatalogUsecase } from "./get_catalog_usecase"; +import { GetCatalogController } from "./get_catalog_controller"; + +const repository = new Repository({ activity_repo: true, institution_repo: true }); + +const usecase = new GetCatalogUsecase( + repository.ActivityRepo, + repository.InstitutionRepo +); +const controller = new GetCatalogController(usecase); + +export const handler = async (event: any, context: any) => { + let request = new HttpRequest(event); + let response = await controller.execute(request); + return response.to_json(); +}; diff --git a/src/modules/get_catalog/app/get_catalog_usecase.ts b/src/modules/get_catalog/app/get_catalog_usecase.ts new file mode 100644 index 0000000..c3b1184 --- /dev/null +++ b/src/modules/get_catalog/app/get_catalog_usecase.ts @@ -0,0 +1,33 @@ +import { TokenAuth } from "../../../core/helpers/functions/token_auth"; +import { IInstitutionRepo } from "../../../core/repositories/interfaces/IInstitutionRepo"; +import { ActivityTypeEnum } from "../../../core/helpers/enums/ActivityTypeEnum"; +import { IActivityRepo } from "../../../core/repositories/interfaces/IActivityRepo"; + + +export class GetCatalogUsecase { + public token_auth: TokenAuth; + public activity_repo: IActivityRepo; + public institution_repo: IInstitutionRepo; + + constructor(activity_repo: IActivityRepo, institution_repo: IInstitutionRepo) { + this.token_auth = new TokenAuth(); + this.activity_repo = activity_repo; + this.institution_repo = institution_repo; + } + + async execute() { + const activities = await this.activity_repo.get_all_activities_catalog(); + const institutions = await this.institution_repo.get_all_institutions(); + + const projects = activities ? activities.filter((activity) => activity.type_activity === ActivityTypeEnum.PROJECT) : null; + const mobilities = activities ? activities.filter((activity) => activity.type_activity === ActivityTypeEnum.ACADEMIC_MOBILITY) : null; + + const response = { + projects: projects, + mobilities: mobilities, + institutions: institutions + } + + return response; + } +} diff --git a/src/modules/update_activity/app/update_activity_usecase.ts b/src/modules/update_activity/app/update_activity_usecase.ts index 441be77..a774ed9 100644 --- a/src/modules/update_activity/app/update_activity_usecase.ts +++ b/src/modules/update_activity/app/update_activity_usecase.ts @@ -17,6 +17,7 @@ import { IUserRepo } from "../../../core/repositories/interfaces/IUserRepo"; import { EventBridgeManager } from "../../../core/helpers/functions/event_bridge"; import { ActivityStatusEnum } from "../../../core/helpers/enums/ActivityStatusEnum"; import { IActivityRepo } from "../../../core/repositories/interfaces/IActivityRepo"; +import { time } from "console"; export class UpdateActivityUsecase { public token_auth: TokenAuth; @@ -66,166 +67,210 @@ export class UpdateActivityUsecase { throw new UserNotAllowed(); } - if (body.title && await this.activity_repo.check_activity_by_title(body.title)) { - throw new UniqueConstraintError({ - message: "Activity with this title already exists" - }); - } + let activity: Activity | null; + let activity_update: Activity; + let status_activity: ActivityStatusEnum = body.status_activity; - if (body.languages) { - if (!Array.isArray(body.languages)) { - throw new InvalidParameter("Languages", "must be an array of ids"); - } - body.languages.forEach((language_id: number) => { - if (!language_id) { - throw new MissingParameter("Language ID"); + if (!status_activity) { + if (body.title && await this.activity_repo.check_activity_by_title(body.title)) { + throw new UniqueConstraintError({ + message: "Activity with this title already exists" + }); } - if (typeof language_id !== 'number') { - throw new InvalidParameter("Language ID", "must be a number"); - } - }) - } - if (body.courses) { - if (!Array.isArray(body.courses)) { - throw new InvalidParameter("Courses", "must be an array of ids"); - } - body.courses.forEach((course_id: number) => { - if (!course_id) { - throw new MissingParameter("Course ID"); - } - if (typeof course_id !== 'number') { - throw new InvalidParameter("Course ID", "must be a number"); + if (body.languages) { + if (!Array.isArray(body.languages)) { + throw new InvalidParameter("Languages", "must be an array of ids"); + } + body.languages.forEach((language_id: number) => { + if (!language_id) { + throw new MissingParameter("Language ID"); + } + if (typeof language_id !== 'number') { + throw new InvalidParameter("Language ID", "must be a number"); + } + }) } - }) - } - if (body.criterias) { - if (!Array.isArray(body.criterias)) { - throw new InvalidParameter("Criterias", "must be an array of criterias"); - } - body.criterias.forEach((criteria: { id?: number, criteria?: string }) => { - if (criteria.criteria && criteria.id) { - throw new InvalidParameter("Criteria or Criteria ID", "You must provide only the criteria or the criteria id"); - } - if (!criteria.criteria && !criteria.id) { - throw new MissingParameter("Criteria or Criteria ID"); + if (body.courses) { + if (!Array.isArray(body.courses)) { + throw new InvalidParameter("Courses", "must be an array of ids"); + } + body.courses.forEach((course_id: number) => { + if (!course_id) { + throw new MissingParameter("Course ID"); + } + if (typeof course_id !== 'number') { + throw new InvalidParameter("Course ID", "must be a number"); + } + }) } - if (criteria.id && typeof criteria.id !== 'number') { - throw new InvalidParameter("Criteria ID", "must be a number"); + + if (body.criterias) { + if (!Array.isArray(body.criterias)) { + throw new InvalidParameter("Criterias", "must be an array of criterias"); + } + body.criterias.forEach((criteria: { id?: number, criteria?: string }) => { + if (criteria.criteria && criteria.id) { + throw new InvalidParameter("Criteria or Criteria ID", "You must provide only the criteria or the criteria id"); + } + if (!criteria.criteria && !criteria.id) { + throw new MissingParameter("Criteria or Criteria ID"); + } + if (criteria.id && typeof criteria.id !== 'number') { + throw new InvalidParameter("Criteria ID", "must be a number"); + } + if (criteria.criteria && typeof criteria.criteria !== 'string') { + throw new InvalidParameter("Criteria", "must be a string"); + } + }) } - if (criteria.criteria && typeof criteria.criteria !== 'string') { - throw new InvalidParameter("Criteria", "must be a string"); + + if (body.partner_institutions) { + if (!Array.isArray(body.partner_institutions)) { + throw new InvalidParameter("Partner Institutions", "must be an array of ids"); + } + + body.partner_institutions.forEach((institution: string) => { + if (!institution) { + throw new MissingParameter("Partner Institution"); + } + if (typeof institution !== 'string') { + throw new InvalidParameter("Partner Institution", "must be a string"); + } + }) } - }) - } - if (body.partner_institutions) { - if (!Array.isArray(body.partner_institutions)) { - throw new InvalidParameter("Partner Institutions", "must be an array of ids"); + let languages: { id: number }[] = []; + if (body.languages) { + languages = body.languages.map((language_id: number) => { + return { + id: language_id + } + }); } - body.partner_institutions.forEach((institution: string) => { - if (!institution) { - throw new MissingParameter("Partner Institution"); + let courses: { id: number, course?: Course }[] = []; + if (body.courses) { + courses = body.courses.map((course_id: number) => { + return { + id: course_id + } + }); } - if (typeof institution !== 'string') { - throw new InvalidParameter("Partner Institution", "must be a string"); + + let criterias: { id: number, criteria?: Criteria }[] = []; + if (body.criterias) { + criterias = body.criterias.map((criteria: { id?: number, criteria?: string }) => { + return { + id: criteria.id || -1, + criteria: criteria.criteria ? new Criteria({ + id: 1, + criteria: criteria.criteria + }) : null + } + }); } - }) - } - let languages: { id: number }[] = []; - if (body.languages) { - languages = body.languages.map((language_id: number) => { - return { - id: language_id - } - }); - } + let partner_institutions: { id: string }[] = []; + if (body.partner_institutions) { + partner_institutions = body.partner_institutions.map((institution: string) => { + return { + id: institution + }; + }); + } - let courses: { id: number, course?: Course }[] = []; - if (body.courses) { - courses = body.courses.map((course_id: number) => { - return { - id: course_id + let status_activity: ActivityStatusEnum = body.status_activity; + if (status_activity) { + if (!Object.values(ActivityStatusEnum).includes(body.status_activity)) { + throw new InvalidParameter("Status Activity", "Invalid status activity"); } - }); - } - - let criterias: { id: number, criteria?: Criteria }[] = []; - if (body.criterias) { - criterias = body.criterias.map((criteria: { id?: number, criteria?: string }) => { - return { - id: criteria.id || -1, - criteria: criteria.criteria ? new Criteria({ - id: 1, - criteria: criteria.criteria - }) : null + if (![ActivityStatusEnum.ENDED, ActivityStatusEnum.CANCELED].includes(body.status_activity)) { + throw new InvalidParameter("Status Activity", "Invalid status activity"); } - }); - } - - let partner_institutions: { id: string }[] = []; - if (body.partner_institutions) { - partner_institutions = body.partner_institutions.map((institution: string) => { - return { - id: institution - }; - }); - } + } - const activity = await this.activity_repo.get_activity(body.activity_id); - if (!activity) { - throw new NotfoundError("Activity not found"); - } - if (body.start_date && body.end_date) { - if (new Date(body.start_date) < new Date()) { - throw new InvalidParameter("StartDate", "Start Date must be in the future"); + activity = await this.activity_repo.get_activity(body.activity_id); + if (!activity) { + throw new NotfoundError("Activity not found"); } - if (new Date(body.start_date) >= new Date(body.end_date)) { - throw new InvalidParameter("StartDate and EndDate", "Start Date must be before End Date"); + + if (body.start_date || body.end_date) { + let time_now: Date = new Date(); + time_now.setHours(time_now.getHours() - 3); + if (new Date(body.start_date) < time_now) { + throw new InvalidParameter("StartDate", "Start Date must be in the future"); + } + if (new Date(body.start_date) >= new Date(body.end_date)) { + throw new InvalidParameter("StartDate and EndDate", "Start Date must be before End Date"); + } + } else if (body.start_date && !body.end_date) { + if (new Date(body.start_date) >= activity.end_date) { + throw new InvalidParameter("StartDate", "Start Date must be before End Date"); + } + } else if (!body.start_date && body.end_date) { + if (activity.start_date >= new Date(body.end_date)) { + throw new InvalidParameter("EndDate", "End Date must be after Start Date"); + } } - } else if (body.start_date && !body.end_date) { - if (new Date(body.start_date) >= activity.end_date) { - throw new InvalidParameter("StartDate", "Start Date must be before End Date"); + activity_update = new Activity({ + id: activity.id, + title: body.title ? body.title : activity.title, + description: body.description ? body.description : activity.description, + start_date: body.start_date ? new Date(body.start_date) : activity.start_date, + end_date: body.end_date ? new Date(body.end_date) : activity.end_date, + languages: languages.length > 0 ? languages : activity.languages, + courses: courses.length > 0 ? courses : activity.courses, + partner_institutions: partner_institutions.length > 0 ? partner_institutions : activity.partner_institutions, + criterias: criterias.length > 0 ? criterias : activity.criterias, + status_activity: activity.status_activity, + type_activity: activity.type_activity, + created_at: activity.created_at, + updated_at: new Date(), + applicants: activity.applicants + }); + } else { + activity = await this.activity_repo.get_activity(body.activity_id); + if (!activity) { + throw new NotfoundError("Activity not found"); } - } else if (!body.start_date && body.end_date) { - if (activity.start_date >= new Date(body.end_date)) { - throw new InvalidParameter("EndDate", "End Date must be after Start Date"); + if (![ActivityStatusEnum.ENDED, ActivityStatusEnum.CANCELED].includes(body.status_activity)) { + throw new InvalidParameter("Status Activity", "Invalid status activity"); } + activity_update = new Activity({ + id: activity.id, + title: activity.title, + description: activity.description, + start_date: activity.start_date, + end_date: activity.end_date, + languages: activity.languages, + courses: activity.courses, + partner_institutions: activity.partner_institutions, + criterias: activity.criterias, + status_activity: body.status_activity, + type_activity: activity.type_activity, + created_at: activity.created_at, + updated_at: new Date(), + applicants: activity.applicants + }); } - const activity_update: Activity = new Activity({ - id: activity.id, - title: body.title ? body.title : activity.title, - description: body.description ? body.description : activity.description, - start_date: body.start_date ? new Date(body.start_date) : activity.start_date, - end_date: body.end_date ? new Date(body.end_date) : activity.end_date, - languages: languages.length > 0 ? languages : activity.languages, - courses: courses.length > 0 ? courses : activity.courses, - partner_institutions: partner_institutions.length > 0 ? partner_institutions : activity.partner_institutions, - criterias: criterias.length > 0 ? criterias : activity.criterias, - status_activity: body.status_activity ? body.status_activity : activity.status_activity, - type_activity: body.type_activity ? body.type_activity : activity.type_activity, - created_at: activity.created_at, - updated_at: new Date(), - applicants: activity.applicants - }); - await this.activity_repo.update_activity(activity_update).then(async (response) => { if (response && process.env.STAGE === "prod") { if (activity_update.start_date !== activity.start_date) { + let start_date = activity_update.start_date; + start_date.setHours(start_date.getHours() + 3); // Delete the previous trigger and create a new one await this.event_bridge.delete_trigger( - "START_ACTIVITY_" + activity.id, + "START_COIL_" + activity.id.substring(0, 8), "Update_Activity_Event" ); await this.event_bridge.create_trigger( - "START_ACTIVITY_" + activity.id, + "START_COIL_" + activity.id.substring(0, 8), "Update_Activity_Event", - activity_update.start_date, + start_date, { "body": { activity_id: activity.id, @@ -235,15 +280,17 @@ export class UpdateActivityUsecase { ); } if (activity_update.end_date !== activity.end_date) { + let end_date = activity_update.end_date; + end_date.setHours(end_date.getHours() + 3); // Delete the previous trigger and create a new one await this.event_bridge.delete_trigger( - "END_ACTIVITY_" + activity.id, + "END_COIL_" + activity.id.substring(0, 8), "Update_Activity_Event" ); await this.event_bridge.create_trigger( - "END_ACTIVITY_" + activity.id, + "END_COIL_" + activity.id.substring(0, 8), "Update_Activity_Event", - activity_update.end_date, + end_date, { "body": { activity_id: activity.id, diff --git a/src/modules/update_activity_event/app/update_activity_event_presenter.ts b/src/modules/update_activity_event/app/update_activity_event_presenter.ts index 7aa982b..f9aa276 100644 --- a/src/modules/update_activity_event/app/update_activity_event_presenter.ts +++ b/src/modules/update_activity_event/app/update_activity_event_presenter.ts @@ -1,12 +1,19 @@ import { Repository } from "../../../core/repositories/Repository"; +import { EventBridgeManager } from "../../../core/helpers/functions/event_bridge"; +import { ActivityStatusEnum } from "../../../core/helpers/enums/ActivityStatusEnum"; const repository = new Repository({ activity_repo: true }); export const handler = async (event: any, context: any) => { - const body = event.body; - const resp = await repository.ActivityRepo.update_activity_status(body.activity_id, body.status); + const event_bridge = new EventBridgeManager(); + console.log("Event: ", event); + const body: { activity_id: string; status_activity: ActivityStatusEnum } = event.body; + console.log("Activity ID: ", body.activity_id + " Status: " + body.status_activity); + const resp = await repository.ActivityRepo.update_activity_status(body.activity_id, body.status_activity); if (resp) { console.log("Activity updated successfully"); + let event_name = body.status_activity === ActivityStatusEnum.ACTIVE ? "START_COIL_" : "END_COIL_"; + await event_bridge.delete_trigger(event_name + body.activity_id.substring(0, 8), "Update_Activity_Event"); } else { console.log("Activity not found"); } diff --git a/src/modules/update_users_activity/app/update_users_activity_controller.ts b/src/modules/update_users_activity/app/update_users_activity_controller.ts index 7c7f6ba..eece7d7 100644 --- a/src/modules/update_users_activity/app/update_users_activity_controller.ts +++ b/src/modules/update_users_activity/app/update_users_activity_controller.ts @@ -1,5 +1,6 @@ import { UpdateUsersActivityUsecase } from "./update_users_activity_usecase"; import { + ConflictError, InvalidParameter, InvalidRequest, MissingParameter, @@ -16,6 +17,7 @@ import { OK, ParameterError, Unauthorized, + Unprocessable_Entity, } from "../../../core/helpers/http/http_codes"; import { EntityError } from "../../../core/helpers/errors/EntityError"; @@ -54,6 +56,9 @@ export class UpdateUsersActivityController { if (error instanceof NotfoundError) { return new NotFound(error.message); } + if (error instanceof ConflictError) { + return new Unprocessable_Entity(error.message); + } if (error instanceof EntityError) { return new ParameterError(error.message); } diff --git a/src/modules/update_users_activity/app/update_users_activity_usecase.ts b/src/modules/update_users_activity/app/update_users_activity_usecase.ts index a7f0209..c029197 100644 --- a/src/modules/update_users_activity/app/update_users_activity_usecase.ts +++ b/src/modules/update_users_activity/app/update_users_activity_usecase.ts @@ -4,7 +4,8 @@ import { NotfoundError, UserNotAllowed, UserNotAuthenticated, - InvalidParameter + InvalidParameter, + ConflictError } from "../../../core/helpers/errors/ModuleError"; import { TokenAuth } from "../../../core/helpers/functions/token_auth"; import { UserTypeEnum } from "../../../core/helpers/enums/UserTypeEnum"; @@ -95,16 +96,9 @@ export class UpdateUsersActivityUsecase { throw new NotfoundError("Applicants not found"); } - applicants_db.forEach((applicant: {user_id: string, status: boolean}) => { - return { - user_id: applicant.user_id, - status: !applicant.status, - }; - }); - const updateStatusResult = await this.activity_repo.update_users_activity_status(body.activity_id, body.applicants); if (!updateStatusResult) { - throw new NotfoundError("Activity not found"); + throw new ConflictError("Error updating activity status"); } return true; diff --git a/test/modules/create_moderator/app/create_moderator.test.ts b/test/modules/create_moderator/app/create_moderator.test.ts index bae7150..fc08837 100644 --- a/test/modules/create_moderator/app/create_moderator.test.ts +++ b/test/modules/create_moderator/app/create_moderator.test.ts @@ -9,6 +9,7 @@ describe("Testing Create Moderator Presenter", () => { const user_admin = new UserMock().users[0]; const user_student = new UserMock().users[1]; const user_moderator = { + name: "Moderator Test", email: "moderador@maua.br" }; diff --git a/test/modules/delete_moderator/app/delete_moderator.test.ts b/test/modules/delete_moderator/app/delete_moderator.test.ts new file mode 100644 index 0000000..7caed69 --- /dev/null +++ b/test/modules/delete_moderator/app/delete_moderator.test.ts @@ -0,0 +1,49 @@ +import { it, describe, expect } from 'vitest'; +import { UserMock } from '../../../../src/core/structure/mocks/UserMock'; +import { TokenAuth } from '../../../../src/core/helpers/functions/token_auth'; +import { handler } from '../../../../src/modules/delete_moderator/app/delete_moderator_presenter'; + +describe("Testing Delete Moderator Presenter", () => { + const user_admin = new UserMock().users[0]; + const user_moderator = new UserMock().users[2]; + + it("should delete a moderator", async () => { + var token = (await new TokenAuth().generate_token(user_admin.id)).toString(); + + var response = await handler({ + headers: { + Authorization: token + }, + body: JSON.stringify({ moderator_id: user_moderator.id }) + }, null); + + expect(response.statusCode).toBe(200); + expect(JSON.parse(response.body).message).toBe("Moderator deleted successfully"); + }); + + it("should not delete a moderator with invalid token", async () => { + var response = await handler({ + headers: { + Authorization: "invalid_token" + }, + body: JSON.stringify({ moderator_id: user_moderator.id }) + }, null); + + expect(response.statusCode).toBe(401); + expect(JSON.parse(response.body).message).toBe("Invalid or expired token"); + }); + + it("should not delete a moderator with missing parameter moderator_id", async () => { + var token = (await new TokenAuth().generate_token(user_admin.id)).toString(); + + var response = await handler({ + headers: { + Authorization: token + }, + body: null + }, null); + + expect(response.statusCode).toBe(400); + expect(JSON.parse(response.body).message).toBe("Body not found"); + }); +}); diff --git a/test/modules/get_catalog/app/get_catalog.test.ts b/test/modules/get_catalog/app/get_catalog.test.ts new file mode 100644 index 0000000..17a2f3a --- /dev/null +++ b/test/modules/get_catalog/app/get_catalog.test.ts @@ -0,0 +1,16 @@ +import { it, describe, expect } from "vitest"; +import { handler } from "../../../../src/modules/get_catalog/app/get_catalog_presenter"; + +describe("Get Catalog", () => { + it("should return the catolog for hero page", async () => { + const event = { + headers: { + }, + }; + const response = await handler(event, null); + expect(response.statusCode).toBe(200); + }); + +}); + + diff --git a/test/modules/update_activity/app/update_activity.test.ts b/test/modules/update_activity/app/update_activity.test.ts index fef0c8a..c453cdf 100644 --- a/test/modules/update_activity/app/update_activity.test.ts +++ b/test/modules/update_activity/app/update_activity.test.ts @@ -2,10 +2,9 @@ import { it, describe, expect } from 'vitest'; import { UserMock } from '../../../../src/core/structure/mocks/UserMock'; import { TokenAuth } from '../../../../src/core/helpers/functions/token_auth'; -import { ActivityRepoMock } from "../../../../src/core/repositories/mocks/ActivityRepoMock"; -import { handler } from "../../../../src/modules/update_activity/app/update_activity_presenter"; import { ActivityMock } from '../../../../src/core/structure/mocks/ActivityMock'; -import { a } from 'vitest/dist/suite-ynYMzeLu'; +import { ActivityStatusEnum } from '../../../../src/core/helpers/enums/ActivityStatusEnum'; +import { handler } from "../../../../src/modules/update_activity/app/update_activity_presenter"; describe("Update Activity Presenter", () => { const user_admin = new UserMock().users[0]; @@ -27,7 +26,7 @@ describe("Update Activity Presenter", () => { partner_institutions: [activity.partner_institutions[0].id], criterias: [{ id: activity.criterias[0].id }], courses: [activity.courses[0].course?.id], - status_activity: activity.status_activity, + status_activity: ActivityStatusEnum.ENDED, type_activity: activity.type_activity, start_date: activity.start_date, end_date: activity.end_date, @@ -52,7 +51,7 @@ describe("Update Activity Presenter", () => { partner_institutions: [], criterias: [], courses: [], - status_activity: "ACTIVE", + status_activity: null, type_activity: "PROJECT", start_date: new Date(), end_date: new Date(), @@ -79,7 +78,7 @@ describe("Update Activity Presenter", () => { partner_institutions: [activity.partner_institutions[0].id], criterias: [{ id: activity.criterias[0].id }], courses: [activity.courses[0].course?.id], - status_activity: activity.status_activity, + status_activity: null, type_activity: activity.type_activity, start_date: new Date().getTime() - 1000 * 60 * 60 * 24 * 7, end_date: new Date(), @@ -133,7 +132,7 @@ describe("Update Activity Presenter", () => { partner_institutions: [activity.partner_institutions[0].id], criterias: [activity.criterias[0].criteria], courses: [{ id: activity.courses[0].id, name: activity.courses[0].name }], - status_activity: activity.status_activity, + status_activity: null, type_activity: activity.type_activity, start_date: new Date(), end_date: new Date(),