From 4c9f585cf76d2d501aad8cbd2b2d43e866f371a5 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Sun, 14 Nov 2021 20:29:21 +0100 Subject: [PATCH 1/8] added templates for emails for new user registration; refactor logic for loading in config --- notifications.yml | 23 +++++++- package.json | 2 +- quickstart-mailslurper.yml | 1 + ...tification.recipient.provider.interface.ts | 6 +- ...n.recipient.template.provider.interface.ts | 12 ++-- .../notified.users.provider.interface.ts | 6 +- .../alkemio.client.adapter.ts | 8 +-- .../application.notification.builder.ts | 58 ++++++++++++++++--- .../template.to.credential.mapper.spec.ts | 6 +- .../template.to.credential.mapper.ts | 25 +++++--- .../utils/utils.ts | 17 ++---- src/templates/user.registration.admin.js | 22 +++++++ src/templates/user.registration.registrant.js | 29 ++++++++++ src/types/index.ts | 2 +- .../user.registration.event.payload.d.ts | 6 ++ 15 files changed, 173 insertions(+), 50 deletions(-) create mode 100644 src/templates/user.registration.admin.js create mode 100644 src/templates/user.registration.registrant.js create mode 100644 src/types/user.registration.event.payload.d.ts diff --git a/notifications.yml b/notifications.yml index b4227229..c752e188 100644 --- a/notifications.yml +++ b/notifications.yml @@ -84,7 +84,8 @@ kratos: recipients: application_created: - admin: + - name: admin + rules: - rule: type: CHALLENGE_ADMIN resource_id: @@ -94,10 +95,28 @@ recipients: - rule: type: ECOVERSE_ADMIN resource_id: + - rule: + type: GLOBAL_COMMUNITY_ADMIN + resource_id: <> - rule: type: GLOBAL_ADMIN resource_id: <> - applicant: + - name: applicant + rules: - rule: type: USER_SELF_MANAGEMENT resource_id: + user_registration: + - name: admin + rules: + - rule: + type: GLOBAL_COMMUNITY_ADMIN + resource_id: <> + - rule: + type: GLOBAL_ADMIN + resource_id: <> + - name: registrant + rules: + - rule: + type: USER_SELF_MANAGEMENT + resource_id: XX diff --git a/package.json b/package.json index d7d97664..c6e33cff 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", - "start:services": "docker-compose -f quickstart-mailslurper.yml --env-file .env.docker up --build --force-recreate", + "start:services": "docker-compose -f quickstart-mailslurper.yml up --build --force-recreate", "lint": "tsc --noEmit && eslint src/**/*.ts{,x}", "lint:prod": "tsc --noEmit && cross-env NODE_ENV=production eslint src/**/*.ts{,x}", "lint:fix": "tsc --noEmit && eslint src/**/*.ts{,x} --fix", diff --git a/quickstart-mailslurper.yml b/quickstart-mailslurper.yml index 56cc0126..8d4dd0c0 100644 --- a/quickstart-mailslurper.yml +++ b/quickstart-mailslurper.yml @@ -6,6 +6,7 @@ networks: services: mailslurper: container_name: alkemio_notifications_mailslurper + ## todo: fixed version image: oryd/mailslurper:latest-smtps ports: - 4436:4436 diff --git a/src/core/contracts/notification.recipient.provider.interface.ts b/src/core/contracts/notification.recipient.provider.interface.ts index 12b33e66..e13b42e7 100644 --- a/src/core/contracts/notification.recipient.provider.interface.ts +++ b/src/core/contracts/notification.recipient.provider.interface.ts @@ -2,13 +2,13 @@ import { AuthorizationCredential } from '@alkemio/client-lib'; import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; export type RecipientCredential = { - role: AuthorizationCredential; + type: AuthorizationCredential; resourceID?: string; - isAdmin: boolean; }; export interface INotificationRecipientProvider { getApplicationCreatedRecipients( - payload: ApplicationCreatedEventPayload + payload: ApplicationCreatedEventPayload, + roleName: string ): RecipientCredential[]; } diff --git a/src/core/contracts/notification.recipient.template.provider.interface.ts b/src/core/contracts/notification.recipient.template.provider.interface.ts index a6139289..605dae1c 100644 --- a/src/core/contracts/notification.recipient.template.provider.interface.ts +++ b/src/core/contracts/notification.recipient.template.provider.interface.ts @@ -3,17 +3,19 @@ import { AuthorizationCredential } from '@alkemio/client-lib'; export type TemplateRule = { rule: { type: AuthorizationCredential; - resource_id: string; + resource_id: string | undefined; }; }; -export type TemplateRow = { - admin: TemplateRule[]; - applicant: TemplateRule[]; +export type TemplateRuleSet = { + name: string; + rules: TemplateRule[]; }; export type TemplateConfig = { - application_created?: TemplateRow; + application_created?: TemplateRuleSet[]; + user_registration?: TemplateRuleSet[]; + communication_update?: TemplateRuleSet[]; }; export interface INotificationRecipientTemplateProvider { diff --git a/src/core/contracts/notified.users.provider.interface.ts b/src/core/contracts/notified.users.provider.interface.ts index 4b43cf0e..f6945678 100644 --- a/src/core/contracts/notified.users.provider.interface.ts +++ b/src/core/contracts/notified.users.provider.interface.ts @@ -6,8 +6,8 @@ export interface INotifiedUsersProvider { getChallengeAdmins(challengeID: string): Promise | User[]; getOpportunityAdmins(opportunityID: string): Promise | User[]; getApplicant(payload: any): Promise | User; - getUsersWithCredentials( - credential: AuthorizationCredential, - resourceID?: string + getUsersMatchingCredentialCriteria( + credentialType: AuthorizationCredential, + credentialResourceID?: string ): Promise; } diff --git a/src/services/alkemio-client-adapter/alkemio.client.adapter.ts b/src/services/alkemio-client-adapter/alkemio.client.adapter.ts index 63697e7b..1280e291 100644 --- a/src/services/alkemio-client-adapter/alkemio.client.adapter.ts +++ b/src/services/alkemio-client-adapter/alkemio.client.adapter.ts @@ -41,27 +41,27 @@ export class AlkemioClientAdapter } async getOpportunityAdmins(opportunityID: string): Promise { - return await this.getUsersWithCredentials( + return await this.getUsersMatchingCredentialCriteria( AuthorizationCredential.OpportunityAdmin, opportunityID ); } async getHubAdmins(ecoverseID: string): Promise { - return await this.getUsersWithCredentials( + return await this.getUsersMatchingCredentialCriteria( AuthorizationCredential.EcoverseAdmin, ecoverseID ); } async getChallengeAdmins(challengeID: string): Promise { - return await this.getUsersWithCredentials( + return await this.getUsersMatchingCredentialCriteria( AuthorizationCredential.ChallengeAdmin, challengeID ); } - async getUsersWithCredentials( + async getUsersMatchingCredentialCriteria( credential: AuthorizationCredential, resourceID?: string ): Promise { diff --git a/src/services/application-notification-builder/application.notification.builder.ts b/src/services/application-notification-builder/application.notification.builder.ts index b8655c58..8d8fe6a0 100644 --- a/src/services/application-notification-builder/application.notification.builder.ts +++ b/src/services/application-notification-builder/application.notification.builder.ts @@ -3,15 +3,20 @@ import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { ALKEMIO_CLIENT_ADAPTER, LogContext, + NOTIFICATION_RECIPIENTS_YML_ADAPTER, TEMPLATE_PROVIDER, } from '@src/common'; import { NotificationTemplateBuilder } from '@src/wrappers/notifme/notification.templates.builder'; import { INotificationRecipientProvider, + INotificationRecipientTemplateProvider, INotifiedUsersProvider, + RecipientCredential, } from '@core/contracts'; import { User } from '@core/models'; import { TemplateToCredentialMapper } from '../template-to-credential-mapper'; +import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; +import { ruleToCredential } from '../template-to-credential-mapper/utils/utils'; @Injectable() export class ApplicationNotificationBuilder { @@ -23,20 +28,31 @@ export class ApplicationNotificationBuilder { @Inject(ALKEMIO_CLIENT_ADAPTER) private readonly notifiedUsersService: INotifiedUsersProvider, @Inject(TEMPLATE_PROVIDER) - private readonly notificationTemplateBuilder: NotificationTemplateBuilder + private readonly notificationTemplateBuilder: NotificationTemplateBuilder, + @Inject(NOTIFICATION_RECIPIENTS_YML_ADAPTER) + private readonly recipientTemplateProvider: INotificationRecipientTemplateProvider ) {} async buildNotifications(payload: any): Promise { - const receiverCredentials = - this.notificationReceivers.getApplicationCreatedRecipients(payload); + const adminCredentials = this.getApplicationCreatedRecipients( + payload, + 'admin' + ); + + // const applicantsCredentials = + // this.notificationReceivers.getApplicationCreatedRecipients( + // payload, + // 'applicants' + // ); const applicant = await this.notifiedUsersService.getApplicant(payload); - const adminRequests = receiverCredentials - .filter(x => x.isAdmin) - .map(({ role, resourceID }) => - this.notifiedUsersService.getUsersWithCredentials(role, resourceID) - ); + const adminRequests = adminCredentials.map(credential => + this.notifiedUsersService.getUsersMatchingCredentialCriteria( + credential.type, + credential.resourceID + ) + ); const settledAdminUsers = await Promise.allSettled(adminRequests); @@ -57,6 +73,7 @@ export class ApplicationNotificationBuilder { ); // todo what to do with the applicant field in the template?? + // todo: need to send a message to all users configured to receive the applicants email; cannot assume it is just one. const applicantNotification = this.buildUserNotification( payload, @@ -88,6 +105,31 @@ export class ApplicationNotificationBuilder { buildNotification = (payload: any, templateName: string) => this.notificationTemplateBuilder.buildTemplate(templateName, payload); + + public getApplicationCreatedRecipients( + payload: ApplicationCreatedEventPayload, + roleName: string + ): RecipientCredential[] { + const applicationCreatedTemplate = + this.recipientTemplateProvider.getTemplate().application_created; + + if (!applicationCreatedTemplate) { + return []; + } + + const ruleSetForRole = applicationCreatedTemplate.find( + templateRuleSet => templateRuleSet.name === roleName + ); + + if (!ruleSetForRole) { + this.logger.error(`Unable to identify rule set for role: ${roleName}`); + return []; + } + + const rules = ruleSetForRole.rules; + + return rules.map(x => ruleToCredential(x, payload)); + } } const getBaseNotification = (payload: any, applicant: User) => ({ diff --git a/src/services/template-to-credential-mapper/template.to.credential.mapper.spec.ts b/src/services/template-to-credential-mapper/template.to.credential.mapper.spec.ts index be96211f..a0116ae6 100644 --- a/src/services/template-to-credential-mapper/template.to.credential.mapper.spec.ts +++ b/src/services/template-to-credential-mapper/template.to.credential.mapper.spec.ts @@ -72,17 +72,17 @@ describe('TemplateToCredentialMapper', () => { const expectedResponse: RecipientCredential[] = [ { - role: AuthorizationCredential.EcoverseAdmin, + type: AuthorizationCredential.EcoverseAdmin, resourceID: 'hub', isAdmin: true, }, { - role: AuthorizationCredential.GlobalAdmin, + type: AuthorizationCredential.GlobalAdmin, resourceID: undefined, isAdmin: true, }, { - role: AuthorizationCredential.UserSelfManagement, + type: AuthorizationCredential.UserSelfManagement, resourceID: 'applicant', isAdmin: false, }, diff --git a/src/services/template-to-credential-mapper/template.to.credential.mapper.ts b/src/services/template-to-credential-mapper/template.to.credential.mapper.ts index c9493da5..0690ee43 100644 --- a/src/services/template-to-credential-mapper/template.to.credential.mapper.ts +++ b/src/services/template-to-credential-mapper/template.to.credential.mapper.ts @@ -5,9 +5,9 @@ import { INotificationRecipientTemplateProvider, RecipientCredential, } from '@core/contracts'; -import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; import { NOTIFICATION_RECIPIENTS_YML_ADAPTER } from '@src/common'; import { ruleToCredential } from './utils/utils'; +import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; // todo tests @Injectable() @@ -22,20 +22,27 @@ export class TemplateToCredentialMapper ) {} public getApplicationCreatedRecipients( - payload: ApplicationCreatedEventPayload + payload: ApplicationCreatedEventPayload, + roleName: string ): RecipientCredential[] { - const template = this.recipientTemplateProvider.getTemplate(); + const applicationCreatedTemplate = + this.recipientTemplateProvider.getTemplate().application_created; - if (!template.application_created) { + if (!applicationCreatedTemplate) { return []; } - const { admin = [], applicant = [] } = template.application_created; + const ruleSetForRole = applicationCreatedTemplate.find( + templateRuleSet => templateRuleSet.name === roleName + ); + + if (!ruleSetForRole) { + this.logger.error(`Unable to identify rule set for role: ${roleName}`); + return []; + } - const admins = admin.map(x => ruleToCredential(x, payload, true)); - const applicants = applicant.map(x => ruleToCredential(x, payload)); + const rules = ruleSetForRole.rules; - // and filter out the mismatches - return [...admins, ...applicants].filter(x => x) as RecipientCredential[]; + return rules.map(x => ruleToCredential(x, payload)); } } diff --git a/src/services/template-to-credential-mapper/utils/utils.ts b/src/services/template-to-credential-mapper/utils/utils.ts index cd7c0be5..6ff747be 100644 --- a/src/services/template-to-credential-mapper/utils/utils.ts +++ b/src/services/template-to-credential-mapper/utils/utils.ts @@ -1,6 +1,6 @@ import { RecipientCredential, TemplateRule } from '@core/contracts'; -import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; import { AuthorizationCredential } from '@alkemio/client-lib'; +import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; /*** * Returns a credential from the payload based on the rule provided @@ -11,21 +11,16 @@ import { AuthorizationCredential } from '@alkemio/client-lib'; */ export const ruleToCredential = ( templateRule: TemplateRule, - payload: ApplicationCreatedEventPayload, - isAdmin = false -): RecipientCredential | undefined => { + payload: ApplicationCreatedEventPayload +): RecipientCredential => { const { rule } = templateRule; - const resourceID = getResourceId(rule.type, rule.resource_id, payload); + const resourceID = getResourceId(rule.type, rule.resource_id || '', payload); if (resourceID === null) { - return undefined; + rule.resource_id = undefined; } - return { - role: templateRule.rule.type, - resourceID, - isAdmin, - }; + return rule; }; /*** diff --git a/src/templates/user.registration.admin.js b/src/templates/user.registration.admin.js new file mode 100644 index 00000000..10bfa67c --- /dev/null +++ b/src/templates/user.registration.admin.js @@ -0,0 +1,22 @@ +/* eslint-disable quotes */ +module.exports = () => ({ + name: 'user-registration-admin', + title: 'New user registration {{user.firstname}} {{user.lastname}}', + version: 1, + channels: { + email: { + from: '{{emailFrom}}', + to: '{{admin.email}}', + subject: 'New user registration: {{user.name}}', + html: `{% extends "src/templates/_layouts/email-transactional.html" %} + {% block content %} + Hi {{admin.firstname}},

