Skip to content

Commit

Permalink
Merge pull request #62 from alkem-io/develop
Browse files Browse the repository at this point in the history
Release: Notifications filtering based on preferences
  • Loading branch information
valentinyanakiev authored Dec 7, 2021
2 parents d9cc9ef + 9f34143 commit 912edd2
Show file tree
Hide file tree
Showing 28 changed files with 501 additions and 114 deletions.
4 changes: 3 additions & 1 deletion notifications.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ monitoring:

notification_providers:
email:
#multi-provider strategy. Values can be fallback | no-fallback | roundrobin
multi_provider_strategy: ${EMAIL_MULTI_PROVIDER_STRATEGY}:fallback
smtp:
host: ${EMAIL_SMTP_HOST}:localhost
port: ${EMAIL_SMTP_PORT}:1025
Expand Down Expand Up @@ -145,7 +147,7 @@ recipients:
- name: admin
rules:
- rule:
type: GLOBAL_COMMUNITY_ADMIN
type: GLOBAL_ADMIN_COMMUNITY
resource_id:
- rule:
type: GLOBAL_ADMIN
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "alkemio-notifications",
"version": "0.4.0",
"version": "0.4.2",
"description": "Alkemio notifications service",
"author": "Cherrytwist Foundation",
"private": false,
Expand Down Expand Up @@ -34,7 +34,7 @@
"validate-connection": "ts-node src/utils/validate-connection.ts"
},
"dependencies": {
"@alkemio/client-lib": "^0.10.1",
"@alkemio/client-lib": "^0.10.2",
"@nestjs/axios": "^0.0.1",
"@nestjs/common": "^8.0.5",
"@nestjs/config": "^1.0.1",
Expand Down
2 changes: 1 addition & 1 deletion src/app.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class AppController {
this.sendNotifications(
eventPayload,
context,
this.notificationService.sendCommunicationUpdateddNotification(
this.notificationService.sendCommunicationUpdatedNotification(
eventPayload
),
COMMUNICATION_UPDATE_SENT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type TemplateConfig = {
application_created?: TemplateRuleSet[];
user_registered?: TemplateRuleSet[];
communication_update_sent?: TemplateRuleSet[];
communication_discussion_created?: TemplateRuleSet[];
};

export interface INotificationRecipientTemplateProvider {
Expand Down
14 changes: 14 additions & 0 deletions src/core/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,18 @@ export type User = {
lastName: string;
displayName: string;
email: string;
preferences?: UserPreference[];
};

export type UserPreference = {
definition: UserPreferenceDefinition;
value: string;
};

export type UserPreferenceDefinition = {
group: string;
displayName: string;
description: string;
valueType: string;
type: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { AlkemioClientAdapterModule } from './alkemio.client.adapter.module';
import * as challengeAdminsData from '@test/data/challenge.admins.json';
import * as opportunityAdminsData from '@test/data/opportunity.admins.json';
import * as hubAdminsData from '@test/data/hub.admins.json';
import * as eventPayload from '@test/data/event.payload.json';
import * as eventPayload from '@test/data/event.application.created.payload.json';
import { AlkemioClientAdapter } from './alkemio.client.adapter';

const testData = {
Expand Down Expand Up @@ -64,9 +64,7 @@ describe('AlkemioAdapter', () => {
it('Should throw an error', async () => {
jest.spyOn(alkemioClient, 'user').mockResolvedValue(undefined);

expect(
alkemioAdapter.getUser(testData.eventPayload.data.hub.id)
).rejects.toThrow();
expect(alkemioAdapter.getUser(testData.data.hub.id)).rejects.toThrow();
});

it('Should return true', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ export class AlkemioClientAdapter implements IFeatureFlagProvider {

const users = await this.alkemioClient.usersWithAuthorizationCredential(
credentialCriteria.type,
resourceID
resourceID,
true
);
if (!users) return [];
return users;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ApplicationCreatedEventPayload } from '@src/types/application.created.e
import { EmailTemplate } from '@src/common/enums/email.template';
import { AlkemioClientAdapter } from '@src/services';
import { AlkemioUrlGenerator } from '@src/services/application/alkemio-url-generator';
import { UserPreferenceType } from '@alkemio/client-lib';

@Injectable()
export class ApplicationCreatedNotificationBuilder {
Expand All @@ -40,14 +41,16 @@ export class ApplicationCreatedNotificationBuilder {
eventPayload,
'admin',
EmailTemplate.USER_APPLICATION_ADMIN,
applicant
applicant,
UserPreferenceType.NotificationApplicationReceived
);

const applicantNotificationPromises = await this.buildNotificationsForRole(
eventPayload,
'applicant',
EmailTemplate.USER_APPLICATION_APPLICANT,
applicant
applicant,
UserPreferenceType.NotificationApplicationSubmitted
);
return Promise.all([
...adminNotificationPromises,
Expand All @@ -59,7 +62,8 @@ export class ApplicationCreatedNotificationBuilder {
eventPayload: any,
recipientRole: string,
emailTemplate: EmailTemplate,
applicant: User
applicant: User,
preferenceType: UserPreferenceType
): Promise<any> {
this.logger.verbose?.(
`Notifications [${emailTemplate}] - role '${recipientRole}`,
Expand All @@ -82,12 +86,27 @@ export class ApplicationCreatedNotificationBuilder {
credentialCriterias
);

const filteredRecipients: User[] = [];
for (const recipient of recipients) {
if (recipient.preferences) {
if (
recipient.preferences.find(
preference =>
preference.definition.group === 'Notification' &&
preference.definition.type === preferenceType &&
preference.value === 'true'
)
)
filteredRecipients.push(recipient);
}
}

this.logger.verbose?.(
`Notifications [${emailTemplate}] - identified ${recipients.length} recipients`,
`Notifications [${emailTemplate}] - identified ${filteredRecipients.length} recipients`,
LogContext.NOTIFICATIONS
);

const notifications = recipients.map(recipient =>
const notifications = filteredRecipients.map(recipient =>
this.buildNotification(eventPayload, recipient, emailTemplate, applicant)
);

Expand Down Expand Up @@ -140,9 +159,9 @@ export class ApplicationCreatedNotificationBuilder {
applicant.nameID
);
const communityURL = this.alkemioUrlGenerator.createCommunityURL(
eventPayload.hub.id,
eventPayload.hub.challenge?.id,
eventPayload.hub.challenge?.opportunity?.id
eventPayload.hub.nameID,
eventPayload.hub.challenge?.nameID,
eventPayload.hub.challenge?.opportunity?.nameID
);
return {
emailFrom: '[email protected]',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ConfigService } from '@nestjs/config';
import { AlkemioClientAdapter } from '@src/services';
import { CommunicationDiscussionCreatedEventPayload } from '@src/types/communication.discussion.created.event.payload';
import { AlkemioUrlGenerator } from '@src/services/application/alkemio-url-generator';
import { UserPreferenceType } from '@alkemio/client-lib';

@Injectable()
export class CommunicationDiscussionCreatedNotificationBuilder {
Expand All @@ -38,7 +39,7 @@ export class CommunicationDiscussionCreatedNotificationBuilder {
)?.webclient_endpoint;
}

async sendNotifications(
async buildNotifications(
eventPayload: CommunicationDiscussionCreatedEventPayload
) {
this.logger.verbose?.(
Expand All @@ -52,18 +53,19 @@ export class CommunicationDiscussionCreatedNotificationBuilder {
eventPayload.discussion.createdBy
);

const adminNotificationPromises = await this.sendNotificationsForRole(
const adminNotificationPromises = await this.buildNotificationsForRole(
eventPayload,
'admin',
EmailTemplate.COMMUNICATION_UPDATE_ADMIN,
EmailTemplate.COMMUNICATION_DISCUSSION_CREATED_ADMIN,
sender
);

const memberNotificationPromises = await this.sendNotificationsForRole(
const memberNotificationPromises = await this.buildNotificationsForRole(
eventPayload,
'member',
EmailTemplate.COMMUNICATION_UPDATE_MEMBER,
sender
EmailTemplate.COMMUNICATION_DISCUSSION_CREATED_MEMBER,
sender,
UserPreferenceType.NotificationCommunicationDiscussionCreated
);

return Promise.all([
Expand All @@ -72,11 +74,12 @@ export class CommunicationDiscussionCreatedNotificationBuilder {
]);
}

async sendNotificationsForRole(
async buildNotificationsForRole(
eventPayload: CommunicationDiscussionCreatedEventPayload,
recipientRole: string,
emailTemplate: EmailTemplate,
sender: User
sender: User,
preferenceType?: UserPreferenceType
): Promise<any> {
this.logger.verbose?.(
`Notifications [${emailTemplate}] - recipients role: '${recipientRole}`,
Expand All @@ -85,7 +88,8 @@ export class CommunicationDiscussionCreatedNotificationBuilder {
// Get the lookup map
const lookupMap = this.createLookupMap(eventPayload);
const userRegistrationRuleSets =
this.recipientTemplateProvider.getTemplate().user_registered;
this.recipientTemplateProvider.getTemplate()
.communication_discussion_created;

const credentialCriterias =
this.recipientTemplateProvider.getCredentialCriterias(
Expand All @@ -99,12 +103,28 @@ export class CommunicationDiscussionCreatedNotificationBuilder {
credentialCriterias
);

const filteredRecipients: User[] = [];
for (const recipient of recipients) {
if (recipient.preferences) {
if (
!preferenceType ||
recipient.preferences.find(
preference =>
preference.definition.group === 'Notification' &&
preference.definition.type === preferenceType &&
preference.value === 'true'
)
)
filteredRecipients.push(recipient);
}
}

this.logger.verbose?.(
`Notifications [${emailTemplate}] - identified ${recipients.length} recipients`,
`Notifications [${emailTemplate}] - identified ${filteredRecipients.length} recipients`,
LogContext.NOTIFICATIONS
);

const notifications = recipients.map(recipient =>
const notifications = filteredRecipients.map(recipient =>
this.buildNotification(eventPayload, recipient, emailTemplate, sender)
);

Expand Down Expand Up @@ -153,9 +173,9 @@ export class CommunicationDiscussionCreatedNotificationBuilder {
sender: User
): any {
const communityURL = this.alkemioUrlGenerator.createCommunityURL(
eventPayload.hub.id,
eventPayload.hub.challenge?.id,
eventPayload.hub.challenge?.opportunity?.id
eventPayload.hub.nameID,
eventPayload.hub.challenge?.nameID,
eventPayload.hub.challenge?.opportunity?.nameID
);
const senderProfile = this.alkemioUrlGenerator.createUserURL(sender.nameID);
return {
Expand Down
Loading

0 comments on commit 912edd2

Please sign in to comment.