Skip to content

Commit

Permalink
COM-687 Add automatic deletion process (#59)
Browse files Browse the repository at this point in the history
* add job for deleting blacklistedContacts

* add changeset

* fix prettier error

* refactor findNonMainTargetGroups to findMainTargetGroups including where parameter

* add where parameter to brevo-contacts-services functions

* use findMainTargetGroups function to get scopes for deleting contacts

* fix prettier problems

* fix prettier problems

* add @CreateRequestContext to execute function

* fix pipeline

* correct scope by using dynamic values

* rename variable numberOfBlacklistedContacts to hasMoreContacts

* correct condition to check if more contacts are avaiable

* rename findMainTargetGroups to findTargetGroups

* correct type of where in findTargetGroups

* use scope directly

* fix lint error

* adapt changeset to only include information that is relevant for the application

* remove scope from findTargetGroups and pass it in the prop instead

* correct hasMoreContacts

* rename DeleteUnsubscribedContactsConsole to DeleteUnsubscribedBrevoContactsConsole

---------

Co-authored-by: Julia Wegmayr <[email protected]>
  • Loading branch information
juliawegmayr and Julia Wegmayr authored Aug 6, 2024
1 parent e56d830 commit d21db92
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 14 deletions.
5 changes: 5 additions & 0 deletions .changeset/cold-clouds-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@comet/brevo-api": minor
---

Add `DeleteUnsubscribedBrevoContactsConsole` job to enable the deletion of blocklisted contacts. This job can be utilized as a cronjob to periodically clean up the blocklisted contacts.
17 changes: 17 additions & 0 deletions packages/api/src/brevo-api/brevo-api-contact.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,23 @@ export class BrevoApiContactsService {
return [data.body.contacts, data.body.count];
}

public async findContacts(limit: number, offset: number, scope: EmailCampaignScopeInterface): Promise<BrevoContactInterface[]> {
const data = await this.getContactsApi(scope).getContacts(limit, offset);

return data.body.contacts;
}

public async deleteContacts(contacts: BrevoContactInterface[], scope: EmailCampaignScopeInterface): Promise<boolean> {
for (const contact of contacts) {
const idAsString = contact.id.toString();
const response = await this.getContactsApi(scope).deleteContact(idAsString);
if (response.response.statusCode !== 204) {
return false;
}
}
return true;
}

public async blacklistMultipleContacts(emails: string[], scope: EmailCampaignScopeInterface): Promise<void> {
const blacklistedContacts = emails.map((email) => ({ email, emailBlacklisted: true }));

Expand Down
49 changes: 49 additions & 0 deletions packages/api/src/brevo-contact/brevo-contact.console.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { CreateRequestContext, MikroORM } from "@mikro-orm/core";
import { Injectable } from "@nestjs/common";
import { Command, Console } from "nestjs-console";

import { BrevoApiContactsService } from "../brevo-api/brevo-api-contact.service";
import { TargetGroupsService } from "../target-group/target-groups.service";

@Injectable()
@Console()
export class DeleteUnsubscribedBrevoContactsConsole {
constructor(
private readonly brevoApiContactsService: BrevoApiContactsService,
private readonly targetGroupsService: TargetGroupsService,
private readonly orm: MikroORM,
) {}

@Command({
command: "delete-unsubscribed-brevo-contacts",
description: "deletes unsubscribed contacts",
})
@CreateRequestContext()
async execute(): Promise<void> {
const offset = 0;
const limit = 50;
const where = { isMainList: true };

const [targetGroups] = await this.targetGroupsService.findTargetGroups({ offset, limit, where });

for (const targetGroup of targetGroups) {
let hasMoreContacts = false;
let offset = 0;

do {
const contacts = await this.brevoApiContactsService.findContacts(limit, offset, {
scope: targetGroup.scope,
});

const blacklistedContacts = contacts.filter((contact) => contact.emailBlacklisted === true);

if (blacklistedContacts.length > 0) {
await this.brevoApiContactsService.deleteContacts(blacklistedContacts, { scope: targetGroup.scope });
}

hasMoreContacts = !(contacts.length < limit);
offset += limit;
} while (hasMoreContacts);
}
}
}
9 changes: 8 additions & 1 deletion packages/api/src/brevo-contact/brevo-contact.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BrevoApiModule } from "../brevo-api/brevo-api.module";
import { ConfigModule } from "../config/config.module";
import { TargetGroupInterface } from "../target-group/entity/target-group-entity.factory";
import { BrevoContactAttributesInterface, EmailCampaignScopeInterface } from "../types";
import { DeleteUnsubscribedBrevoContactsConsole } from "./brevo-contact.console";
import { createBrevoContactResolver } from "./brevo-contact.resolver";
import { BrevoContactsService } from "./brevo-contacts.service";
import { BrevoContactFactory } from "./dto/brevo-contact.factory";
Expand Down Expand Up @@ -36,7 +37,13 @@ export class BrevoContactModule {
return {
module: BrevoContactModule,
imports: [BrevoApiModule, ConfigModule, MikroOrmModule.forFeature([TargetGroup])],
providers: [BrevoContactsService, BrevoContactResolver, EcgRtrListService, IsValidRedirectURLConstraint],
providers: [
BrevoContactsService,
BrevoContactResolver,
EcgRtrListService,
IsValidRedirectURLConstraint,
DeleteUnsubscribedBrevoContactsConsole,
],
exports: [BrevoContactsService],
};
}
Expand Down
6 changes: 4 additions & 2 deletions packages/api/src/brevo-contact/brevo-contacts.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ export class BrevoContactsService {
let totalCount = 0;
const targetGroupIds: number[] = [];
const limit = 50;
const where = { isMainList: false, scope };

do {
const [targetGroups, totalContactLists] = await this.targetGroupService.findNonMainTargetGroups({ scope, offset, limit });
const [targetGroups, totalContactLists] = await this.targetGroupService.findTargetGroups({ offset, limit, where });
totalCount = totalContactLists;
offset += targetGroups.length;

Expand Down Expand Up @@ -102,9 +103,10 @@ export class BrevoContactsService {
let totalCount = 0;
const targetGroupIds: number[] = [];
const limit = 50;
const where = { isMainList: false, scope };

do {
const [targetGroups, totalContactLists] = await this.targetGroupService.findNonMainTargetGroups({ scope, offset, limit });
const [targetGroups, totalContactLists] = await this.targetGroupService.findTargetGroups({ offset, limit, where });
totalCount = totalContactLists;
offset += targetGroups.length;

Expand Down
16 changes: 5 additions & 11 deletions packages/api/src/target-group/target-groups.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { filtersToMikroOrmQuery, searchToMikroOrmQuery } from "@comet/cms-api";
import { EntityManager, EntityRepository, ObjectQuery } from "@mikro-orm/core";
import { EntityManager, EntityRepository, FilterQuery, ObjectQuery } from "@mikro-orm/core";
import { InjectRepository } from "@mikro-orm/nestjs";
import { Injectable } from "@nestjs/common";

Expand Down Expand Up @@ -106,22 +106,16 @@ export class TargetGroupsService {
return true;
}

async findNonMainTargetGroups({
scope,
async findTargetGroups({
offset,
limit,
where,
}: {
scope?: EmailCampaignScopeInterface;
offset: number;
limit: number;
where: FilterQuery<TargetGroupInterface>;
}): Promise<[TargetGroupInterface[], number]> {
const [targetGroups, totalContactLists] = await this.repository.findAndCount(
{
isMainList: false,
...(scope ? { scope } : {}),
},
{ limit, offset },
);
const [targetGroups, totalContactLists] = await this.repository.findAndCount(where, { offset, limit });

return [targetGroups, totalContactLists];
}
Expand Down

0 comments on commit d21db92

Please sign in to comment.