+ + There is a new user registration: {{user.name}}, with email {{user.email}}

+ + Sincerely yours, + Team Alkemio + {% endblock %}`, + }, + }, +}); diff --git a/src/templates/user.registration.registrant.js b/src/templates/user.registration.registrant.js new file mode 100644 index 00000000..41763121 --- /dev/null +++ b/src/templates/user.registration.registrant.js @@ -0,0 +1,29 @@ +/* eslint-disable quotes */ +module.exports = () => ({ + name: 'user-registration-registrant', + title: 'Welcome {{user.firstname}}', + version: 1, + channels: { + email: { + from: '{{emailFrom}}', + to: '{{registrant.email}}', + subject: 'Alkemio registration successful!', + html: `{% extends "src/templates/_layouts/email-transactional.html" %} + {% block content %} + Hi {{registrant.name}},

+ + Welcome to the Alkemio platform!

+ + Your user profile has been successfully created. Please spend some time to further populate your profile so that other users / organizations can find you. + +

+ And please look at the Challenges on the platform to see what interests you! Then apply to join one or more communities :) +
+ Looking forward to seeing your interactions and contributions! + + Sincerely yours, + Team Alkemio + {% endblock %}`, + }, + }, +}); diff --git a/src/types/index.ts b/src/types/index.ts index 95fd928d..1cf4aaed 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1 +1 @@ -export * from './application.created.event.payload'; +export * from './user.registration.event.payload'; diff --git a/src/types/user.registration.event.payload.d.ts b/src/types/user.registration.event.payload.d.ts new file mode 100644 index 00000000..7d76ed0d --- /dev/null +++ b/src/types/user.registration.event.payload.d.ts @@ -0,0 +1,6 @@ +export type UserRegistrationEventPayload = { + userID: string; + userEmail: string; + userFirstName: string; + userLastName: string; +}; From 909e96434fd96119b96ef250c212849bdd5d73f3 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Sun, 14 Nov 2021 20:35:09 +0100 Subject: [PATCH 2/8] extended config for additional event types --- notifications.yml | 21 ++++++++ .../application.notification.builder.ts | 4 -- .../template.to.credential.mapper.spec.ts | 3 -- .../template.to.credential.mapper.ts | 48 ------------------- 4 files changed, 21 insertions(+), 55 deletions(-) delete mode 100644 src/services/template-to-credential-mapper/template.to.credential.mapper.ts diff --git a/notifications.yml b/notifications.yml index c752e188..90f5bd48 100644 --- a/notifications.yml +++ b/notifications.yml @@ -120,3 +120,24 @@ recipients: - rule: type: USER_SELF_MANAGEMENT resource_id: XX + communication_update: + - name: admin + rules: + - rule: + type: GLOBAL_COMMUNITY_ADMIN + resource_id: <> + - rule: + type: GLOBAL_ADMIN + resource_id: <> + - name: community_member + rules: + - rule: + type: CHALLENGE_MEMBER + resource_id: + - rule: + type: OPPORTUNITY_MEMBER + resource_id: + - rule: + type: ECOVERSE_MEMBER + resource_id: + diff --git a/src/services/application-notification-builder/application.notification.builder.ts b/src/services/application-notification-builder/application.notification.builder.ts index 8d8fe6a0..18553e49 100644 --- a/src/services/application-notification-builder/application.notification.builder.ts +++ b/src/services/application-notification-builder/application.notification.builder.ts @@ -8,21 +8,17 @@ import { } from '@src/common'; import { NotificationTemplateBuilder } from '@src/wrappers/notifme/notification.templates.builder'; import { - INotificationRecipientProvider, INotificationRecipientTemplateProvider, INotifiedUsersProvider, RecipientCredential, } from '@core/contracts'; import { User } from '@core/models'; -import { TemplateToCredentialMapper } from '../template-to-credential-mapper'; import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; import { ruleToCredential } from '../template-to-credential-mapper/utils/utils'; @Injectable() export class ApplicationNotificationBuilder { constructor( - @Inject(TemplateToCredentialMapper) - private readonly notificationReceivers: INotificationRecipientProvider, @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService, @Inject(ALKEMIO_CLIENT_ADAPTER) diff --git a/src/services/template-to-credential-mapper/template.to.credential.mapper.spec.ts b/src/services/template-to-credential-mapper/template.to.credential.mapper.spec.ts index a0116ae6..b1bd6a26 100644 --- a/src/services/template-to-credential-mapper/template.to.credential.mapper.spec.ts +++ b/src/services/template-to-credential-mapper/template.to.credential.mapper.spec.ts @@ -74,17 +74,14 @@ describe('TemplateToCredentialMapper', () => { { type: AuthorizationCredential.EcoverseAdmin, resourceID: 'hub', - isAdmin: true, }, { type: AuthorizationCredential.GlobalAdmin, resourceID: undefined, - isAdmin: true, }, { type: AuthorizationCredential.UserSelfManagement, resourceID: 'applicant', - isAdmin: false, }, ]; diff --git a/src/services/template-to-credential-mapper/template.to.credential.mapper.ts b/src/services/template-to-credential-mapper/template.to.credential.mapper.ts deleted file mode 100644 index 0690ee43..00000000 --- a/src/services/template-to-credential-mapper/template.to.credential.mapper.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Inject, Injectable, LoggerService } from '@nestjs/common'; -import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; -import { - INotificationRecipientProvider, - INotificationRecipientTemplateProvider, - RecipientCredential, -} from '@core/contracts'; -import { NOTIFICATION_RECIPIENTS_YML_ADAPTER } from '@src/common'; -import { ruleToCredential } from './utils/utils'; -import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; - -// todo tests -@Injectable() -export class TemplateToCredentialMapper - implements INotificationRecipientProvider -{ - constructor( - @Inject(WINSTON_MODULE_NEST_PROVIDER) - private readonly logger: LoggerService, - @Inject(NOTIFICATION_RECIPIENTS_YML_ADAPTER) - private readonly recipientTemplateProvider: INotificationRecipientTemplateProvider - ) {} - - public getApplicationCreatedRecipients( - payload: ApplicationCreatedEventPayload, - roleName: string - ): RecipientCredential[] { - const applicationCreatedTemplate = - this.recipientTemplateProvider.getTemplate().application_created; - - if (!applicationCreatedTemplate) { - return []; - } - - const ruleSetForRole = applicationCreatedTemplate.find( - templateRuleSet => templateRuleSet.name === roleName - ); - - if (!ruleSetForRole) { - this.logger.error(`Unable to identify rule set for role: ${roleName}`); - return []; - } - - const rules = ruleSetForRole.rules; - - return rules.map(x => ruleToCredential(x, payload)); - } -} From bf72c3d283d9adb7f1c399681b1b9d23c222bdd2 Mon Sep 17 00:00:00 2001 From: Neil Smyth Date: Sun, 14 Nov 2021 20:48:10 +0100 Subject: [PATCH 3/8] updated modules; added event handler for user registration --- src/app.module.ts | 2 - .../notified.users.provider.interface.ts | 3 - .../application.notification.builder.ts | 2 +- .../user.registration.notification.builder.ts | 144 ++++++++++++++++++ .../alkemio.client.adapter.ts | 21 --- src/services/index.ts | 3 +- .../notification/notification.service.spec.ts | 2 +- .../notification/notification.service.ts | 2 +- .../template-to-credential-mapper/index.ts | 1 - 9 files changed, 148 insertions(+), 32 deletions(-) rename src/{services => event-handlers}/application-notification-builder/application.notification.builder.ts (97%) create mode 100644 src/event-handlers/user-registration-notification-builder/user.registration.notification.builder.ts diff --git a/src/app.module.ts b/src/app.module.ts index a87a149e..a4cf5e79 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -9,7 +9,6 @@ import { HttpExceptionsFilter } from './core'; import { ApplicationNotificationBuilder, NotificationService, - TemplateToCredentialMapper, AlkemioClientAdapterModule, } from '@src/services'; import { AlkemioClientModule, NotifmeModule } from '@src/wrappers'; @@ -37,7 +36,6 @@ import { NotificationRecipientsAdapterModule } from './services/notification-rec }, NotificationService, ApplicationNotificationBuilder, - TemplateToCredentialMapper, ], controllers: [AppController], }) diff --git a/src/core/contracts/notified.users.provider.interface.ts b/src/core/contracts/notified.users.provider.interface.ts index f6945678..247fcfad 100644 --- a/src/core/contracts/notified.users.provider.interface.ts +++ b/src/core/contracts/notified.users.provider.interface.ts @@ -2,9 +2,6 @@ import { User } from '../models'; import { AuthorizationCredential } from '@alkemio/client-lib'; export interface INotifiedUsersProvider { - getHubAdmins(hubID: string): Promise | User[]; - getChallengeAdmins(challengeID: string): Promise | User[]; - getOpportunityAdmins(opportunityID: string): Promise | User[]; getApplicant(payload: any): Promise | User; getUsersMatchingCredentialCriteria( credentialType: AuthorizationCredential, diff --git a/src/services/application-notification-builder/application.notification.builder.ts b/src/event-handlers/application-notification-builder/application.notification.builder.ts similarity index 97% rename from src/services/application-notification-builder/application.notification.builder.ts rename to src/event-handlers/application-notification-builder/application.notification.builder.ts index 18553e49..7cac0d6b 100644 --- a/src/services/application-notification-builder/application.notification.builder.ts +++ b/src/event-handlers/application-notification-builder/application.notification.builder.ts @@ -14,7 +14,7 @@ import { } from '@core/contracts'; import { User } from '@core/models'; import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; -import { ruleToCredential } from '../template-to-credential-mapper/utils/utils'; +import { ruleToCredential } from '../../services/template-to-credential-mapper/utils/utils'; @Injectable() export class ApplicationNotificationBuilder { diff --git a/src/event-handlers/user-registration-notification-builder/user.registration.notification.builder.ts b/src/event-handlers/user-registration-notification-builder/user.registration.notification.builder.ts new file mode 100644 index 00000000..1568a903 --- /dev/null +++ b/src/event-handlers/user-registration-notification-builder/user.registration.notification.builder.ts @@ -0,0 +1,144 @@ +import { Injectable, Inject, LoggerService } from '@nestjs/common'; +import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; +import { + ALKEMIO_CLIENT_ADAPTER, + LogContext, + NOTIFICATION_RECIPIENTS_YML_ADAPTER, + TEMPLATE_PROVIDER, +} from '@src/common'; +import { NotificationTemplateBuilder } from '@src/wrappers/notifme/notification.templates.builder'; +import { + INotificationRecipientTemplateProvider, + INotifiedUsersProvider, + RecipientCredential, +} from '@core/contracts'; +import { User } from '@core/models'; +import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; +import { ruleToCredential } from '../../services/template-to-credential-mapper/utils/utils'; + +@Injectable() +export class UserRegistrationNotificationBuilder { + constructor( + @Inject(WINSTON_MODULE_NEST_PROVIDER) + private readonly logger: LoggerService, + @Inject(ALKEMIO_CLIENT_ADAPTER) + private readonly notifiedUsersService: INotifiedUsersProvider, + @Inject(TEMPLATE_PROVIDER) + private readonly notificationTemplateBuilder: NotificationTemplateBuilder, + @Inject(NOTIFICATION_RECIPIENTS_YML_ADAPTER) + private readonly recipientTemplateProvider: INotificationRecipientTemplateProvider + ) {} + + async buildNotifications(payload: any): Promise { + const adminCredentials = this.getApplicationCreatedRecipients( + payload, + 'admin' + ); + + // const applicantsCredentials = + // this.notificationReceivers.getApplicationCreatedRecipients( + // payload, + // 'applicants' + // ); + + const applicant = await this.notifiedUsersService.getApplicant(payload); + + const adminRequests = adminCredentials.map(credential => + this.notifiedUsersService.getUsersMatchingCredentialCriteria( + credential.type, + credential.resourceID + ) + ); + + const settledAdminUsers = await Promise.allSettled(adminRequests); + + const adminUsers: User[] = []; + settledAdminUsers.forEach(x => { + if (x.status === 'fulfilled') { + adminUsers.push(...x.value); + } else { + this.logger.error( + `Could not fetch admin users: ${x.reason}`, + LogContext.NOTIFICATIONS + ); + } + }); + + const adminNotifications = adminUsers.map(x => + this.buildAdminNotification(payload, applicant, x) + ); + + // todo what to do with the applicant field in the template?? + // todo: need to send a message to all users configured to receive the applicants email; cannot assume it is just one. + + const applicantNotification = this.buildUserNotification( + payload, + applicant + ); + + // will resolve on the first rejection or when all are resolved normally + return Promise.all([...adminNotifications, applicantNotification]); + } + + buildUserNotification(payload: any, applicant: User) { + const mergedUserPayload = getBaseNotification(payload, applicant); + + return this.buildNotification( + mergedUserPayload, + 'user.registration.registrant' + ); + } + + buildAdminNotification(payload: any, applicant: User, admin: User) { + const mergedAdminPayload = getBaseNotification(payload, applicant) as any; + mergedAdminPayload.admin = { + firstname: admin.firstName, + email: admin.email, + }; + + return this.buildNotification( + mergedAdminPayload, + 'user.registration.admin' + ); + } + + buildNotification = (payload: any, templateName: string) => + this.notificationTemplateBuilder.buildTemplate(templateName, payload); + + public getApplicationCreatedRecipients( + payload: ApplicationCreatedEventPayload, + roleName: string + ): RecipientCredential[] { + const applicationCreatedTemplate = + this.recipientTemplateProvider.getTemplate().user_registration; + + if (!applicationCreatedTemplate) { + return []; + } + + const ruleSetForRole = applicationCreatedTemplate.find( + templateRuleSet => templateRuleSet.name === roleName + ); + + if (!ruleSetForRole) { + this.logger.error(`Unable to identify rule set for role: ${roleName}`); + return []; + } + + const rules = ruleSetForRole.rules; + + return rules.map(x => ruleToCredential(x, payload)); + } +} + +const getBaseNotification = (payload: any, applicant: User) => ({ + emailFrom: 'info@alkem.io', + applicant: { + name: applicant.displayName, + email: applicant.email, + }, + community: { + name: payload.community.name, + type: payload.community.type, + }, +}); diff --git a/src/services/alkemio-client-adapter/alkemio.client.adapter.ts b/src/services/alkemio-client-adapter/alkemio.client.adapter.ts index 1280e291..d844f361 100644 --- a/src/services/alkemio-client-adapter/alkemio.client.adapter.ts +++ b/src/services/alkemio-client-adapter/alkemio.client.adapter.ts @@ -40,27 +40,6 @@ export class AlkemioClientAdapter return applicationCreator; } - async getOpportunityAdmins(opportunityID: string): Promise { - return await this.getUsersMatchingCredentialCriteria( - AuthorizationCredential.OpportunityAdmin, - opportunityID - ); - } - - async getHubAdmins(ecoverseID: string): Promise { - return await this.getUsersMatchingCredentialCriteria( - AuthorizationCredential.EcoverseAdmin, - ecoverseID - ); - } - - async getChallengeAdmins(challengeID: string): Promise { - return await this.getUsersMatchingCredentialCriteria( - AuthorizationCredential.ChallengeAdmin, - challengeID - ); - } - async getUsersMatchingCredentialCriteria( credential: AuthorizationCredential, resourceID?: string diff --git a/src/services/index.ts b/src/services/index.ts index 0caec3a9..7feef05a 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -1,5 +1,4 @@ export * from './alkemio-client-adapter'; -export * from './application-notification-builder/application.notification.builder'; +export * from '../event-handlers/application-notification-builder/application.notification.builder'; export * from './notification/notification.service'; export * from './notification-recipients-adapter/'; -export * from './template-to-credential-mapper/'; diff --git a/src/services/notification/notification.service.spec.ts b/src/services/notification/notification.service.spec.ts index 58c96a30..b0d9020a 100644 --- a/src/services/notification/notification.service.spec.ts +++ b/src/services/notification/notification.service.spec.ts @@ -14,7 +14,7 @@ import { INotifiedUsersProvider } from '@core/contracts'; import { ApplicationCreatedEventPayload } from '@src/types'; import { NotificationStatus } from 'notifme-sdk'; import { NotificationService } from './notification.service'; -import { ApplicationNotificationBuilder } from '../application-notification-builder/application.notification.builder'; +import { ApplicationNotificationBuilder } from '../../event-handlers/application-notification-builder/application.notification.builder'; import { TemplateToCredentialMapper, NotificationRecipientsYmlAdapter, diff --git a/src/services/notification/notification.service.ts b/src/services/notification/notification.service.ts index c52a173f..3c961617 100644 --- a/src/services/notification/notification.service.ts +++ b/src/services/notification/notification.service.ts @@ -2,7 +2,7 @@ import { Injectable, Inject, LoggerService } from '@nestjs/common'; import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import NotifmeSdk, { NotificationStatus } from 'notifme-sdk'; import { LogContext, NOTIFICATIONS_PROVIDER } from '@src/common'; -import { ApplicationNotificationBuilder } from '../application-notification-builder/application.notification.builder'; +import { ApplicationNotificationBuilder } from '../../event-handlers/application-notification-builder/application.notification.builder'; import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; @Injectable() diff --git a/src/services/template-to-credential-mapper/index.ts b/src/services/template-to-credential-mapper/index.ts index a4b14b6b..e69de29b 100644 --- a/src/services/template-to-credential-mapper/index.ts +++ b/src/services/template-to-credential-mapper/index.ts @@ -1 +0,0 @@ -export * from './template.to.credential.mapper'; From 805f224767e0b1ac7bcaee2217abc6e0306bae3b Mon Sep 17 00:00:00 2001 From: Valentin Yanakiev Date: Thu, 18 Nov 2021 11:14:15 +0200 Subject: [PATCH 4/8] Refactoring WIP --- quickstart-mailslurper.yml | 1 - src/app.module.ts | 4 ++-- .../alkemio.client.adapter.module.ts | 2 +- .../alkemio-client-adapter/alkemio.client.adapter.spec.ts | 0 .../alkemio-client-adapter/alkemio.client.adapter.ts | 0 .../{ => application}/alkemio-client-adapter/index.ts | 0 .../notification-recipients-adapter/index.ts | 0 .../notification.recipients.adapter.module.ts | 0 .../notification.recipients.yml.adapter.spec.ts | 0 .../notification.recipients.yml.adapter.ts | 0 .../template-to-credential-mapper/index.ts | 0 .../template.to.credential.mapper.spec.ts | 2 +- .../template-to-credential-mapper/utils/utils.spec.ts | 0 .../template-to-credential-mapper/utils/utils.ts | 0 .../application.notification.builder.ts | 4 ++-- .../notification/notification.service.spec.ts | 6 +++--- .../{ => domain}/notification/notification.service.ts | 2 +- .../user.registration.notification.builder.ts | 4 ++-- .../external}/alkemio-client/alkemio.client.factory.ts | 0 .../external}/alkemio-client/alkemio.client.module.ts | 0 .../external}/alkemio-client/index.ts | 0 src/{wrappers => services/external}/index.ts | 0 src/{wrappers => services/external}/notifme/index.ts | 0 .../external}/notifme/notification.templates.builder.ts | 0 .../external}/notifme/notifme.module.ts | 0 .../external}/notifme/notifme.sdk.factory.ts | 0 src/services/index.ts | 8 ++++---- src/types/index.ts | 1 + 28 files changed, 17 insertions(+), 17 deletions(-) rename src/services/{ => application}/alkemio-client-adapter/alkemio.client.adapter.module.ts (86%) rename src/services/{ => application}/alkemio-client-adapter/alkemio.client.adapter.spec.ts (100%) rename src/services/{ => application}/alkemio-client-adapter/alkemio.client.adapter.ts (100%) rename src/services/{ => application}/alkemio-client-adapter/index.ts (100%) rename src/services/{ => application}/notification-recipients-adapter/index.ts (100%) rename src/services/{ => application}/notification-recipients-adapter/notification.recipients.adapter.module.ts (100%) rename src/services/{ => application}/notification-recipients-adapter/notification.recipients.yml.adapter.spec.ts (100%) rename src/services/{ => application}/notification-recipients-adapter/notification.recipients.yml.adapter.ts (100%) rename src/services/{ => application}/template-to-credential-mapper/index.ts (100%) rename src/services/{ => application}/template-to-credential-mapper/template.to.credential.mapper.spec.ts (98%) rename src/services/{ => application}/template-to-credential-mapper/utils/utils.spec.ts (100%) rename src/services/{ => application}/template-to-credential-mapper/utils/utils.ts (100%) rename src/{event-handlers => services/domain}/application-notification-builder/application.notification.builder.ts (95%) rename src/services/{ => domain}/notification/notification.service.spec.ts (91%) rename src/services/{ => domain}/notification/notification.service.ts (91%) rename src/{event-handlers => services/domain}/user-registration-notification-builder/user.registration.notification.builder.ts (95%) rename src/{wrappers => services/external}/alkemio-client/alkemio.client.factory.ts (100%) rename src/{wrappers => services/external}/alkemio-client/alkemio.client.module.ts (100%) rename src/{wrappers => services/external}/alkemio-client/index.ts (100%) rename src/{wrappers => services/external}/index.ts (100%) rename src/{wrappers => services/external}/notifme/index.ts (100%) rename src/{wrappers => services/external}/notifme/notification.templates.builder.ts (100%) rename src/{wrappers => services/external}/notifme/notifme.module.ts (100%) rename src/{wrappers => services/external}/notifme/notifme.sdk.factory.ts (100%) diff --git a/quickstart-mailslurper.yml b/quickstart-mailslurper.yml index 8d4dd0c0..56cc0126 100644 --- a/quickstart-mailslurper.yml +++ b/quickstart-mailslurper.yml @@ -6,7 +6,6 @@ networks: services: mailslurper: container_name: alkemio_notifications_mailslurper - ## todo: fixed version image: oryd/mailslurper:latest-smtps ports: - 4436:4436 diff --git a/src/app.module.ts b/src/app.module.ts index a4cf5e79..c5717353 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -11,8 +11,8 @@ import { NotificationService, AlkemioClientAdapterModule, } from '@src/services'; -import { AlkemioClientModule, NotifmeModule } from '@src/wrappers'; -import { NotificationRecipientsAdapterModule } from './services/notification-recipients-adapter/notification.recipients.adapter.module'; +import { AlkemioClientModule, NotifmeModule } from '@src/services/external'; +import { NotificationRecipientsAdapterModule } from './services/application/notification-recipients-adapter/notification.recipients.adapter.module'; @Module({ imports: [ diff --git a/src/services/alkemio-client-adapter/alkemio.client.adapter.module.ts b/src/services/application/alkemio-client-adapter/alkemio.client.adapter.module.ts similarity index 86% rename from src/services/alkemio-client-adapter/alkemio.client.adapter.module.ts rename to src/services/application/alkemio-client-adapter/alkemio.client.adapter.module.ts index 555daea4..bde6720e 100644 --- a/src/services/alkemio-client-adapter/alkemio.client.adapter.module.ts +++ b/src/services/application/alkemio-client-adapter/alkemio.client.adapter.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { ALKEMIO_CLIENT_ADAPTER } from '@src/common'; -import { AlkemioClientModule } from '../../wrappers/alkemio-client/alkemio.client.module'; +import { AlkemioClientModule } from '../../external/alkemio-client/alkemio.client.module'; import { AlkemioClientAdapter } from './alkemio.client.adapter'; @Module({ diff --git a/src/services/alkemio-client-adapter/alkemio.client.adapter.spec.ts b/src/services/application/alkemio-client-adapter/alkemio.client.adapter.spec.ts similarity index 100% rename from src/services/alkemio-client-adapter/alkemio.client.adapter.spec.ts rename to src/services/application/alkemio-client-adapter/alkemio.client.adapter.spec.ts diff --git a/src/services/alkemio-client-adapter/alkemio.client.adapter.ts b/src/services/application/alkemio-client-adapter/alkemio.client.adapter.ts similarity index 100% rename from src/services/alkemio-client-adapter/alkemio.client.adapter.ts rename to src/services/application/alkemio-client-adapter/alkemio.client.adapter.ts diff --git a/src/services/alkemio-client-adapter/index.ts b/src/services/application/alkemio-client-adapter/index.ts similarity index 100% rename from src/services/alkemio-client-adapter/index.ts rename to src/services/application/alkemio-client-adapter/index.ts diff --git a/src/services/notification-recipients-adapter/index.ts b/src/services/application/notification-recipients-adapter/index.ts similarity index 100% rename from src/services/notification-recipients-adapter/index.ts rename to src/services/application/notification-recipients-adapter/index.ts diff --git a/src/services/notification-recipients-adapter/notification.recipients.adapter.module.ts b/src/services/application/notification-recipients-adapter/notification.recipients.adapter.module.ts similarity index 100% rename from src/services/notification-recipients-adapter/notification.recipients.adapter.module.ts rename to src/services/application/notification-recipients-adapter/notification.recipients.adapter.module.ts diff --git a/src/services/notification-recipients-adapter/notification.recipients.yml.adapter.spec.ts b/src/services/application/notification-recipients-adapter/notification.recipients.yml.adapter.spec.ts similarity index 100% rename from src/services/notification-recipients-adapter/notification.recipients.yml.adapter.spec.ts rename to src/services/application/notification-recipients-adapter/notification.recipients.yml.adapter.spec.ts diff --git a/src/services/notification-recipients-adapter/notification.recipients.yml.adapter.ts b/src/services/application/notification-recipients-adapter/notification.recipients.yml.adapter.ts similarity index 100% rename from src/services/notification-recipients-adapter/notification.recipients.yml.adapter.ts rename to src/services/application/notification-recipients-adapter/notification.recipients.yml.adapter.ts diff --git a/src/services/template-to-credential-mapper/index.ts b/src/services/application/template-to-credential-mapper/index.ts similarity index 100% rename from src/services/template-to-credential-mapper/index.ts rename to src/services/application/template-to-credential-mapper/index.ts diff --git a/src/services/template-to-credential-mapper/template.to.credential.mapper.spec.ts b/src/services/application/template-to-credential-mapper/template.to.credential.mapper.spec.ts similarity index 98% rename from src/services/template-to-credential-mapper/template.to.credential.mapper.spec.ts rename to src/services/application/template-to-credential-mapper/template.to.credential.mapper.spec.ts index b1bd6a26..c478ae61 100644 --- a/src/services/template-to-credential-mapper/template.to.credential.mapper.spec.ts +++ b/src/services/application/template-to-credential-mapper/template.to.credential.mapper.spec.ts @@ -12,7 +12,7 @@ import { ApplicationCreatedEventPayload } from '@src/types'; import configuration from '@config/configuration'; import { MockNotificationRecipientsYmlProvider } from '@test/mocks'; import { NOTIFICATION_RECIPIENTS_YML_ADAPTER } from '@src/common'; -import { TemplateToCredentialMapper } from './'; +import { TemplateToCredentialMapper } from '.'; import { MockNotificationRecipientsAdapterModule } from '@test/mocks/notification.recipiens.adapter.module.mock'; describe('TemplateToCredentialMapper', () => { diff --git a/src/services/template-to-credential-mapper/utils/utils.spec.ts b/src/services/application/template-to-credential-mapper/utils/utils.spec.ts similarity index 100% rename from src/services/template-to-credential-mapper/utils/utils.spec.ts rename to src/services/application/template-to-credential-mapper/utils/utils.spec.ts diff --git a/src/services/template-to-credential-mapper/utils/utils.ts b/src/services/application/template-to-credential-mapper/utils/utils.ts similarity index 100% rename from src/services/template-to-credential-mapper/utils/utils.ts rename to src/services/application/template-to-credential-mapper/utils/utils.ts diff --git a/src/event-handlers/application-notification-builder/application.notification.builder.ts b/src/services/domain/application-notification-builder/application.notification.builder.ts similarity index 95% rename from src/event-handlers/application-notification-builder/application.notification.builder.ts rename to src/services/domain/application-notification-builder/application.notification.builder.ts index 7cac0d6b..5a54ccb6 100644 --- a/src/event-handlers/application-notification-builder/application.notification.builder.ts +++ b/src/services/domain/application-notification-builder/application.notification.builder.ts @@ -6,7 +6,7 @@ import { NOTIFICATION_RECIPIENTS_YML_ADAPTER, TEMPLATE_PROVIDER, } from '@src/common'; -import { NotificationTemplateBuilder } from '@src/wrappers/notifme/notification.templates.builder'; +import { NotificationTemplateBuilder } from '@src/services/external/notifme/notification.templates.builder'; import { INotificationRecipientTemplateProvider, INotifiedUsersProvider, @@ -14,7 +14,7 @@ import { } from '@core/contracts'; import { User } from '@core/models'; import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; -import { ruleToCredential } from '../../services/template-to-credential-mapper/utils/utils'; +import { ruleToCredential } from '../../application/template-to-credential-mapper/utils/utils'; @Injectable() export class ApplicationNotificationBuilder { diff --git a/src/services/notification/notification.service.spec.ts b/src/services/domain/notification/notification.service.spec.ts similarity index 91% rename from src/services/notification/notification.service.spec.ts rename to src/services/domain/notification/notification.service.spec.ts index b0d9020a..a17528d2 100644 --- a/src/services/notification/notification.service.spec.ts +++ b/src/services/domain/notification/notification.service.spec.ts @@ -3,7 +3,7 @@ import { WinstonConfigService } from '@src/config'; import { WinstonModule } from 'nest-winston'; import { ConfigModule, ConfigService } from '@nestjs/config'; import configuration from '@src/config/configuration'; -import { NotifmeModule } from '@src/wrappers/notifme/notifme.module'; +import { NotifmeModule } from '@src/services/external/notifme/notifme.module'; import { ALKEMIO_CLIENT_ADAPTER } from '@src/common'; import * as challengeAdminsData from '@test/data/challenge.admins.json'; import * as opportunityAdminsData from '@test/data/opportunity.admins.json'; @@ -14,12 +14,12 @@ import { INotifiedUsersProvider } from '@core/contracts'; import { ApplicationCreatedEventPayload } from '@src/types'; import { NotificationStatus } from 'notifme-sdk'; import { NotificationService } from './notification.service'; -import { ApplicationNotificationBuilder } from '../../event-handlers/application-notification-builder/application.notification.builder'; +import { ApplicationNotificationBuilder } from '../application-notification-builder/application.notification.builder'; import { TemplateToCredentialMapper, NotificationRecipientsYmlAdapter, } from '@src/services'; -import { NotificationRecipientsAdapterModule } from '../notification-recipients-adapter/notification.recipients.adapter.module'; +import { NotificationRecipientsAdapterModule } from '../../application/notification-recipients-adapter/notification.recipients.adapter.module'; const testData = { ...challengeAdminsData, diff --git a/src/services/notification/notification.service.ts b/src/services/domain/notification/notification.service.ts similarity index 91% rename from src/services/notification/notification.service.ts rename to src/services/domain/notification/notification.service.ts index 3c961617..c52a173f 100644 --- a/src/services/notification/notification.service.ts +++ b/src/services/domain/notification/notification.service.ts @@ -2,7 +2,7 @@ import { Injectable, Inject, LoggerService } from '@nestjs/common'; import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import NotifmeSdk, { NotificationStatus } from 'notifme-sdk'; import { LogContext, NOTIFICATIONS_PROVIDER } from '@src/common'; -import { ApplicationNotificationBuilder } from '../../event-handlers/application-notification-builder/application.notification.builder'; +import { ApplicationNotificationBuilder } from '../application-notification-builder/application.notification.builder'; import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; @Injectable() diff --git a/src/event-handlers/user-registration-notification-builder/user.registration.notification.builder.ts b/src/services/domain/user-registration-notification-builder/user.registration.notification.builder.ts similarity index 95% rename from src/event-handlers/user-registration-notification-builder/user.registration.notification.builder.ts rename to src/services/domain/user-registration-notification-builder/user.registration.notification.builder.ts index 1568a903..e842986a 100644 --- a/src/event-handlers/user-registration-notification-builder/user.registration.notification.builder.ts +++ b/src/services/domain/user-registration-notification-builder/user.registration.notification.builder.ts @@ -6,7 +6,7 @@ import { NOTIFICATION_RECIPIENTS_YML_ADAPTER, TEMPLATE_PROVIDER, } from '@src/common'; -import { NotificationTemplateBuilder } from '@src/wrappers/notifme/notification.templates.builder'; +import { NotificationTemplateBuilder } from '@src/services/external/notifme/notification.templates.builder'; import { INotificationRecipientTemplateProvider, INotifiedUsersProvider, @@ -14,7 +14,7 @@ import { } from '@core/contracts'; import { User } from '@core/models'; import { ApplicationCreatedEventPayload } from '@src/types/application.created.event.payload'; -import { ruleToCredential } from '../../services/template-to-credential-mapper/utils/utils'; +import { ruleToCredential } from '../../application/template-to-credential-mapper/utils/utils'; @Injectable() export class UserRegistrationNotificationBuilder { diff --git a/src/wrappers/alkemio-client/alkemio.client.factory.ts b/src/services/external/alkemio-client/alkemio.client.factory.ts similarity index 100% rename from src/wrappers/alkemio-client/alkemio.client.factory.ts rename to src/services/external/alkemio-client/alkemio.client.factory.ts diff --git a/src/wrappers/alkemio-client/alkemio.client.module.ts b/src/services/external/alkemio-client/alkemio.client.module.ts similarity index 100% rename from src/wrappers/alkemio-client/alkemio.client.module.ts rename to src/services/external/alkemio-client/alkemio.client.module.ts diff --git a/src/wrappers/alkemio-client/index.ts b/src/services/external/alkemio-client/index.ts similarity index 100% rename from src/wrappers/alkemio-client/index.ts rename to src/services/external/alkemio-client/index.ts diff --git a/src/wrappers/index.ts b/src/services/external/index.ts similarity index 100% rename from src/wrappers/index.ts rename to src/services/external/index.ts diff --git a/src/wrappers/notifme/index.ts b/src/services/external/notifme/index.ts similarity index 100% rename from src/wrappers/notifme/index.ts rename to src/services/external/notifme/index.ts diff --git a/src/wrappers/notifme/notification.templates.builder.ts b/src/services/external/notifme/notification.templates.builder.ts similarity index 100% rename from src/wrappers/notifme/notification.templates.builder.ts rename to src/services/external/notifme/notification.templates.builder.ts diff --git a/src/wrappers/notifme/notifme.module.ts b/src/services/external/notifme/notifme.module.ts similarity index 100% rename from src/wrappers/notifme/notifme.module.ts rename to src/services/external/notifme/notifme.module.ts diff --git a/src/wrappers/notifme/notifme.sdk.factory.ts b/src/services/external/notifme/notifme.sdk.factory.ts similarity index 100% rename from src/wrappers/notifme/notifme.sdk.factory.ts rename to src/services/external/notifme/notifme.sdk.factory.ts diff --git a/src/services/index.ts b/src/services/index.ts index 7feef05a..4cfd9d8e 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -1,4 +1,4 @@ -export * from './alkemio-client-adapter'; -export * from '../event-handlers/application-notification-builder/application.notification.builder'; -export * from './notification/notification.service'; -export * from './notification-recipients-adapter/'; +export * from './application/alkemio-client-adapter'; +export * from './domain/application-notification-builder/application.notification.builder'; +export * from './domain/notification/notification.service'; +export * from './application/notification-recipients-adapter'; diff --git a/src/types/index.ts b/src/types/index.ts index 1cf4aaed..4de76fc1 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1 +1,2 @@ export * from './user.registration.event.payload'; +export * from './application.created.event.payload'; From 0b2dad65bd50aeed3c11267876f737e0c807b74d Mon Sep 17 00:00:00 2001 From: Valentin Yanakiev Date: Thu, 18 Nov 2021 12:30:47 +0200 Subject: [PATCH 5/8] Fixed tests --- notifications.yml | 85 ++++++------ .../notified.users.provider.interface.ts | 2 +- .../alkemio.client.adapter.spec.ts | 33 ----- .../template.to.credential.mapper.spec.ts | 126 ------------------ .../utils/utils.spec.ts | 21 +-- .../utils/utils.ts | 1 + .../application.notification.builder.ts | 3 + .../notification/notification.service.spec.ts | 25 +--- .../user.registration.notification.builder.ts | 14 +- .../application.created.event.payload.d.ts | 1 + .../user.registration.event.payload.d.ts | 1 + 11 files changed, 74 insertions(+), 238 deletions(-) delete mode 100644 src/services/application/template-to-credential-mapper/template.to.credential.mapper.spec.ts diff --git a/notifications.yml b/notifications.yml index 90f5bd48..41170271 100644 --- a/notifications.yml +++ b/notifications.yml @@ -86,58 +86,57 @@ recipients: application_created: - name: admin rules: - - rule: - type: CHALLENGE_ADMIN - resource_id: - - rule: - type: OPPORTUNITY_ADMIN - resource_id: - - rule: - type: ECOVERSE_ADMIN - resource_id: - - rule: - type: GLOBAL_COMMUNITY_ADMIN - resource_id: <> - - rule: - type: GLOBAL_ADMIN - resource_id: <> + - rule: + type: CHALLENGE_ADMIN + resource_id: + - rule: + type: OPPORTUNITY_ADMIN + resource_id: + - rule: + type: ECOVERSE_ADMIN + resource_id: + - rule: + type: GLOBAL_COMMUNITY_ADMIN + resource_id: <> + - rule: + type: GLOBAL_ADMIN + resource_id: <> - name: applicant rules: - - rule: - type: USER_SELF_MANAGEMENT - resource_id: + - rule: + type: USER_SELF_MANAGEMENT + resource_id: user_registration: - name: admin rules: - - rule: - type: GLOBAL_COMMUNITY_ADMIN - resource_id: <> - - rule: - type: GLOBAL_ADMIN - resource_id: <> + - rule: + type: GLOBAL_COMMUNITY_ADMIN + resource_id: <> + - rule: + type: GLOBAL_ADMIN + resource_id: <> - name: registrant rules: - - rule: - type: USER_SELF_MANAGEMENT - resource_id: XX + - rule: + type: USER_SELF_MANAGEMENT + resource_id: communication_update: - name: admin rules: - - rule: - type: GLOBAL_COMMUNITY_ADMIN - resource_id: <> - - rule: - type: GLOBAL_ADMIN - resource_id: <> + - rule: + type: GLOBAL_COMMUNITY_ADMIN + resource_id: <> + - rule: + type: GLOBAL_ADMIN + resource_id: <> - name: community_member rules: - - rule: - type: CHALLENGE_MEMBER - resource_id: - - rule: - type: OPPORTUNITY_MEMBER - resource_id: - - rule: - type: ECOVERSE_MEMBER - resource_id: - + - rule: + type: CHALLENGE_MEMBER + resource_id: + - rule: + type: OPPORTUNITY_MEMBER + resource_id: + - rule: + type: ECOVERSE_MEMBER + resource_id: diff --git a/src/core/contracts/notified.users.provider.interface.ts b/src/core/contracts/notified.users.provider.interface.ts index 247fcfad..fa0325f7 100644 --- a/src/core/contracts/notified.users.provider.interface.ts +++ b/src/core/contracts/notified.users.provider.interface.ts @@ -5,6 +5,6 @@ export interface INotifiedUsersProvider { getApplicant(payload: any): Promise | User; getUsersMatchingCredentialCriteria( credentialType: AuthorizationCredential, - credentialResourceID?: string + resourceID?: string ): Promise; } diff --git a/src/services/application/alkemio-client-adapter/alkemio.client.adapter.spec.ts b/src/services/application/alkemio-client-adapter/alkemio.client.adapter.spec.ts index c662b1fa..17f2a430 100644 --- a/src/services/application/alkemio-client-adapter/alkemio.client.adapter.spec.ts +++ b/src/services/application/alkemio-client-adapter/alkemio.client.adapter.spec.ts @@ -61,39 +61,6 @@ describe('AlkemioAdapter', () => { }); describe('Alkemio Client Adapter', () => { - it('Should get hub admins', async () => { - jest - .spyOn(alkemioClient, 'usersWithAuthorizationCredential') - .mockResolvedValue(testData.hubAdmins); - - const res = await alkemioAdapter.getHubAdmins( - testData.eventPayload.data.hub.id - ); - expect(res.length === 0); - }); - - it('Should get opportunity admins', async () => { - jest - .spyOn(alkemioClient, 'usersWithAuthorizationCredential') - .mockResolvedValue(testData.opportunityAdmins); - - const res = await alkemioAdapter.getOpportunityAdmins( - testData.eventPayload.data.hub.id - ); - expect(res.length === 1); - }); - - it('Should get challenge admins', async () => { - jest - .spyOn(alkemioClient, 'usersWithAuthorizationCredential') - .mockResolvedValue(testData.challengeAdmins); - - const res = await alkemioAdapter.getChallengeAdmins( - testData.eventPayload.data.hub.id - ); - expect(res.length === 1); - }); - it('Should throw an error', async () => { jest.spyOn(alkemioClient, 'user').mockResolvedValue(undefined); diff --git a/src/services/application/template-to-credential-mapper/template.to.credential.mapper.spec.ts b/src/services/application/template-to-credential-mapper/template.to.credential.mapper.spec.ts deleted file mode 100644 index c478ae61..00000000 --- a/src/services/application/template-to-credential-mapper/template.to.credential.mapper.spec.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { AuthorizationCredential } from '@alkemio/client-lib'; -import { Test } from '@nestjs/testing'; -import { ConfigModule } from '@nestjs/config'; -import { WinstonModule } from 'nest-winston'; -import { - INotificationRecipientProvider, - INotificationRecipientTemplateProvider, - RecipientCredential, -} from '@core/contracts'; -import { WinstonConfigService } from '@src/config'; -import { ApplicationCreatedEventPayload } from '@src/types'; -import configuration from '@config/configuration'; -import { MockNotificationRecipientsYmlProvider } from '@test/mocks'; -import { NOTIFICATION_RECIPIENTS_YML_ADAPTER } from '@src/common'; -import { TemplateToCredentialMapper } from '.'; -import { MockNotificationRecipientsAdapterModule } from '@test/mocks/notification.recipiens.adapter.module.mock'; - -describe('TemplateToCredentialMapper', () => { - let notificationReceivers: INotificationRecipientProvider; - let recipientTemplateProvider: INotificationRecipientTemplateProvider; - - beforeAll(async () => { - const moduleRef = await Test.createTestingModule({ - imports: [ - ConfigModule.forRoot({ - envFilePath: ['.env'], - isGlobal: true, - load: [configuration], - }), - WinstonModule.forRootAsync({ - useClass: WinstonConfigService, - }), - MockNotificationRecipientsAdapterModule, - ], - providers: [ - MockNotificationRecipientsYmlProvider, - TemplateToCredentialMapper, - ], - }).compile(); - - notificationReceivers = moduleRef.get( - TemplateToCredentialMapper - ); - recipientTemplateProvider = - moduleRef.get( - NOTIFICATION_RECIPIENTS_YML_ADAPTER - ); - }); - - describe('getApplicationCreatedRecipients', () => { - it('returns empty array on missing template', () => { - // setup - const emptyTemplateMock = {}; - jest - .spyOn(recipientTemplateProvider, 'getTemplate') - .mockReturnValue(emptyTemplateMock); - // act - const result = notificationReceivers.getApplicationCreatedRecipients( - {} as any - ); - // assert - expect(Array.isArray(result)).toBeTruthy(); - expect(result).toHaveLength(0); - }); - it('returns correct response', () => { - const payload = { - applicantID: 'applicant', - hub: { - id: 'hub', - }, - } as ApplicationCreatedEventPayload; - - const expectedResponse: RecipientCredential[] = [ - { - type: AuthorizationCredential.EcoverseAdmin, - resourceID: 'hub', - }, - { - type: AuthorizationCredential.GlobalAdmin, - resourceID: undefined, - }, - { - type: AuthorizationCredential.UserSelfManagement, - resourceID: 'applicant', - }, - ]; - - jest - .spyOn(recipientTemplateProvider, 'getTemplate') - .mockReturnValue(templateMock); - - // act - const res = - notificationReceivers.getApplicationCreatedRecipients(payload); - // assert - expect(res).toEqual(expect.arrayContaining(expectedResponse)); - }); - }); -}); - -const templateMock = { - application_created: { - admin: [ - { - rule: { - type: AuthorizationCredential.EcoverseAdmin, - resource_id: '<>', - }, - }, - { - rule: { - type: AuthorizationCredential.GlobalAdmin, - resource_id: '<>', - }, - }, - ], - applicant: [ - { - rule: { - type: AuthorizationCredential.UserSelfManagement, - resource_id: '<>', - }, - }, - ], - }, -}; diff --git a/src/services/application/template-to-credential-mapper/utils/utils.spec.ts b/src/services/application/template-to-credential-mapper/utils/utils.spec.ts index b997fbfa..3eee174e 100644 --- a/src/services/application/template-to-credential-mapper/utils/utils.spec.ts +++ b/src/services/application/template-to-credential-mapper/utils/utils.spec.ts @@ -3,7 +3,7 @@ import { ApplicationCreatedEventPayload } from '@src/types/application.created.e import { getResourceId, getResourceIdByRole, ruleToCredential } from './utils'; describe('ruleToCredential', () => { - it('returns undefined on unsupported role or missing data', () => { + it('returns undefined resource_id on unsupported role or missing data', () => { const payload = { hub: { id: 'hub', @@ -19,7 +19,10 @@ describe('ruleToCredential', () => { }, payload ) - ).toBeUndefined(); + ).toEqual({ + type: AuthorizationCredential.GlobalAdminCommunity, + resource_id: undefined, + }); expect( ruleToCredential( { @@ -30,7 +33,10 @@ describe('ruleToCredential', () => { }, payload ) - ).toBeUndefined(); + ).toEqual({ + type: AuthorizationCredential.ChallengeAdmin, + resource_id: undefined, + }); }); it('returns correct response', () => { const payload = { @@ -49,9 +55,8 @@ describe('ruleToCredential', () => { payload ) ).toEqual({ - role: AuthorizationCredential.EcoverseAdmin, - resourceID: 'hub', - isAdmin: false, + type: AuthorizationCredential.EcoverseAdmin, + resource_id: '<>', }); }); it('returns correct response on global admin', () => { @@ -71,8 +76,8 @@ describe('ruleToCredential', () => { payload ) ).toEqual({ - role: AuthorizationCredential.GlobalAdmin, - isAdmin: false, + type: AuthorizationCredential.GlobalAdmin, + resource_id: '<>', }); }); }); diff --git a/src/services/application/template-to-credential-mapper/utils/utils.ts b/src/services/application/template-to-credential-mapper/utils/utils.ts index 6ff747be..87a4d9f9 100644 --- a/src/services/application/template-to-credential-mapper/utils/utils.ts +++ b/src/services/application/template-to-credential-mapper/utils/utils.ts @@ -16,6 +16,7 @@ export const ruleToCredential = ( const { rule } = templateRule; const resourceID = getResourceId(rule.type, rule.resource_id || '', payload); + //valentin - what if it's undefined and not null? if (resourceID === null) { rule.resource_id = undefined; } diff --git a/src/services/domain/application-notification-builder/application.notification.builder.ts b/src/services/domain/application-notification-builder/application.notification.builder.ts index 5a54ccb6..f6bc345e 100644 --- a/src/services/domain/application-notification-builder/application.notification.builder.ts +++ b/src/services/domain/application-notification-builder/application.notification.builder.ts @@ -70,6 +70,7 @@ export class ApplicationNotificationBuilder { // todo what to do with the applicant field in the template?? // todo: need to send a message to all users configured to receive the applicants email; cannot assume it is just one. + // valentin - I am not sure that's the case, in my opinion it's a 3rd flow. Applicant is applicant and is always one person. const applicantNotification = this.buildUserNotification( payload, @@ -102,6 +103,8 @@ export class ApplicationNotificationBuilder { buildNotification = (payload: any, templateName: string) => this.notificationTemplateBuilder.buildTemplate(templateName, payload); + // toDo: + // 1. Move getRecipients back to the credential mapper. Logically, it has nothing to do with this service. public getApplicationCreatedRecipients( payload: ApplicationCreatedEventPayload, roleName: string diff --git a/src/services/domain/notification/notification.service.spec.ts b/src/services/domain/notification/notification.service.spec.ts index a17528d2..9544e96b 100644 --- a/src/services/domain/notification/notification.service.spec.ts +++ b/src/services/domain/notification/notification.service.spec.ts @@ -15,10 +15,7 @@ import { ApplicationCreatedEventPayload } from '@src/types'; import { NotificationStatus } from 'notifme-sdk'; import { NotificationService } from './notification.service'; import { ApplicationNotificationBuilder } from '../application-notification-builder/application.notification.builder'; -import { - TemplateToCredentialMapper, - NotificationRecipientsYmlAdapter, -} from '@src/services'; +import { NotificationRecipientsYmlAdapter } from '@src/services'; import { NotificationRecipientsAdapterModule } from '../../application/notification-recipients-adapter/notification.recipients.adapter.module'; const testData = { @@ -48,7 +45,6 @@ describe('NotificationService', () => { NotificationRecipientsAdapterModule, ], providers: [ - TemplateToCredentialMapper, NotificationRecipientsYmlAdapter, NotificationService, ApplicationNotificationBuilder, @@ -58,10 +54,7 @@ describe('NotificationService', () => { useValue: { getApplicant: jest.fn(), getApplicationCreator: jest.fn(), - getOpportunityAdmins: jest.fn(), - getHubAdmins: jest.fn(), - getChallengeAdmins: jest.fn(), - getUsersWithCredentials: jest.fn(), + getUsersMatchingCredentialCriteria: jest.fn(), }, }, ], @@ -77,25 +70,13 @@ describe('NotificationService', () => { describe('Application Notifications', () => { it('Should send application notification', async () => { jest - .spyOn(alkemioAdapter, 'getUsersWithCredentials') + .spyOn(alkemioAdapter, 'getUsersMatchingCredentialCriteria') .mockResolvedValue(testData.hubAdmins); jest .spyOn(alkemioAdapter, 'getApplicant') .mockResolvedValue(testData.adminUser); - jest - .spyOn(alkemioAdapter, 'getHubAdmins') - .mockResolvedValue(testData.hubAdmins); - - jest - .spyOn(alkemioAdapter, 'getChallengeAdmins') - .mockResolvedValue(testData.challengeAdmins); - - jest - .spyOn(alkemioAdapter, 'getOpportunityAdmins') - .mockResolvedValue(testData.opportunityAdmins); - const res = await notificationService.sendApplicationNotifications( testData.eventPayload.data as ApplicationCreatedEventPayload ); diff --git a/src/services/domain/user-registration-notification-builder/user.registration.notification.builder.ts b/src/services/domain/user-registration-notification-builder/user.registration.notification.builder.ts index e842986a..8fe543b2 100644 --- a/src/services/domain/user-registration-notification-builder/user.registration.notification.builder.ts +++ b/src/services/domain/user-registration-notification-builder/user.registration.notification.builder.ts @@ -35,11 +35,10 @@ export class UserRegistrationNotificationBuilder { 'admin' ); - // const applicantsCredentials = - // this.notificationReceivers.getApplicationCreatedRecipients( - // payload, - // 'applicants' - // ); + // const applicantsCredentials = this.getApplicationCreatedRecipients( + // payload, + // 'applicant' + // ); const applicant = await this.notifiedUsersService.getApplicant(payload); @@ -105,6 +104,11 @@ export class UserRegistrationNotificationBuilder { buildNotification = (payload: any, templateName: string) => this.notificationTemplateBuilder.buildTemplate(templateName, payload); + // toDo: + // 1. Move getRecipients back to the credential mapper. Logically, it has nothing to do with this service. + // 2. Decide on a design how to get a template, either have the template with named sections, or create callbacks to get the specific template or... + // 3. Abstract ApplicationCreatedEventPayload. Create base payload. + // 4. Use the abstraction of the payload in the getRecipients. The format of what is needed in the credential mapper is quite fixed and the rules are strongly types - we know what we need. public getApplicationCreatedRecipients( payload: ApplicationCreatedEventPayload, roleName: string diff --git a/src/types/application.created.event.payload.d.ts b/src/types/application.created.event.payload.d.ts index ec76d853..de752fea 100644 --- a/src/types/application.created.event.payload.d.ts +++ b/src/types/application.created.event.payload.d.ts @@ -4,6 +4,7 @@ export enum CommunityType { OPPORTUNITY = 'opportunity', } +// toDo: fix this type - derive from base event payload. export type ApplicationCreatedEventPayload = { applicationCreatorID: string; applicantID: string; diff --git a/src/types/user.registration.event.payload.d.ts b/src/types/user.registration.event.payload.d.ts index 7d76ed0d..34a454f8 100644 --- a/src/types/user.registration.event.payload.d.ts +++ b/src/types/user.registration.event.payload.d.ts @@ -1,3 +1,4 @@ +// toDo: fix this type, it is completely detached from the template and its purpose is to define the template. Derive from base event payload. export type UserRegistrationEventPayload = { userID: string; userEmail: string; From de4f886e7e6b63b7915bbf0e8fa95e79c8344e13 Mon Sep 17 00:00:00 2001 From: Valentin Yanakiev Date: Thu, 18 Nov 2021 15:16:14 +0200 Subject: [PATCH 6/8] Added CI tests debugging config for VS Code --- .../application.notification.builder.ts | 1 + .../notification/notification.service.spec.ts | 34 +++++++++++++++++++ test/data/opportunity.admins.json | 8 +++++ 3 files changed, 43 insertions(+) diff --git a/src/services/domain/application-notification-builder/application.notification.builder.ts b/src/services/domain/application-notification-builder/application.notification.builder.ts index f6bc345e..c33863c2 100644 --- a/src/services/domain/application-notification-builder/application.notification.builder.ts +++ b/src/services/domain/application-notification-builder/application.notification.builder.ts @@ -55,6 +55,7 @@ export class ApplicationNotificationBuilder { const adminUsers: User[] = []; settledAdminUsers.forEach(x => { if (x.status === 'fulfilled') { + console.log(JSON.stringify(x.value)); adminUsers.push(...x.value); } else { this.logger.error( diff --git a/src/services/domain/notification/notification.service.spec.ts b/src/services/domain/notification/notification.service.spec.ts index 9544e96b..d30f068f 100644 --- a/src/services/domain/notification/notification.service.spec.ts +++ b/src/services/domain/notification/notification.service.spec.ts @@ -88,6 +88,40 @@ describe('NotificationService', () => { } }); + it('Should send 3 application notifications', async () => { + const admins = [ + ...testData.hubAdmins, + ...testData.challengeAdmins, + ...testData.opportunityAdmins, + ]; + + console.log(admins.length); + + jest + .spyOn(alkemioAdapter, 'getUsersMatchingCredentialCriteria') + .mockResolvedValue(admins); + + jest + .spyOn(alkemioAdapter, 'getApplicant') + .mockResolvedValue(testData.adminUser); + + const res = await notificationService.sendApplicationNotifications( + testData.eventPayload.data as ApplicationCreatedEventPayload + ); + + let emailCount = 0; + for (const notificationStatus of res) { + expect( + (notificationStatus as PromiseFulfilledResult) + .value.status + ).toBe('success'); + + console.log(JSON.stringify(notificationStatus)); + emailCount++; + } + console.log(emailCount); + }); + it('Should fail to send notification', async () => { jest .spyOn(alkemioAdapter, 'getApplicant') diff --git a/test/data/opportunity.admins.json b/test/data/opportunity.admins.json index b979851c..a6802076 100644 --- a/test/data/opportunity.admins.json +++ b/test/data/opportunity.admins.json @@ -7,6 +7,14 @@ "firstName": "Kathern", "lastName": "Keira", "email": "Kathern@Keira.com" + }, + { + "id": "d2c354a9-afad-4e4d-9969-cba1a925b302", + "nameID": "madalynjerold", + "displayName": "Madalyn Jerold", + "firstName": "Madalyn", + "lastName": "Jerold", + "email": "Madalyn@Jerold.com" } ] } From 0a0c18313ea882529eb989bfd09d64503b4372f9 Mon Sep 17 00:00:00 2001 From: Valentin Yanakiev Date: Thu, 18 Nov 2021 15:46:56 +0200 Subject: [PATCH 7/8] Workaround + fix for duplicate admins. Credentials yet to be fixed. --- .../application.notification.builder.ts | 7 +++++-- .../domain/notification/notification.service.spec.ts | 9 ++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/services/domain/application-notification-builder/application.notification.builder.ts b/src/services/domain/application-notification-builder/application.notification.builder.ts index c33863c2..75d1d40f 100644 --- a/src/services/domain/application-notification-builder/application.notification.builder.ts +++ b/src/services/domain/application-notification-builder/application.notification.builder.ts @@ -55,8 +55,11 @@ export class ApplicationNotificationBuilder { const adminUsers: User[] = []; settledAdminUsers.forEach(x => { if (x.status === 'fulfilled') { - console.log(JSON.stringify(x.value)); - adminUsers.push(...x.value); + const users = [...x.value]; + for (const user of users) { + if (adminUsers.find(x => x.email === user.email)) continue; + adminUsers.push(user); + } } else { this.logger.error( `Could not fetch admin users: ${x.reason}`, diff --git a/src/services/domain/notification/notification.service.spec.ts b/src/services/domain/notification/notification.service.spec.ts index d30f068f..3be0ffb2 100644 --- a/src/services/domain/notification/notification.service.spec.ts +++ b/src/services/domain/notification/notification.service.spec.ts @@ -95,8 +95,6 @@ describe('NotificationService', () => { ...testData.opportunityAdmins, ]; - console.log(admins.length); - jest .spyOn(alkemioAdapter, 'getUsersMatchingCredentialCriteria') .mockResolvedValue(admins); @@ -109,17 +107,14 @@ describe('NotificationService', () => { testData.eventPayload.data as ApplicationCreatedEventPayload ); - let emailCount = 0; for (const notificationStatus of res) { expect( (notificationStatus as PromiseFulfilledResult) .value.status ).toBe('success'); - - console.log(JSON.stringify(notificationStatus)); - emailCount++; } - console.log(emailCount); + + expect(res.length).toBe(admins.length); //1 admin is duplicated + 1 applicant }); it('Should fail to send notification', async () => { From 28db104e77449ced4db990dd3ea5e13367dee6eb Mon Sep 17 00:00:00 2001 From: Valentin Yanakiev Date: Thu, 18 Nov 2021 18:05:59 +0200 Subject: [PATCH 8/8] Bumped version --- package-lock.json | 4 ++-- package.json | 2 +- src/services/domain/notification/notification.service.spec.ts | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b421cf96..7f6ffb24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "alkemio-notifications", - "version": "0.3.2", + "version": "0.3.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "alkemio-notifications", - "version": "0.3.2", + "version": "0.3.3", "license": "EUPL-1.2", "dependencies": { "@alkemio/client-lib": "^0.10.1", diff --git a/package.json b/package.json index c6e33cff..36dda990 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "alkemio-notifications", - "version": "0.3.2", + "version": "0.3.3", "description": "Alkemio notifications service", "author": "Cherrytwist Foundation", "private": false, diff --git a/src/services/domain/notification/notification.service.spec.ts b/src/services/domain/notification/notification.service.spec.ts index 3be0ffb2..5cea97f6 100644 --- a/src/services/domain/notification/notification.service.spec.ts +++ b/src/services/domain/notification/notification.service.spec.ts @@ -69,6 +69,7 @@ describe('NotificationService', () => { describe('Application Notifications', () => { it('Should send application notification', async () => { + //toDo investigate mocking this function result based on input arguments https://stackoverflow.com/questions/41697513/can-i-mock-functions-with-specific-arguments-using-jest jest .spyOn(alkemioAdapter, 'getUsersMatchingCredentialCriteria') .mockResolvedValue(testData.hubAdmins);