diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c204eb321..7819a0703 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -545,7 +545,7 @@ jobs: report_name: Coverage Backend type: lcov result_path: ./backend/coverage/lcov.info - min_coverage: 78 + min_coverage: 80 token: ${{ github.token }} ########################################################################## diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 77e86ad46..8e65d85f2 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -1,509 +1,212 @@ -import decimal from 'decimal.js-light' +import { EventProtocol as DbEvent } from '@entity/EventProtocol' +import Decimal from 'decimal.js-light' import { EventProtocolType } from './EventProtocolType' -export class EventBasic { - type: string - createdAt: Date -} -export class EventBasicUserId extends EventBasic { - userId: number +export const Event = ( + type: EventProtocolType, + userId: number, + xUserId: number | null = null, + xCommunityId: number | null = null, + transactionId: number | null = null, + contributionId: number | null = null, + amount: Decimal | null = null, + messageId: number | null = null, +): DbEvent => { + const event = new DbEvent() + event.type = type + event.userId = userId + event.xUserId = xUserId + event.xCommunityId = xCommunityId + event.transactionId = transactionId + event.contributionId = contributionId + event.amount = amount + event.messageId = messageId + return event } -export class EventBasicTx extends EventBasicUserId { - transactionId: number - amount: decimal -} - -export class EventBasicTxX extends EventBasicTx { - xUserId: number - xCommunityId: number -} - -export class EventBasicCt extends EventBasicUserId { - contributionId: number - amount: decimal -} - -export class EventBasicCtX extends EventBasicCt { - xUserId: number - xCommunityId: number -} - -export class EventBasicRedeem extends EventBasicUserId { - transactionId?: number - contributionId?: number -} - -export class EventBasicCtMsg extends EventBasicCt { - messageId: number -} - -export class EventVisitGradido extends EventBasic {} -export class EventRegister extends EventBasicUserId {} -export class EventRedeemRegister extends EventBasicRedeem {} -export class EventVerifyRedeem extends EventBasicRedeem {} -export class EventInactiveAccount extends EventBasicUserId {} -export class EventSendConfirmationEmail extends EventBasicUserId {} -export class EventSendAccountMultiRegistrationEmail extends EventBasicUserId {} -export class EventSendForgotPasswordEmail extends EventBasicUserId {} -export class EventSendTransactionSendEmail extends EventBasicTxX {} -export class EventSendTransactionReceiveEmail extends EventBasicTxX {} -export class EventSendTransactionLinkRedeemEmail extends EventBasicTxX {} -export class EventSendAddedContributionEmail extends EventBasicCt {} -export class EventSendContributionConfirmEmail extends EventBasicCt {} -export class EventConfirmationEmail extends EventBasicUserId {} -export class EventRegisterEmailKlicktipp extends EventBasicUserId {} -export class EventLogin extends EventBasicUserId {} -export class EventLogout extends EventBasicUserId {} -export class EventRedeemLogin extends EventBasicRedeem {} -export class EventActivateAccount extends EventBasicUserId {} -export class EventPasswordChange extends EventBasicUserId {} -export class EventTransactionSend extends EventBasicTxX {} -export class EventTransactionSendRedeem extends EventBasicTxX {} -export class EventTransactionRepeateRedeem extends EventBasicTxX {} -export class EventTransactionCreation extends EventBasicTx {} -export class EventTransactionReceive extends EventBasicTxX {} -export class EventTransactionReceiveRedeem extends EventBasicTxX {} -export class EventContributionCreate extends EventBasicCt {} -export class EventAdminContributionCreate extends EventBasicCt {} -export class EventAdminContributionDelete extends EventBasicCt {} -export class EventAdminContributionDeny extends EventBasicCt {} -export class EventAdminContributionUpdate extends EventBasicCt {} -export class EventUserCreateContributionMessage extends EventBasicCtMsg {} -export class EventAdminCreateContributionMessage extends EventBasicCtMsg {} -export class EventContributionDelete extends EventBasicCt {} -export class EventContributionUpdate extends EventBasicCt {} -export class EventContributionConfirm extends EventBasicCtX {} -export class EventContributionDeny extends EventBasicCtX {} -export class EventContributionLinkDefine extends EventBasicCt {} -export class EventContributionLinkActivateRedeem extends EventBasicCt {} -export class EventDeleteUser extends EventBasicUserId {} -export class EventUndeleteUser extends EventBasicUserId {} -export class EventChangeUserRole extends EventBasicUserId {} -export class EventAdminUpdateContribution extends EventBasicCt {} -export class EventAdminDeleteContribution extends EventBasicCt {} -export class EventCreateContributionLink extends EventBasicCt {} -export class EventDeleteContributionLink extends EventBasicCt {} -export class EventUpdateContributionLink extends EventBasicCt {} - -export class Event { - public setEventBasic(): Event { - this.type = EventProtocolType.BASIC - this.createdAt = new Date() - - return this - } - - public setEventVisitGradido(): Event { - this.setEventBasic() - this.type = EventProtocolType.VISIT_GRADIDO - - return this - } - - public setEventRegister(ev: EventRegister): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.REGISTER - - return this - } - - public setEventRedeemRegister(ev: EventRedeemRegister): Event { - this.setByBasicRedeem(ev.userId, ev.transactionId, ev.contributionId) - this.type = EventProtocolType.REDEEM_REGISTER - - return this - } - - public setEventVerifyRedeem(ev: EventVerifyRedeem): Event { - this.setByBasicRedeem(ev.userId, ev.transactionId, ev.contributionId) - this.type = EventProtocolType.VERIFY_REDEEM - - return this - } - - public setEventInactiveAccount(ev: EventInactiveAccount): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.INACTIVE_ACCOUNT - - return this - } - - public setEventSendConfirmationEmail(ev: EventSendConfirmationEmail): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.SEND_CONFIRMATION_EMAIL - - return this - } - - public setEventSendAccountMultiRegistrationEmail( - ev: EventSendAccountMultiRegistrationEmail, - ): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL - - return this - } - - public setEventSendForgotPasswordEmail(ev: EventSendForgotPasswordEmail): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.SEND_FORGOT_PASSWORD_EMAIL - - return this - } - - public setEventSendTransactionSendEmail(ev: EventSendTransactionSendEmail): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.SEND_TRANSACTION_SEND_EMAIL - - return this - } - - public setEventSendTransactionReceiveEmail(ev: EventSendTransactionReceiveEmail): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.SEND_TRANSACTION_RECEIVE_EMAIL - - return this - } - - public setEventSendTransactionLinkRedeemEmail(ev: EventSendTransactionLinkRedeemEmail): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.SEND_TRANSACTION_LINK_REDEEM_EMAIL - - return this - } - - public setEventSendAddedContributionEmail(ev: EventSendAddedContributionEmail): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.SEND_ADDED_CONTRIBUTION_EMAIL - - return this - } - - public setEventSendContributionConfirmEmail(ev: EventSendContributionConfirmEmail): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.SEND_CONTRIBUTION_CONFIRM_EMAIL - - return this - } - - public setEventConfirmationEmail(ev: EventConfirmationEmail): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.CONFIRM_EMAIL - - return this - } - - public setEventRegisterEmailKlicktipp(ev: EventRegisterEmailKlicktipp): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.REGISTER_EMAIL_KLICKTIPP - - return this - } - - public setEventLogin(ev: EventLogin): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.LOGIN - - return this - } - - public setEventLogout(ev: EventLogout): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.LOGOUT - - return this - } - - public setEventRedeemLogin(ev: EventRedeemLogin): Event { - this.setByBasicRedeem(ev.userId, ev.transactionId, ev.contributionId) - this.type = EventProtocolType.REDEEM_LOGIN - - return this - } - - public setEventActivateAccount(ev: EventActivateAccount): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.ACTIVATE_ACCOUNT - - return this - } - - public setEventPasswordChange(ev: EventPasswordChange): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.PASSWORD_CHANGE - - return this - } - - public setEventTransactionSend(ev: EventTransactionSend): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.TRANSACTION_SEND - - return this - } - - public setEventTransactionSendRedeem(ev: EventTransactionSendRedeem): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.TRANSACTION_SEND_REDEEM - - return this - } - - public setEventTransactionRepeateRedeem(ev: EventTransactionRepeateRedeem): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.TRANSACTION_REPEATE_REDEEM - - return this - } - - public setEventTransactionCreation(ev: EventTransactionCreation): Event { - this.setByBasicTx(ev.userId, ev.transactionId, ev.amount) - this.type = EventProtocolType.TRANSACTION_CREATION - - return this - } - - public setEventTransactionReceive(ev: EventTransactionReceive): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.TRANSACTION_RECEIVE - - return this - } - - public setEventTransactionReceiveRedeem(ev: EventTransactionReceiveRedeem): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.TRANSACTION_RECEIVE_REDEEM - - return this - } - - public setEventContributionCreate(ev: EventContributionCreate): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.CONTRIBUTION_CREATE - - return this - } - - public setEventAdminContributionCreate(ev: EventAdminContributionCreate): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.ADMIN_CONTRIBUTION_CREATE - - return this - } - - public setEventAdminContributionDelete(ev: EventAdminContributionDelete): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.ADMIN_CONTRIBUTION_DELETE - - return this - } - - public setEventAdminContributionDeny(ev: EventAdminContributionDeny): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.ADMIN_CONTRIBUTION_DENY - - return this - } - - public setEventAdminContributionUpdate(ev: EventAdminContributionUpdate): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.ADMIN_CONTRIBUTION_UPDATE - - return this - } - - public setEventUserCreateContributionMessage(ev: EventUserCreateContributionMessage): Event { - this.setByBasicCtMsg(ev.userId, ev.contributionId, ev.amount, ev.messageId) - this.type = EventProtocolType.USER_CREATE_CONTRIBUTION_MESSAGE - - return this - } - - public setEventAdminCreateContributionMessage(ev: EventAdminCreateContributionMessage): Event { - this.setByBasicCtMsg(ev.userId, ev.contributionId, ev.amount, ev.messageId) - this.type = EventProtocolType.ADMIN_CREATE_CONTRIBUTION_MESSAGE - - return this - } - - public setEventContributionDelete(ev: EventContributionDelete): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.CONTRIBUTION_DELETE - - return this - } - - public setEventContributionUpdate(ev: EventContributionUpdate): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.CONTRIBUTION_UPDATE - - return this - } - - public setEventContributionConfirm(ev: EventContributionConfirm): Event { - this.setByBasicCtX(ev.userId, ev.contributionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.CONTRIBUTION_CONFIRM - - return this - } - - public setEventContributionDeny(ev: EventContributionDeny): Event { - this.setByBasicCtX(ev.userId, ev.contributionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.CONTRIBUTION_DENY - - return this - } - - public setEventContributionLinkDefine(ev: EventContributionLinkDefine): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.CONTRIBUTION_LINK_DEFINE - - return this - } - - public setEventContributionLinkActivateRedeem(ev: EventContributionLinkActivateRedeem): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.CONTRIBUTION_LINK_ACTIVATE_REDEEM - - return this - } - - public setEventDeleteUser(ev: EventDeleteUser): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.DELETE_USER - - return this - } - - public setEventUndeleteUser(ev: EventUndeleteUser): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.UNDELETE_USER - - return this - } - - public setEventChangeUserRole(ev: EventChangeUserRole): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.CHANGE_USER_ROLE - - return this - } - - public setEventAdminUpdateContribution(ev: EventAdminUpdateContribution): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.ADMIN_UPDATE_CONTRIBUTION - - return this - } - - public setEventAdminDeleteContribution(ev: EventAdminDeleteContribution): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.ADMIN_DELETE_CONTRIBUTION - - return this - } - - public setEventCreateContributionLink(ev: EventCreateContributionLink): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.CREATE_CONTRIBUTION_LINK - - return this - } - - public setEventDeleteContributionLink(ev: EventDeleteContributionLink): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.DELETE_CONTRIBUTION_LINK - - return this - } - - public setEventUpdateContributionLink(ev: EventUpdateContributionLink): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.UPDATE_CONTRIBUTION_LINK - - return this - } - - setByBasicUser(userId: number): Event { - this.setEventBasic() - this.userId = userId - - return this - } - - setByBasicTx(userId: number, transactionId: number, amount: decimal): Event { - this.setByBasicUser(userId) - this.transactionId = transactionId - this.amount = amount - - return this - } - - setByBasicTxX( - userId: number, - transactionId: number, - amount: decimal, - xUserId: number, - xCommunityId: number, - ): Event { - this.setByBasicTx(userId, transactionId, amount) - this.xUserId = xUserId - this.xCommunityId = xCommunityId - - return this - } - - setByBasicCt(userId: number, contributionId: number, amount: decimal): Event { - this.setByBasicUser(userId) - this.contributionId = contributionId - this.amount = amount - - return this - } - - setByBasicCtMsg( - userId: number, - contributionId: number, - amount: decimal, - messageId: number, - ): Event { - this.setByBasicCt(userId, contributionId, amount) - this.messageId = messageId - - return this - } - - setByBasicCtX( - userId: number, - contributionId: number, - amount: decimal, - xUserId: number, - xCommunityId: number, - ): Event { - this.setByBasicCt(userId, contributionId, amount) - this.xUserId = xUserId - this.xCommunityId = xCommunityId - - return this - } - - setByBasicRedeem(userId: number, transactionId?: number, contributionId?: number): Event { - this.setByBasicUser(userId) - if (transactionId) this.transactionId = transactionId - if (contributionId) this.contributionId = contributionId - - return this - } - - id: number - type: string - createdAt: Date - userId: number - xUserId?: number - xCommunityId?: number - transactionId?: number - contributionId?: number - amount?: decimal - messageId?: number -} +export const EVENT_CONTRIBUTION_CREATE = async ( + userId: number, + contributionId: number, + amount: Decimal, +): Promise => + Event( + EventProtocolType.CONTRIBUTION_CREATE, + userId, + null, + null, + null, + contributionId, + amount, + ).save() + +export const EVENT_CONTRIBUTION_DELETE = async ( + userId: number, + contributionId: number, + amount: Decimal, +): Promise => + Event( + EventProtocolType.CONTRIBUTION_DELETE, + userId, + null, + null, + null, + contributionId, + amount, + ).save() + +export const EVENT_CONTRIBUTION_UPDATE = async ( + userId: number, + contributionId: number, + amount: Decimal, +): Promise => + Event( + EventProtocolType.CONTRIBUTION_UPDATE, + userId, + null, + null, + null, + contributionId, + amount, + ).save() + +export const EVENT_ADMIN_CONTRIBUTION_CREATE = async ( + userId: number, + contributionId: number, + amount: Decimal, +): Promise => + Event( + EventProtocolType.ADMIN_CONTRIBUTION_CREATE, + userId, + null, + null, + null, + contributionId, + amount, + ).save() + +export const EVENT_ADMIN_CONTRIBUTION_UPDATE = async ( + userId: number, + contributionId: number, + amount: Decimal, +): Promise => + Event( + EventProtocolType.ADMIN_CONTRIBUTION_UPDATE, + userId, + null, + null, + null, + contributionId, + amount, + ).save() + +export const EVENT_ADMIN_CONTRIBUTION_DELETE = async ( + userId: number, + contributionId: number, + amount: Decimal, +): Promise => + Event( + EventProtocolType.ADMIN_CONTRIBUTION_DELETE, + userId, + null, + null, + null, + contributionId, + amount, + ).save() + +export const EVENT_CONTRIBUTION_CONFIRM = async ( + userId: number, + contributionId: number, + amount: Decimal, +): Promise => + Event( + EventProtocolType.CONTRIBUTION_CONFIRM, + userId, + null, + null, + null, + contributionId, + amount, + ).save() + +export const EVENT_ADMIN_CONTRIBUTION_DENY = async ( + userId: number, + xUserId: number, + contributionId: number, + amount: Decimal, +): Promise => + Event( + EventProtocolType.ADMIN_CONTRIBUTION_DENY, + userId, + xUserId, + null, + null, + contributionId, + amount, + ).save() + +export const EVENT_TRANSACTION_SEND = async ( + userId: number, + xUserId: number, + transactionId: number, + amount: Decimal, +): Promise => + Event( + EventProtocolType.TRANSACTION_SEND, + userId, + xUserId, + null, + transactionId, + null, + amount, + ).save() + +export const EVENT_TRANSACTION_RECEIVE = async ( + userId: number, + xUserId: number, + transactionId: number, + amount: Decimal, +): Promise => + Event( + EventProtocolType.TRANSACTION_RECEIVE, + userId, + xUserId, + null, + transactionId, + null, + amount, + ).save() + +export const EVENT_LOGIN = async (userId: number): Promise => + Event(EventProtocolType.LOGIN, userId, null, null, null, null, null, null).save() + +export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = async ( + userId: number, +): Promise => Event(EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, userId).save() + +export const EVENT_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => + Event(EventProtocolType.SEND_CONFIRMATION_EMAIL, userId).save() + +export const EVENT_ADMIN_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => + Event(EventProtocolType.ADMIN_SEND_CONFIRMATION_EMAIL, userId).save() + +/* export const EVENT_REDEEM_REGISTER = async ( + userId: number, + transactionId: number | null = null, + contributionId: number | null = null, +): Promise => + Event( + EventProtocolType.REDEEM_REGISTER, + userId, + null, + null, + transactionId, + contributionId, + ).save() +*/ + +export const EVENT_REGISTER = async (userId: number): Promise => + Event(EventProtocolType.REGISTER, userId).save() + +export const EVENT_ACTIVATE_ACCOUNT = async (userId: number): Promise => + Event(EventProtocolType.ACTIVATE_ACCOUNT, userId).save() diff --git a/backend/src/event/EventProtocolEmitter.ts b/backend/src/event/EventProtocolEmitter.ts deleted file mode 100644 index a87e8a256..000000000 --- a/backend/src/event/EventProtocolEmitter.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Event } from '@/event/Event' -import { backendLogger as logger } from '@/server/logger' -import { EventProtocol } from '@entity/EventProtocol' - -export const writeEvent = async (event: Event): Promise => { - logger.info('writeEvent', event) - const dbEvent = new EventProtocol() - dbEvent.type = event.type - dbEvent.createdAt = event.createdAt - dbEvent.userId = event.userId - dbEvent.xUserId = event.xUserId || null - dbEvent.xCommunityId = event.xCommunityId || null - dbEvent.contributionId = event.contributionId || null - dbEvent.transactionId = event.transactionId || null - dbEvent.amount = event.amount || null - return dbEvent.save() -} diff --git a/backend/src/event/EventProtocolType.ts b/backend/src/event/EventProtocolType.ts index ccd15d238..3a4c914c1 100644 --- a/backend/src/event/EventProtocolType.ts +++ b/backend/src/event/EventProtocolType.ts @@ -1,50 +1,50 @@ export enum EventProtocolType { - BASIC = 'BASIC', - VISIT_GRADIDO = 'VISIT_GRADIDO', + // VISIT_GRADIDO = 'VISIT_GRADIDO', REGISTER = 'REGISTER', REDEEM_REGISTER = 'REDEEM_REGISTER', - VERIFY_REDEEM = 'VERIFY_REDEEM', - INACTIVE_ACCOUNT = 'INACTIVE_ACCOUNT', + // VERIFY_REDEEM = 'VERIFY_REDEEM', + // INACTIVE_ACCOUNT = 'INACTIVE_ACCOUNT', SEND_CONFIRMATION_EMAIL = 'SEND_CONFIRMATION_EMAIL', + ADMIN_SEND_CONFIRMATION_EMAIL = 'ADMIN_SEND_CONFIRMATION_EMAIL', SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = 'SEND_ACCOUNT_MULTIREGISTRATION_EMAIL', - CONFIRM_EMAIL = 'CONFIRM_EMAIL', - REGISTER_EMAIL_KLICKTIPP = 'REGISTER_EMAIL_KLICKTIPP', + // CONFIRM_EMAIL = 'CONFIRM_EMAIL', + // REGISTER_EMAIL_KLICKTIPP = 'REGISTER_EMAIL_KLICKTIPP', LOGIN = 'LOGIN', - LOGOUT = 'LOGOUT', - REDEEM_LOGIN = 'REDEEM_LOGIN', + // LOGOUT = 'LOGOUT', + // REDEEM_LOGIN = 'REDEEM_LOGIN', ACTIVATE_ACCOUNT = 'ACTIVATE_ACCOUNT', - SEND_FORGOT_PASSWORD_EMAIL = 'SEND_FORGOT_PASSWORD_EMAIL', - PASSWORD_CHANGE = 'PASSWORD_CHANGE', - SEND_TRANSACTION_SEND_EMAIL = 'SEND_TRANSACTION_SEND_EMAIL', - SEND_TRANSACTION_RECEIVE_EMAIL = 'SEND_TRANSACTION_RECEIVE_EMAIL', + // SEND_FORGOT_PASSWORD_EMAIL = 'SEND_FORGOT_PASSWORD_EMAIL', + // PASSWORD_CHANGE = 'PASSWORD_CHANGE', + // SEND_TRANSACTION_SEND_EMAIL = 'SEND_TRANSACTION_SEND_EMAIL', + // SEND_TRANSACTION_RECEIVE_EMAIL = 'SEND_TRANSACTION_RECEIVE_EMAIL', TRANSACTION_SEND = 'TRANSACTION_SEND', - TRANSACTION_SEND_REDEEM = 'TRANSACTION_SEND_REDEEM', - TRANSACTION_REPEATE_REDEEM = 'TRANSACTION_REPEATE_REDEEM', - TRANSACTION_CREATION = 'TRANSACTION_CREATION', + // TRANSACTION_SEND_REDEEM = 'TRANSACTION_SEND_REDEEM', + // TRANSACTION_REPEATE_REDEEM = 'TRANSACTION_REPEATE_REDEEM', + // TRANSACTION_CREATION = 'TRANSACTION_CREATION', TRANSACTION_RECEIVE = 'TRANSACTION_RECEIVE', - TRANSACTION_RECEIVE_REDEEM = 'TRANSACTION_RECEIVE_REDEEM', - SEND_TRANSACTION_LINK_REDEEM_EMAIL = 'SEND_TRANSACTION_LINK_REDEEM_EMAIL', - SEND_ADDED_CONTRIBUTION_EMAIL = 'SEND_ADDED_CONTRIBUTION_EMAIL', - SEND_CONTRIBUTION_CONFIRM_EMAIL = 'SEND_CONTRIBUTION_CONFIRM_EMAIL', + // TRANSACTION_RECEIVE_REDEEM = 'TRANSACTION_RECEIVE_REDEEM', + // SEND_TRANSACTION_LINK_REDEEM_EMAIL = 'SEND_TRANSACTION_LINK_REDEEM_EMAIL', + // SEND_ADDED_CONTRIBUTION_EMAIL = 'SEND_ADDED_CONTRIBUTION_EMAIL', + // SEND_CONTRIBUTION_CONFIRM_EMAIL = 'SEND_CONTRIBUTION_CONFIRM_EMAIL', CONTRIBUTION_CREATE = 'CONTRIBUTION_CREATE', CONTRIBUTION_CONFIRM = 'CONTRIBUTION_CONFIRM', - CONTRIBUTION_DENY = 'CONTRIBUTION_DENY', - CONTRIBUTION_LINK_DEFINE = 'CONTRIBUTION_LINK_DEFINE', - CONTRIBUTION_LINK_ACTIVATE_REDEEM = 'CONTRIBUTION_LINK_ACTIVATE_REDEEM', + // CONTRIBUTION_DENY = 'CONTRIBUTION_DENY', + // CONTRIBUTION_LINK_DEFINE = 'CONTRIBUTION_LINK_DEFINE', + // CONTRIBUTION_LINK_ACTIVATE_REDEEM = 'CONTRIBUTION_LINK_ACTIVATE_REDEEM', CONTRIBUTION_DELETE = 'CONTRIBUTION_DELETE', CONTRIBUTION_UPDATE = 'CONTRIBUTION_UPDATE', ADMIN_CONTRIBUTION_CREATE = 'ADMIN_CONTRIBUTION_CREATE', ADMIN_CONTRIBUTION_DELETE = 'ADMIN_CONTRIBUTION_DELETE', ADMIN_CONTRIBUTION_DENY = 'ADMIN_CONTRIBUTION_DENY', ADMIN_CONTRIBUTION_UPDATE = 'ADMIN_CONTRIBUTION_UPDATE', - USER_CREATE_CONTRIBUTION_MESSAGE = 'USER_CREATE_CONTRIBUTION_MESSAGE', - ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE', - DELETE_USER = 'DELETE_USER', - UNDELETE_USER = 'UNDELETE_USER', - CHANGE_USER_ROLE = 'CHANGE_USER_ROLE', - ADMIN_UPDATE_CONTRIBUTION = 'ADMIN_UPDATE_CONTRIBUTION', - ADMIN_DELETE_CONTRIBUTION = 'ADMIN_DELETE_CONTRIBUTION', - CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK', - DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK', - UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK', + // USER_CREATE_CONTRIBUTION_MESSAGE = 'USER_CREATE_CONTRIBUTION_MESSAGE', + // ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE', + // DELETE_USER = 'DELETE_USER', + // UNDELETE_USER = 'UNDELETE_USER', + // CHANGE_USER_ROLE = 'CHANGE_USER_ROLE', + // ADMIN_UPDATE_CONTRIBUTION = 'ADMIN_UPDATE_CONTRIBUTION', + // ADMIN_DELETE_CONTRIBUTION = 'ADMIN_DELETE_CONTRIBUTION', + // CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK', + // DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK', + // UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK', } diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 4a74029ad..085fb74a1 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -244,7 +244,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the create contribution event in the database', async () => { + it('stores the CONTRIBUTION_CREATE event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.CONTRIBUTION_CREATE, @@ -696,7 +696,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the update contribution event in the database', async () => { + it('stores the CONTRIBUTION_UPDATE event in the database', async () => { bibi = await query({ query: login, variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, @@ -1263,7 +1263,7 @@ describe('ContributionResolver', () => { ).resolves.toBeTruthy() }) - it('stores the delete contribution event in the database', async () => { + it('stores the CONTRIBUTION_DELETE event in the database', async () => { const contribution = await mutate({ mutation: createContribution, variables: { @@ -1780,7 +1780,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the admin create contribution event in the database', async () => { + it('stores the ADMIN_CONTRIBUTION_CREATE event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ADMIN_CONTRIBUTION_CREATE, @@ -2045,7 +2045,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the admin update contribution event in the database', async () => { + it('stores the ADMIN_CONTRIBUTION_UPDATE event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ADMIN_CONTRIBUTION_UPDATE, @@ -2085,7 +2085,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the admin update contribution event in the database', async () => { + it('stores the ADMIN_CONTRIBUTION_UPDATE event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ADMIN_CONTRIBUTION_UPDATE, @@ -2229,7 +2229,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the admin delete contribution event in the database', async () => { + it('stores the ADMIN_CONTRIBUTION_DELETE event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ADMIN_CONTRIBUTION_DELETE, @@ -2371,7 +2371,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the contribution confirm event in the database', async () => { + it('stores the CONTRIBUTION_CONFIRM event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.CONTRIBUTION_CONFIRM, @@ -2403,7 +2403,7 @@ describe('ContributionResolver', () => { }) }) - it('stores the send confirmation email event in the database', async () => { + it('stores the SEND_CONFIRMATION_EMAIL event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.SEND_CONFIRMATION_EMAIL, diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 926742d8a..f070fade5 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -37,17 +37,15 @@ import { } from './util/creations' import { MEMO_MAX_CHARS, MEMO_MIN_CHARS, FULL_CREATION_AVAILABLE } from './const/const' import { - Event, - EventContributionCreate, - EventContributionDelete, - EventContributionUpdate, - EventContributionConfirm, - EventAdminContributionCreate, - EventAdminContributionDelete, - EventAdminContributionDeny, - EventAdminContributionUpdate, + EVENT_CONTRIBUTION_CREATE, + EVENT_CONTRIBUTION_DELETE, + EVENT_CONTRIBUTION_UPDATE, + EVENT_ADMIN_CONTRIBUTION_CREATE, + EVENT_ADMIN_CONTRIBUTION_UPDATE, + EVENT_ADMIN_CONTRIBUTION_DELETE, + EVENT_CONTRIBUTION_CONFIRM, + EVENT_ADMIN_CONTRIBUTION_DENY, } from '@/event/Event' -import { writeEvent } from '@/event/EventProtocolEmitter' import { calculateDecay } from '@/util/decay' import { sendContributionConfirmedEmail, @@ -75,8 +73,6 @@ export class ContributionResolver { throw new LogError('Memo text is too long', memo.length) } - const event = new Event() - const user = getUser(context) const creations = await getUserCreation(user.id, clientTimezoneOffset) logger.trace('creations', creations) @@ -95,11 +91,7 @@ export class ContributionResolver { logger.trace('contribution to save', contribution) await DbContribution.save(contribution) - const eventCreateContribution = new EventContributionCreate() - eventCreateContribution.userId = user.id - eventCreateContribution.amount = amount - eventCreateContribution.contributionId = contribution.id - await writeEvent(event.setEventContributionCreate(eventCreateContribution)) + await EVENT_CONTRIBUTION_CREATE(user.id, contribution.id, amount) return new UnconfirmedContribution(contribution, user, creations) } @@ -110,7 +102,6 @@ export class ContributionResolver { @Arg('id', () => Int) id: number, @Ctx() context: Context, ): Promise { - const event = new Event() const user = getUser(context) const contribution = await DbContribution.findOne(id) if (!contribution) { @@ -128,11 +119,7 @@ export class ContributionResolver { contribution.deletedAt = new Date() await contribution.save() - const eventDeleteContribution = new EventContributionDelete() - eventDeleteContribution.userId = user.id - eventDeleteContribution.contributionId = contribution.id - eventDeleteContribution.amount = contribution.amount - await writeEvent(event.setEventContributionDelete(eventDeleteContribution)) + await EVENT_CONTRIBUTION_DELETE(user.id, contribution.id, contribution.amount) const res = await contribution.softRemove() return !!res @@ -279,13 +266,7 @@ export class ContributionResolver { contributionToUpdate.updatedAt = new Date() DbContribution.save(contributionToUpdate) - const event = new Event() - - const eventUpdateContribution = new EventContributionUpdate() - eventUpdateContribution.userId = user.id - eventUpdateContribution.contributionId = contributionId - eventUpdateContribution.amount = amount - await writeEvent(event.setEventContributionUpdate(eventUpdateContribution)) + await EVENT_CONTRIBUTION_UPDATE(user.id, contributionId, amount) return new UnconfirmedContribution(contributionToUpdate, user, creations) } @@ -321,7 +302,6 @@ export class ContributionResolver { ) } - const event = new Event() const moderator = getUser(context) logger.trace('moderator: ', moderator.id) const creations = await getUserCreation(emailContact.userId, clientTimezoneOffset) @@ -343,11 +323,7 @@ export class ContributionResolver { await DbContribution.save(contribution) - const eventAdminCreateContribution = new EventAdminContributionCreate() - eventAdminCreateContribution.userId = moderator.id - eventAdminCreateContribution.amount = amount - eventAdminCreateContribution.contributionId = contribution.id - await writeEvent(event.setEventAdminContributionCreate(eventAdminCreateContribution)) + await EVENT_ADMIN_CONTRIBUTION_CREATE(moderator.id, contribution.id, amount) return getUserCreation(emailContact.userId, clientTimezoneOffset) } @@ -442,12 +418,7 @@ export class ContributionResolver { result.creation = await getUserCreation(emailContact.user.id, clientTimezoneOffset) - const event = new Event() - const eventAdminContributionUpdate = new EventAdminContributionUpdate() - eventAdminContributionUpdate.userId = emailContact.user.id - eventAdminContributionUpdate.amount = amount - eventAdminContributionUpdate.contributionId = contributionToUpdate.id - await writeEvent(event.setEventAdminContributionUpdate(eventAdminContributionUpdate)) + await EVENT_ADMIN_CONTRIBUTION_UPDATE(emailContact.user.id, contributionToUpdate.id, amount) return result } @@ -518,12 +489,8 @@ export class ContributionResolver { await contribution.save() const res = await contribution.softRemove() - const event = new Event() - const eventAdminContributionDelete = new EventAdminContributionDelete() - eventAdminContributionDelete.userId = contribution.userId - eventAdminContributionDelete.amount = contribution.amount - eventAdminContributionDelete.contributionId = contribution.id - await writeEvent(event.setEventAdminContributionDelete(eventAdminContributionDelete)) + await EVENT_ADMIN_CONTRIBUTION_DELETE(contribution.userId, contribution.id, contribution.amount) + sendContributionDeletedEmail({ firstName: user.firstName, lastName: user.lastName, @@ -635,12 +602,7 @@ export class ContributionResolver { await queryRunner.release() } - const event = new Event() - const eventContributionConfirm = new EventContributionConfirm() - eventContributionConfirm.userId = user.id - eventContributionConfirm.amount = contribution.amount - eventContributionConfirm.contributionId = contribution.id - await writeEvent(event.setEventContributionConfirm(eventContributionConfirm)) + await EVENT_CONTRIBUTION_CONFIRM(user.id, contribution.id, contribution.amount) } finally { releaseLock() } @@ -730,12 +692,12 @@ export class ContributionResolver { contributionToUpdate.deniedAt = new Date() const res = await contributionToUpdate.save() - const event = new Event() - const eventAdminContributionDeny = new EventAdminContributionDeny() - eventAdminContributionDeny.userId = contributionToUpdate.userId - eventAdminContributionDeny.amount = contributionToUpdate.amount - eventAdminContributionDeny.contributionId = contributionToUpdate.id - await writeEvent(event.setEventAdminContributionDeny(eventAdminContributionDeny)) + await EVENT_ADMIN_CONTRIBUTION_DENY( + contributionToUpdate.userId, + moderator.id, + contributionToUpdate.id, + contributionToUpdate.amount, + ) sendContributionDeniedEmail({ firstName: user.firstName, diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 404a76094..6751aa6ad 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -330,7 +330,7 @@ describe('send coins', () => { ) }) - it('stores the send transaction event in the database', async () => { + it('stores the TRANSACTION_SEND event in the database', async () => { // Find the exact transaction (sent one is the one with user[1] as user) const transaction = await Transaction.find({ userId: user[1].id, @@ -347,7 +347,7 @@ describe('send coins', () => { ) }) - it('stores the receive event in the database', async () => { + it('stores the TRANSACTION_RECEIVE event in the database', async () => { // Find the exact transaction (received one is the one with user[0] as user) const transaction = await Transaction.find({ userId: user[0].id, diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 7ab9f34a1..9d5a1d38c 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -29,8 +29,7 @@ import { sendTransactionLinkRedeemedEmail, sendTransactionReceivedEmail, } from '@/emails/sendEmailVariants' -import { Event, EventTransactionReceive, EventTransactionSend } from '@/event/Event' -import { writeEvent } from '@/event/EventProtocolEmitter' +import { EVENT_TRANSACTION_RECEIVE, EVENT_TRANSACTION_SEND } from '@/event/Event' import { BalanceResolver } from './BalanceResolver' import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const' @@ -138,19 +137,19 @@ export const executeTransaction = async ( await queryRunner.commitTransaction() logger.info(`commit Transaction successful...`) - const eventTransactionSend = new EventTransactionSend() - eventTransactionSend.userId = transactionSend.userId - eventTransactionSend.xUserId = transactionSend.linkedUserId - eventTransactionSend.transactionId = transactionSend.id - eventTransactionSend.amount = transactionSend.amount.mul(-1) - await writeEvent(new Event().setEventTransactionSend(eventTransactionSend)) + await EVENT_TRANSACTION_SEND( + transactionSend.userId, + transactionSend.linkedUserId, + transactionSend.id, + transactionSend.amount.mul(-1), + ) - const eventTransactionReceive = new EventTransactionReceive() - eventTransactionReceive.userId = transactionReceive.userId - eventTransactionReceive.xUserId = transactionReceive.linkedUserId - eventTransactionReceive.transactionId = transactionReceive.id - eventTransactionReceive.amount = transactionReceive.amount - await writeEvent(new Event().setEventTransactionReceive(eventTransactionReceive)) + await EVENT_TRANSACTION_RECEIVE( + transactionReceive.userId, + transactionReceive.linkedUserId, + transactionReceive.id, + transactionReceive.amount, + ) } catch (e) { await queryRunner.rollbackTransaction() throw new LogError('Transaction was not successful', e) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 17eddca94..19eb04b34 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -19,6 +19,7 @@ import { setUserRole, deleteUser, unDeleteUser, + sendActivationEmail, } from '@/seeds/graphql/mutations' import { verifyLogin, queryOptIn, searchAdminUsers, searchUsers } from '@/seeds/graphql/queries' import { GraphQLError } from 'graphql' @@ -175,6 +176,19 @@ describe('UserResolver', () => { }) }) }) + + it('stores the REGISTER event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'peter@lustig.de' }, + { relations: ['user'] }, + ) + expect(EventProtocol.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventProtocolType.REGISTER, + userId: userConatct.user.id, + }), + ) + }) }) describe('account activation email', () => { @@ -196,7 +210,7 @@ describe('UserResolver', () => { }) }) - it('stores the send confirmation event in the database', () => { + it('stores the SEND_CONFIRMATION_EMAIL event in the database', () => { expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.SEND_CONFIRMATION_EMAIL, @@ -206,7 +220,7 @@ describe('UserResolver', () => { }) }) - describe('email already exists', () => { + describe('user already exists', () => { let mutation: User beforeAll(async () => { mutation = await mutate({ mutation: createUser, variables }) @@ -236,6 +250,19 @@ describe('UserResolver', () => { }), ) }) + + it('stores the SEND_ACCOUNT_MULTIREGISTRATION_EMAIL event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'peter@lustig.de' }, + { relations: ['user'] }, + ) + expect(EventProtocol.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, + userId: userConatct.user.id, + }), + ) + }) }) describe('unknown language', () => { @@ -328,7 +355,7 @@ describe('UserResolver', () => { ) }) - it('stores the account activated event in the database', () => { + it('stores the ACTIVATE_ACCOUNT event in the database', () => { expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ACTIVATE_ACCOUNT, @@ -337,7 +364,7 @@ describe('UserResolver', () => { ) }) - it('stores the redeem register event in the database', () => { + it('stores the REDEEM_REGISTER event in the database', () => { expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.REDEEM_REGISTER, @@ -421,7 +448,7 @@ describe('UserResolver', () => { ) }) - it('stores the redeem register event in the database', async () => { + it('stores the REDEEM_REGISTER event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.REDEEM_REGISTER, @@ -647,6 +674,19 @@ describe('UserResolver', () => { it('sets the token in the header', () => { expect(headerPushMock).toBeCalledWith({ key: 'token', value: expect.any(String) }) }) + + it('stores the LOGIN event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + expect(EventProtocol.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventProtocolType.LOGIN, + userId: userConatct.user.id, + }), + ) + }) }) describe('user is in database and wrong password', () => { @@ -887,7 +927,7 @@ describe('UserResolver', () => { ) }) - it('stores the login event in the database', () => { + it('stores the LOGIN event in the database', () => { expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.LOGIN, @@ -1668,6 +1708,157 @@ describe('UserResolver', () => { }) }) + /// + + describe('sendActivationEmail', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + mutate({ mutation: sendActivationEmail, variables: { email: 'bibi@bloxberg.de' } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('authenticated', () => { + describe('without admin rights', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + it('returns an error', async () => { + await expect( + mutate({ mutation: sendActivationEmail, variables: { email: 'bibi@bloxberg.de' } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('with admin rights', () => { + beforeAll(async () => { + admin = await userFactory(testEnv, peterLustig) + await mutate({ + mutation: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('user does not exist', () => { + it('throws an error', async () => { + jest.clearAllMocks() + await expect( + mutate({ mutation: sendActivationEmail, variables: { email: 'INVALID' } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('No user with this credentials')], + }), + ) + }) + + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith('No user with this credentials', 'invalid') + }) + }) + + describe('user is deleted', () => { + it('throws an error', async () => { + jest.clearAllMocks() + await userFactory(testEnv, stephenHawking) + await expect( + mutate({ mutation: sendActivationEmail, variables: { email: 'stephen@hawking.uk' } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('User with given email contact is deleted')], + }), + ) + }) + + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith( + 'User with given email contact is deleted', + 'stephen@hawking.uk', + ) + }) + }) + + describe('sendActivationEmail with success', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + }) + + it('returns true', async () => { + const result = await mutate({ + mutation: sendActivationEmail, + variables: { email: 'bibi@bloxberg.de' }, + }) + expect(result).toEqual( + expect.objectContaining({ + data: { + sendActivationEmail: true, + }, + }), + ) + }) + + it('sends an account activation email', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( + /{optin}/g, + userConatct.emailVerificationCode.toString(), + ).replace(/{code}/g, '') + expect(sendAccountActivationEmail).toBeCalledWith({ + firstName: 'Bibi', + lastName: 'Bloxberg', + email: 'bibi@bloxberg.de', + language: 'de', + activationLink, + timeDurationObject: expect.objectContaining({ + hours: expect.any(Number), + minutes: expect.any(Number), + }), + }) + }) + + it('stores the ADMIN_SEND_CONFIRMATION_EMAIL event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + expect(EventProtocol.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventProtocolType.ADMIN_SEND_CONFIRMATION_EMAIL, + userId: userConatct.user.id, + }), + ) + }) + }) + }) + }) + }) + describe('unDelete user', () => { describe('unauthenticated', () => { it('returns an error', async () => { diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 5ebf5b445..f9617b0df 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -48,15 +48,14 @@ import { klicktippNewsletterStateMiddleware } from '@/middleware/klicktippMiddle import { klicktippSignIn } from '@/apis/KlicktippController' import { RIGHTS } from '@/auth/RIGHTS' import { hasElopageBuys } from '@/util/hasElopageBuys' -import { writeEvent } from '@/event/EventProtocolEmitter' import { Event, - EventLogin, - EventRedeemRegister, - EventRegister, - EventSendAccountMultiRegistrationEmail, - EventSendConfirmationEmail, - EventActivateAccount, + EVENT_LOGIN, + EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, + EVENT_SEND_CONFIRMATION_EMAIL, + EVENT_REGISTER, + EVENT_ACTIVATE_ACCOUNT, + EVENT_ADMIN_SEND_CONFIRMATION_EMAIL, } from '@/event/Event' import { getUserCreations } from './util/creations' import { isValidPassword } from '@/password/EncryptorUtils' @@ -64,6 +63,7 @@ import { FULL_CREATION_AVAILABLE } from './const/const' import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor' import { PasswordEncryptionType } from '../enum/PasswordEncryptionType' import LogError from '@/server/LogError' +import { EventProtocolType } from '@/event/EventProtocolType' // eslint-disable-next-line @typescript-eslint/no-var-requires const sodium = require('sodium-native') @@ -177,9 +177,8 @@ export class UserResolver { key: 'token', value: encode(dbUser.gradidoID), }) - const ev = new EventLogin() - ev.userId = user.id - writeEvent(new Event().setEventLogin(ev)) + + await EVENT_LOGIN(user.id) logger.info(`successful Login: ${JSON.stringify(user, null, 2)}`) return user } @@ -211,7 +210,6 @@ export class UserResolver { ) // TODO: wrong default value (should be null), how does graphql work here? Is it an required field? // default int publisher_id = 0; - const event = new Event() // Validate Language (no throw) if (!language || !isLanguage(language)) { @@ -249,9 +247,9 @@ export class UserResolver { email, language: foundUser.language, // use language of the emails owner for sending }) - const eventSendAccountMultiRegistrationEmail = new EventSendAccountMultiRegistrationEmail() - eventSendAccountMultiRegistrationEmail.userId = foundUser.id - writeEvent(event.setEventSendConfirmationEmail(eventSendAccountMultiRegistrationEmail)) + + await EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL(foundUser.id) + logger.info( `sendAccountMultiRegistrationEmail by ${firstName} ${lastName} to ${foundUser.firstName} ${foundUser.lastName} <${email}>`, ) @@ -268,10 +266,7 @@ export class UserResolver { const gradidoID = await newGradidoID() - const eventRegister = new EventRegister() - const eventRedeemRegister = new EventRedeemRegister() - const eventSendConfirmEmail = new EventSendConfirmationEmail() - + const eventRegisterRedeem = Event(EventProtocolType.REDEEM_REGISTER, 0) let dbUser = new DbUser() dbUser.gradidoID = gradidoID dbUser.firstName = firstName @@ -288,14 +283,14 @@ export class UserResolver { logger.info('redeemCode found contributionLink=' + contributionLink) if (contributionLink) { dbUser.contributionLinkId = contributionLink.id - eventRedeemRegister.contributionId = contributionLink.id + eventRegisterRedeem.contributionId = contributionLink.id } } else { const transactionLink = await DbTransactionLink.findOne({ code: redeemCode }) logger.info('redeemCode found transactionLink=' + transactionLink) if (transactionLink) { dbUser.referrerId = transactionLink.userId - eventRedeemRegister.transactionId = transactionLink.id + eventRegisterRedeem.transactionId = transactionLink.id } } } @@ -333,8 +328,8 @@ export class UserResolver { timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME), }) logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`) - eventSendConfirmEmail.userId = dbUser.id - writeEvent(event.setEventSendConfirmationEmail(eventSendConfirmEmail)) + + await EVENT_SEND_CONFIRMATION_EMAIL(dbUser.id) if (!emailSent) { logger.debug(`Account confirmation link: ${activationLink}`) @@ -351,11 +346,10 @@ export class UserResolver { logger.info('createUser() successful...') if (redeemCode) { - eventRedeemRegister.userId = dbUser.id - await writeEvent(event.setEventRedeemRegister(eventRedeemRegister)) + eventRegisterRedeem.userId = dbUser.id + await eventRegisterRedeem.save() } else { - eventRegister.userId = dbUser.id - await writeEvent(event.setEventRegister(eventRegister)) + await EVENT_REGISTER(dbUser.id) } return new User(dbUser) @@ -458,8 +452,6 @@ export class UserResolver { await queryRunner.connect() await queryRunner.startTransaction('REPEATABLE READ') - const event = new Event() - try { // Save user await queryRunner.manager.save(user).catch((error) => { @@ -473,9 +465,7 @@ export class UserResolver { await queryRunner.commitTransaction() logger.info('User and UserContact data written successfully...') - const eventActivateAccount = new EventActivateAccount() - eventActivateAccount.userId = user.id - writeEvent(event.setEventActivateAccount(eventActivateAccount)) + await EVENT_ACTIVATE_ACCOUNT(user.id) } catch (e) { await queryRunner.rollbackTransaction() throw new LogError('Error on writing User and User Contact data', e) @@ -791,19 +781,12 @@ export class UserResolver { email = email.trim().toLowerCase() // const user = await dbUser.findOne({ id: emailContact.userId }) const user = await findUserByEmail(email) - if (!user) { - throw new LogError('Could not find user to given email contact', email) - } - if (user.deletedAt) { + if (user.deletedAt || user.emailContact.deletedAt) { throw new LogError('User with given email contact is deleted', email) } - const emailContact = user.emailContact - if (emailContact.deletedAt) { - throw new LogError('The given email contact for this user is deleted', email) - } - emailContact.emailResendCount++ - await emailContact.save() + user.emailContact.emailResendCount++ + await user.emailContact.save() // eslint-disable-next-line @typescript-eslint/no-unused-vars const emailSent = await sendAccountActivationEmail({ @@ -811,7 +794,7 @@ export class UserResolver { lastName: user.lastName, email, language: user.language, - activationLink: activationLink(emailContact.emailVerificationCode), + activationLink: activationLink(user.emailContact.emailVerificationCode), timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME), }) @@ -819,10 +802,7 @@ export class UserResolver { if (!emailSent) { logger.info(`Account confirmation link: ${activationLink}`) } else { - const event = new Event() - const eventSendConfirmationEmail = new EventSendConfirmationEmail() - eventSendConfirmationEmail.userId = user.id - await writeEvent(event.setEventSendConfirmationEmail(eventSendConfirmationEmail)) + await EVENT_ADMIN_SEND_CONFIRMATION_EMAIL(user.id) } return true diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 2b4ed6656..5c05a4de9 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -68,6 +68,12 @@ export const createUser = gql` } ` +export const sendActivationEmail = gql` + mutation ($email: String!) { + sendActivationEmail(email: $email) + } +` + export const sendCoins = gql` mutation ($email: String!, $amount: Decimal!, $memo: String!) { sendCoins(email: $email, amount: $amount, memo: $memo)