From 933e9168bc123899b4204c60727d97aabbd44d18 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 7 Mar 2023 13:05:26 +0100 Subject: [PATCH 01/13] events for contributionMessageResolver --- ...EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE.ts | 23 +++++++++++++++++++ .../EVENT_CONTRIBUTION_MESSAGE_CREATE.ts | 22 ++++++++++++++++++ backend/src/event/Event.ts | 2 ++ backend/src/event/EventType.ts | 2 ++ .../resolver/ContributionMessageResolver.ts | 17 ++++++++++---- 5 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 backend/src/event/EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE.ts create mode 100644 backend/src/event/EVENT_CONTRIBUTION_MESSAGE_CREATE.ts diff --git a/backend/src/event/EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE.ts b/backend/src/event/EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE.ts new file mode 100644 index 000000000..f07d38e98 --- /dev/null +++ b/backend/src/event/EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE.ts @@ -0,0 +1,23 @@ +import { User as DbUser } from '@entity/User' +import { Contribution as DbContribution } from '@entity/Contribution' +import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage' +import { Event as DbEvent } from '@entity/Event' +import { Event, EventType } from './Event' + +export const EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE = async ( + user: DbUser, + moderator: DbUser, + contribution: DbContribution, + contributionMessage: DbContributionMessage, +): Promise => + Event( + EventType.ADMIN_CONTRIBUTION_MESSAGE_CREATE, + user, + moderator, + null, + null, + contribution, + contributionMessage, + null, + null, + ).save() diff --git a/backend/src/event/EVENT_CONTRIBUTION_MESSAGE_CREATE.ts b/backend/src/event/EVENT_CONTRIBUTION_MESSAGE_CREATE.ts new file mode 100644 index 000000000..b06685a6d --- /dev/null +++ b/backend/src/event/EVENT_CONTRIBUTION_MESSAGE_CREATE.ts @@ -0,0 +1,22 @@ +import { User as DbUser } from '@entity/User' +import { Contribution as DbContribution } from '@entity/Contribution' +import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage' +import { Event as DbEvent } from '@entity/Event' +import { Event, EventType } from './Event' + +export const EVENT_CONTRIBUTION_MESSAGE_CREATE = async ( + user: DbUser, + contribution: DbContribution, + contributionMessage: DbContributionMessage, +): Promise => + Event( + EventType.CONTRIBUTION_MESSAGE_CREATE, + user, + user, + null, + null, + contribution, + contributionMessage, + null, + null, + ).save() diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 2e7cca6af..f1c4269c9 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -45,10 +45,12 @@ export { EVENT_ADMIN_CONTRIBUTION_UPDATE } from './EVENT_ADMIN_CONTRIBUTION_UPDA export { EVENT_ADMIN_CONTRIBUTION_LINK_CREATE } from './EVENT_ADMIN_CONTRIBUTION_LINK_CREATE' export { EVENT_ADMIN_CONTRIBUTION_LINK_DELETE } from './EVENT_ADMIN_CONTRIBUTION_LINK_DELETE' export { EVENT_ADMIN_CONTRIBUTION_LINK_UPDATE } from './EVENT_ADMIN_CONTRIBUTION_LINK_UPDATE' +export { EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE } from './EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE' export { EVENT_ADMIN_SEND_CONFIRMATION_EMAIL } from './EVENT_ADMIN_SEND_CONFIRMATION_EMAIL' export { EVENT_CONTRIBUTION_CREATE } from './EVENT_CONTRIBUTION_CREATE' export { EVENT_CONTRIBUTION_DELETE } from './EVENT_CONTRIBUTION_DELETE' export { EVENT_CONTRIBUTION_UPDATE } from './EVENT_CONTRIBUTION_UPDATE' +export { EVENT_CONTRIBUTION_MESSAGE_CREATE } from './EVENT_CONTRIBUTION_MESSAGE_CREATE' export { EVENT_LOGIN } from './EVENT_LOGIN' export { EVENT_REGISTER } from './EVENT_REGISTER' export { EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL } from './EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL' diff --git a/backend/src/event/EventType.ts b/backend/src/event/EventType.ts index b219a49ba..dda571b5a 100644 --- a/backend/src/event/EventType.ts +++ b/backend/src/event/EventType.ts @@ -9,10 +9,12 @@ export enum EventType { ADMIN_CONTRIBUTION_LINK_CREATE = 'ADMIN_CONTRIBUTION_LINK_CREATE', ADMIN_CONTRIBUTION_LINK_DELETE = 'ADMIN_CONTRIBUTION_LINK_DELETE', ADMIN_CONTRIBUTION_LINK_UPDATE = 'ADMIN_CONTRIBUTION_LINK_UPDATE', + ADMIN_CONTRIBUTION_MESSAGE_CREATE = 'ADMIN_CONTRIBUTION_MESSAGE_CREATE', ADMIN_SEND_CONFIRMATION_EMAIL = 'ADMIN_SEND_CONFIRMATION_EMAIL', CONTRIBUTION_CREATE = 'CONTRIBUTION_CREATE', CONTRIBUTION_DELETE = 'CONTRIBUTION_DELETE', CONTRIBUTION_UPDATE = 'CONTRIBUTION_UPDATE', + CONTRIBUTION_MESSAGE_CREATE = 'CONTRIBUTION_MESSAGE_CREATE', LOGIN = 'LOGIN', REGISTER = 'REGISTER', REDEEM_REGISTER = 'REDEEM_REGISTER', diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index adfcdf160..d4cab2340 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -16,6 +16,7 @@ import { RIGHTS } from '@/auth/RIGHTS' import { Context, getUser } from '@/server/context' import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants' import LogError from '@/server/LogError' +import { EVENT_CONTRIBUTION_MESSAGE_CREATE } from '@/event/Event' @Resolver() export class ContributionMessageResolver { @@ -56,6 +57,11 @@ export class ContributionMessageResolver { await queryRunner.manager.update(DbContribution, { id: contributionId }, contribution) } await queryRunner.commitTransaction() + await EVENT_CONTRIBUTION_MESSAGE_CREATE( + user, + { id: contributionMessage.contributionId } as DbContribution, + contributionMessage, + ) } catch (e) { await queryRunner.rollbackTransaction() throw new LogError(`ContributionMessage was not sent successfully: ${e}`, e) @@ -97,7 +103,7 @@ export class ContributionMessageResolver { @Args() { contributionId, message }: ContributionMessageArgs, @Ctx() context: Context, ): Promise { - const user = getUser(context) + const moderator = getUser(context) const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() @@ -111,7 +117,7 @@ export class ContributionMessageResolver { if (!contribution) { throw new LogError('Contribution not found', contributionId) } - if (contribution.userId === user.id) { + if (contribution.userId === moderator.id) { throw new LogError('Admin can not answer on his own contribution', contributionId) } if (!contribution.user.emailContact) { @@ -122,7 +128,7 @@ export class ContributionMessageResolver { contributionMessage.contributionId = contributionId contributionMessage.createdAt = new Date() contributionMessage.message = message - contributionMessage.userId = user.id + contributionMessage.userId = moderator.id contributionMessage.type = ContributionMessageType.DIALOG contributionMessage.isModerator = true await queryRunner.manager.insert(DbContributionMessage, contributionMessage) @@ -141,11 +147,12 @@ export class ContributionMessageResolver { lastName: contribution.user.lastName, email: contribution.user.emailContact.email, language: contribution.user.language, - senderFirstName: user.firstName, - senderLastName: user.lastName, + senderFirstName: moderator.firstName, + senderLastName: moderator.lastName, contributionMemo: contribution.memo, }) await queryRunner.commitTransaction() + await EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE({id: contribution.userId} as DbUser, moderator, contribution, contributionMessage) } catch (e) { await queryRunner.rollbackTransaction() throw new LogError(`ContributionMessage was not sent successfully: ${e}`, e) From fd912fb6c8596a2502e52b08c970a9e684bf8c20 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 7 Mar 2023 13:12:39 +0100 Subject: [PATCH 02/13] tests for the two new events --- .../ContributionMessageResolver.test.ts | 26 +++++++++++++++++++ .../resolver/ContributionMessageResolver.ts | 19 ++++++++++---- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index f3e5e865d..307eef8c5 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -15,6 +15,8 @@ import { userFactory } from '@/seeds/factory/user' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants' +import { EventType } from '@/event/Event' +import { Event as DbEvent } from '@entity/Event' jest.mock('@/emails/sendEmailVariants', () => { const originalModule = jest.requireActual('@/emails/sendEmailVariants') @@ -192,6 +194,18 @@ describe('ContributionMessageResolver', () => { contributionMemo: 'Test env contribution', }) }) + + it('stores the ADMIN_CONTRIBUTION_MESSAGE_CREATE event in the database', async () => { + await expect(DbEvent.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventType.ADMIN_CONTRIBUTION_MESSAGE_CREATE, + affectedUserId: expect.any(Number), + actingUserId: expect.any(Number), + involvedContributionId: result.data.createContribution.id, + involvedContributionMessageId: expect.any(Number), + }), + ) + }) }) }) }) @@ -317,6 +331,18 @@ describe('ContributionMessageResolver', () => { }), ) }) + + it('stores the CONTRIBUTION_MESSAGE_CREATE event in the database', async () => { + await expect(DbEvent.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventType.CONTRIBUTION_MESSAGE_CREATE, + affectedUserId: expect.any(Number), + actingUserId: expect.any(Number), + involvedContributionId: result.data.createContribution.id, + involvedContributionMessageId: expect.any(Number), + }), + ) + }) }) }) }) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index d4cab2340..c8378bcf4 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -3,7 +3,8 @@ import { getConnection } from '@dbTools/typeorm' import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage' import { Contribution as DbContribution } from '@entity/Contribution' -import { UserContact } from '@entity/UserContact' +import { UserContact as DbUserContact } from '@entity/UserContact' +import { User as DbUser } from '@entity/User' import { ContributionMessage, ContributionMessageListResult } from '@model/ContributionMessage' import ContributionMessageArgs from '@arg/ContributionMessageArgs' @@ -16,7 +17,10 @@ import { RIGHTS } from '@/auth/RIGHTS' import { Context, getUser } from '@/server/context' import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants' import LogError from '@/server/LogError' -import { EVENT_CONTRIBUTION_MESSAGE_CREATE } from '@/event/Event' +import { + EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE, + EVENT_CONTRIBUTION_MESSAGE_CREATE, +} from '@/event/Event' @Resolver() export class ContributionMessageResolver { @@ -121,7 +125,7 @@ export class ContributionMessageResolver { throw new LogError('Admin can not answer on his own contribution', contributionId) } if (!contribution.user.emailContact) { - contribution.user.emailContact = await UserContact.findOneOrFail({ + contribution.user.emailContact = await DbUserContact.findOneOrFail({ where: { id: contribution.user.emailId }, }) } @@ -152,13 +156,18 @@ export class ContributionMessageResolver { contributionMemo: contribution.memo, }) await queryRunner.commitTransaction() - await EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE({id: contribution.userId} as DbUser, moderator, contribution, contributionMessage) + await EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE( + { id: contribution.userId } as DbUser, + moderator, + contribution, + contributionMessage, + ) } catch (e) { await queryRunner.rollbackTransaction() throw new LogError(`ContributionMessage was not sent successfully: ${e}`, e) } finally { await queryRunner.release() } - return new ContributionMessage(contributionMessage, user) + return new ContributionMessage(contributionMessage, moderator) } } From 920f16aad51574e3a09cf40c1199ce4ff68b1505 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 7 Mar 2023 13:33:08 +0100 Subject: [PATCH 03/13] corrected test name, some code style fixes --- .../graphql/resolver/ContributionResolver.test.ts | 2 +- .../src/graphql/resolver/ContributionResolver.ts | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 053e434ea..2b59bde8b 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -2541,7 +2541,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the CONTRIBUTION_CONFIRM event in the database', async () => { + it('stores the ADMIN_CONTRIBUTION_CONFIRM event in the database', async () => { await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventType.ADMIN_CONTRIBUTION_CONFIRM, diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 62c53d993..34a27997b 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -89,7 +89,6 @@ export class ContributionResolver { logger.trace('contribution to save', contribution) await DbContribution.save(contribution) - await EVENT_CONTRIBUTION_CREATE(user, contribution, amount) return new UnconfirmedContribution(contribution, user, creations) @@ -117,7 +116,6 @@ export class ContributionResolver { contribution.deletedBy = user.id contribution.deletedAt = new Date() await contribution.save() - await EVENT_CONTRIBUTION_DELETE(user, contribution, contribution.amount) const res = await contribution.softRemove() @@ -254,7 +252,6 @@ export class ContributionResolver { contributionToUpdate.contributionStatus = ContributionStatus.PENDING contributionToUpdate.updatedAt = new Date() DbContribution.save(contributionToUpdate) - await EVENT_CONTRIBUTION_UPDATE(user, contributionToUpdate, amount) return new UnconfirmedContribution(contributionToUpdate, user, creations) @@ -307,11 +304,8 @@ export class ContributionResolver { contribution.moderatorId = moderator.id contribution.contributionType = ContributionType.ADMIN contribution.contributionStatus = ContributionStatus.PENDING - logger.trace('contribution to save', contribution) - await DbContribution.save(contribution) - await EVENT_ADMIN_CONTRIBUTION_CREATE(emailContact.user, moderator, contribution, amount) return getUserCreation(emailContact.userId, clientTimezoneOffset) @@ -377,9 +371,7 @@ export class ContributionResolver { result.amount = amount result.memo = contributionToUpdate.memo result.date = contributionToUpdate.contributionDate - result.creation = await getUserCreation(emailContact.user.id, clientTimezoneOffset) - await EVENT_ADMIN_CONTRIBUTION_UPDATE( emailContact.user, moderator, @@ -440,14 +432,12 @@ export class ContributionResolver { contribution.deletedBy = moderator.id await contribution.save() const res = await contribution.softRemove() - await EVENT_ADMIN_CONTRIBUTION_DELETE( { id: contribution.userId } as DbUser, moderator, contribution, contribution.amount, ) - sendContributionDeletedEmail({ firstName: user.firstName, lastName: user.lastName, @@ -558,7 +548,6 @@ export class ContributionResolver { } finally { await queryRunner.release() } - await EVENT_ADMIN_CONTRIBUTION_CONFIRM(user, moderatorUser, contribution, contribution.amount) } finally { releaseLock() @@ -648,14 +637,12 @@ export class ContributionResolver { contributionToUpdate.deniedBy = moderator.id contributionToUpdate.deniedAt = new Date() const res = await contributionToUpdate.save() - await EVENT_ADMIN_CONTRIBUTION_DENY( user, moderator, contributionToUpdate, contributionToUpdate.amount, ) - sendContributionDeniedEmail({ firstName: user.firstName, lastName: user.lastName, From 90d9b2114ec02ccf4c8911e3f2ca87b6a0a9a988 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 24 Mar 2023 00:38:19 +0100 Subject: [PATCH 04/13] refactor(backend): add klicktipp-api library --- backend/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/package.json b/backend/package.json index 22e75f4af..19427091b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -68,6 +68,7 @@ "eslint-plugin-type-graphql": "^1.0.0", "faker": "^5.5.3", "jest": "^27.2.4", + "klicktipp-api": "^1.0.2", "nodemon": "^2.0.7", "prettier": "^2.3.1", "ts-jest": "^27.0.5", From 34f96eb284ab5c7401d6d139de82ffaf591e3093 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 24 Mar 2023 00:39:48 +0100 Subject: [PATCH 05/13] remove translated library --- backend/src/apis/klicktippConnector.ts | 624 ------------------------- 1 file changed, 624 deletions(-) delete mode 100644 backend/src/apis/klicktippConnector.ts diff --git a/backend/src/apis/klicktippConnector.ts b/backend/src/apis/klicktippConnector.ts deleted file mode 100644 index 0ff741604..000000000 --- a/backend/src/apis/klicktippConnector.ts +++ /dev/null @@ -1,624 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/restrict-template-expressions */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import axios, { AxiosRequestConfig, Method } from 'axios' - -export class KlicktippConnector { - private baseURL: string - private sessionName: string - private sessionId: string - private error: string - - constructor(service?: string) { - this.baseURL = service !== undefined ? service : 'https://api.klicktipp.com' - this.sessionName = '' - this.sessionId = '' - } - - /** - * Get last error - * - * @return string an error description of the last error - */ - getLastError(): string { - const result = this.error - return result - } - - /** - * login - * - * @param username The login name of the user to login. - * @param password The password of the user. - * @return TRUE on success - */ - async login(username: string, password: string): Promise { - if (!(username.length > 0 && password.length > 0)) { - throw new Error('Klicktipp Login failed: Illegal Arguments') - } - - const res = await this.httpRequest('/account/login', 'POST', { username, password }, false) - - if (!res.isAxiosError) { - this.sessionId = res.data.sessid - this.sessionName = res.data.session_name - - return true - } - - throw new Error(`Klicktipp Login failed: ${res.response.statusText}`) - } - - /** - * Logs out the user currently logged in. - * - * @return TRUE on success - */ - async logout(): Promise { - const res = await this.httpRequest('/account/logout', 'POST') - - if (!res.isAxiosError) { - this.sessionId = '' - this.sessionName = '' - - return true - } - - throw new Error(`Klicktipp Logout failed: ${res.response.statusText}`) - } - - /** - * Get all subscription processes (lists) of the logged in user. Requires to be logged in. - * - * @return A associative obeject => - */ - async subscriptionProcessIndex(): Promise { - const res = await this.httpRequest('/list', 'GET', {}, true) - - if (!res.isAxiosError) { - return res.data - } - - throw new Error(`Klicktipp Subscription process index failed: ${res.response.statusText}`) - } - - /** - * Get subscription process (list) definition. Requires to be logged in. - * - * @param listid The id of the subscription process - * - * @return An object representing the Klicktipp subscription process. - */ - async subscriptionProcessGet(listid: string): Promise { - if (!listid || listid === '') { - throw new Error('Klicktipp Illegal Arguments') - } - - // retrieve - const res = await this.httpRequest(`/subscriber/${listid}`, 'GET', {}, true) - - if (!res.isAxiosError) { - return res.data - } - - throw new Error(`Klicktipp Subscription process get failed: ${res.response.statusText}`) - } - - /** - * Get subscription process (list) redirection url for given subscription. - * - * @param listid The id of the subscription process. - * @param email The email address of the subscriber. - * - * @return A redirection url as defined in the subscription process. - */ - async subscriptionProcessRedirect(listid: string, email: string): Promise { - if (!listid || listid === '' || !email || email === '') { - throw new Error('Klicktipp Illegal Arguments') - } - - // update - const data = { listid, email } - const res = await this.httpRequest('/list/redirect', 'POST', data) - - if (!res.isAxiosError) { - return res.data - } - - throw new Error( - `Klicktipp Subscription process get redirection url failed: ${res.response.statusText}`, - ) - } - - /** - * Get all manual tags of the logged in user. Requires to be logged in. - * - * @return A associative object => - */ - async tagIndex(): Promise { - const res = await this.httpRequest('/tag', 'GET', {}, true) - - if (!res.isAxiosError) { - return res.data - } - - throw new Error(`Klicktipp Tag index failed: ${res.response.statusText}`) - } - - /** - * Get a tag definition. Requires to be logged in. - * - * @param tagid The tag id. - * - * @return An object representing the Klicktipp tag object. - */ - async tagGet(tagid: string): Promise { - if (!tagid || tagid === '') { - throw new Error('Klicktipp Illegal Arguments') - } - const res = await this.httpRequest(`/tag/${tagid}`, 'GET', {}, true) - - if (!res.isAxiosError) { - return res.data - } - - throw new Error(`Klicktipp Tag get failed: ${res.response.statusText}`) - } - - /** - * Create a new manual tag. Requires to be logged in. - * - * @param name The name of the tag. - * @param text (optional) An additional description of the tag. - * - * @return The id of the newly created tag or false if failed. - */ - async tagCreate(name: string, text?: string): Promise { - if (!name || name === '') { - throw new Error('Klicktipp Illegal Arguments') - } - const data = { - name, - text: text !== undefined ? text : '', - } - const res = await this.httpRequest('/tag', 'POST', data, true) - - if (!res.isAxiosError) { - return res.data - } - - throw new Error(`Klicktipp Tag creation failed: ${res.response.statusText}`) - } - - /** - * Updates a tag. Requires to be logged in. - * - * @param tagid The tag id used to identify which tag to modify. - * @param name (optional) The new tag name. Set empty to leave it unchanged. - * @param text (optional) The new tag description. Set empty to leave it unchanged. - * - * @return TRUE on success - */ - async tagUpdate(tagid: string, name?: string, text?: string): Promise { - if (!tagid || tagid === '' || (name === '' && text === '')) { - throw new Error('Klicktipp Illegal Arguments') - } - const data = { - name: name !== undefined ? name : '', - text: text !== undefined ? text : '', - } - - const res = await this.httpRequest(`/tag/${tagid}`, 'PUT', data, true) - - if (!res.isAxiosError) { - return true - } - - throw new Error(`Klicktipp Tag update failed: ${res.response.statusText}`) - } - - /** - * Deletes a tag. Requires to be logged in. - * - * @param tagid The user id of the user to delete. - * - * @return TRUE on success - */ - async tagDelete(tagid: string): Promise { - if (!tagid || tagid === '') { - throw new Error('Klicktipp Illegal Arguments') - } - - const res = await this.httpRequest(`/tag/${tagid}`, 'DELETE') - - if (!res.isAxiosError) { - return true - } - - throw new Error(`Klicktipp Tag deletion failed: ${res.response.statusText}`) - } - - /** - * Get all contact fields of the logged in user. Requires to be logged in. - * - * @return A associative object => - */ - async fieldIndex(): Promise { - const res = await this.httpRequest('/field', 'GET', {}, true) - - if (!res.isAxiosError) { - return res.data - } - - throw new Error(`Klicktipp Field index failed: ${res.response.statusText}`) - } - - /** - * Subscribe an email. Requires to be logged in. - * - * @param email The email address of the subscriber. - * @param listid (optional) The id subscription process. - * @param tagid (optional) The id of the manual tag the subscriber will be tagged with. - * @param fields (optional) Additional fields of the subscriber. - * - * @return An object representing the Klicktipp subscriber object. - */ - async subscribe( - email: string, - listid?: number, - tagid?: number, - fields?: any, - smsnumber?: string, - ): Promise { - if ((!email || email === '') && smsnumber === '') { - throw new Error('Illegal Arguments') - } - // subscribe - const data = { - email, - fields: fields !== undefined ? fields : {}, - smsnumber: smsnumber !== undefined ? smsnumber : '', - listid: listid !== undefined ? listid : 0, - tagid: tagid !== undefined ? tagid : 0, - } - - const res = await this.httpRequest('/subscriber', 'POST', data, true) - - if (!res.isAxiosError) { - return res.data - } - throw new Error(`Klicktipp Subscription failed: ${res.response.statusText}`) - } - - /** - * Unsubscribe an email. Requires to be logged in. - * - * @param email The email address of the subscriber. - * - * @return TRUE on success - */ - async unsubscribe(email: string): Promise { - if (!email || email === '') { - throw new Error('Klicktipp Illegal Arguments') - } - - // unsubscribe - const data = { email } - - const res = await this.httpRequest('/subscriber/unsubscribe', 'POST', data, true) - - if (!res.isAxiosError) { - return true - } - throw new Error(`Klicktipp Unsubscription failed: ${res.response.statusText}`) - } - - /** - * Tag an email. Requires to be logged in. - * - * @param email The email address of the subscriber. - * @param tagids an array of the manual tag(s) the subscriber will be tagged with. - * - * @return TRUE on success - */ - async tag(email: string, tagids: string): Promise { - if (!email || email === '' || !tagids || tagids === '') { - throw new Error('Klicktipp Illegal Arguments') - } - - // tag - const data = { - email, - tagids, - } - - const res = await this.httpRequest('/subscriber/tag', 'POST', data, true) - - if (!res.isAxiosError) { - return res.data - } - throw new Error(`Klicktipp Tagging failed: ${res.response.statusText}`) - } - - /** - * Untag an email. Requires to be logged in. - * - * @param mixed $email The email address of the subscriber. - * @param mixed $tagid The id of the manual tag that will be removed from the subscriber. - * - * @return TRUE on success. - */ - async untag(email: string, tagid: string): Promise { - if (!email || email === '' || !tagid || tagid === '') { - throw new Error('Klicktipp Illegal Arguments') - } - - // subscribe - const data = { - email, - tagid, - } - - const res = await this.httpRequest('/subscriber/untag', 'POST', data, true) - - if (!res.isAxiosError) { - return true - } - throw new Error(`Klicktipp Untagging failed: ${res.response.statusText}`) - } - - /** - * Resend an autoresponder for an email address. Requires to be logged in. - * - * @param email A valid email address - * @param autoresponder An id of the autoresponder - * - * @return TRUE on success - */ - async resend(email: string, autoresponder: string): Promise { - if (!email || email === '' || !autoresponder || autoresponder === '') { - throw new Error('Klicktipp Illegal Arguments') - } - - // resend/reset autoresponder - const data = { email, autoresponder } - - const res = await this.httpRequest('/subscriber/resend', 'POST', data, true) - - if (!res.isAxiosError) { - return true - } - throw new Error(`Klicktipp Resend failed: ${res.response.statusText}`) - } - - /** - * Get all active subscribers. Requires to be logged in. - * - * @return An array of subscriber ids. - */ - async subscriberIndex(): Promise<[string]> { - const res = await this.httpRequest('/subscriber', 'GET', undefined, true) - - if (!res.isAxiosError) { - return res.data - } - throw new Error(`Klicktipp Subscriber index failed: ${res.response.statusText}`) - } - - /** - * Get subscriber information. Requires to be logged in. - * - * @param subscriberid The subscriber id. - * - * @return An object representing the Klicktipp subscriber. - */ - async subscriberGet(subscriberid: string): Promise { - if (!subscriberid || subscriberid === '') { - throw new Error('Klicktipp Illegal Arguments') - } - - // retrieve - const res = await this.httpRequest(`/subscriber/${subscriberid}`, 'GET', {}, true) - if (!res.isAxiosError) { - return res.data - } - throw new Error(`Klicktipp Subscriber get failed: ${res.response.statusText}`) - } - - /** - * Get a subscriber id by email. Requires to be logged in. - * - * @param email The email address of the subscriber. - * - * @return The id of the subscriber. Use subscriber_get to get subscriber details. - */ - async subscriberSearch(email: string): Promise { - if (!email || email === '') { - throw new Error('Klicktipp Illegal Arguments') - } - // search - const data = { email } - const res = await this.httpRequest('/subscriber/search', 'POST', data, true) - - if (!res.isAxiosError) { - return res.data - } - throw new Error(`Klicktipp Subscriber search failed: ${res.response.statusText}`) - } - - /** - * Get all active subscribers tagged with the given tag id. Requires to be logged in. - * - * @param tagid The id of the tag. - * - * @return An array with id -> subscription date of the tagged subscribers. Use subscriber_get to get subscriber details. - */ - async subscriberTagged(tagid: string): Promise { - if (!tagid || tagid === '') { - throw new Error('Klicktipp Illegal Arguments') - } - - // search - const data = { tagid } - const res = await this.httpRequest('/subscriber/tagged', 'POST', data, true) - - if (!res.isAxiosError) { - return res.data - } - throw new Error(`Klicktipp subscriber tagged failed: ${res.response.statusText}`) - } - - /** - * Updates a subscriber. Requires to be logged in. - * - * @param subscriberid The id of the subscriber to update. - * @param fields (optional) The fields of the subscriber to update - * @param newemail (optional) The new email of the subscriber to update - * - * @return TRUE on success - */ - async subscriberUpdate( - subscriberid: string, - fields?: any, - newemail?: string, - newsmsnumber?: string, - ): Promise { - if (!subscriberid || subscriberid === '') { - throw new Error('Klicktipp Illegal Arguments') - } - - // update - const data = { - fields: fields !== undefined ? fields : {}, - newemail: newemail !== undefined ? newemail : '', - newsmsnumber: newsmsnumber !== undefined ? newsmsnumber : '', - } - const res = await this.httpRequest(`/subscriber/${subscriberid}`, 'PUT', data, true) - if (!res.isAxiosError) { - return true - } - throw new Error(`Klicktipp Subscriber update failed: ${res.response.statusText}`) - } - - /** - * Delete a subscribe. Requires to be logged in. - * - * @param subscriberid The id of the subscriber to update. - * - * @return TRUE on success. - */ - async subscriberDelete(subscriberid: string): Promise { - if (!subscriberid || subscriberid === '') { - throw new Error('Klicktipp Illegal Arguments') - } - - // delete - const res = await this.httpRequest(`/subscriber/${subscriberid}`, 'DELETE', {}, true) - - if (!res.isAxiosError) { - return true - } - throw new Error(`Klicktipp Subscriber deletion failed: ${res.response.statusText}`) - } - - /** - * Subscribe an email. Requires an api key. - * - * @param apikey The api key (listbuildng configuration). - * @param email The email address of the subscriber. - * @param fields (optional) Additional fields of the subscriber. - * - * @return A redirection url as defined in the subscription process. - */ - async signin(apikey: string, email: string, fields?: any, smsnumber?: string): Promise { - if (!apikey || apikey === '' || ((!email || email === '') && smsnumber === '')) { - throw new Error('Klicktipp Illegal Arguments') - } - - // subscribe - const data = { - apikey, - email, - fields: fields !== undefined ? fields : {}, - smsnumber: smsnumber !== undefined ? smsnumber : '', - } - - const res = await this.httpRequest('/subscriber/signin', 'POST', data) - - if (!res.isAxiosError) { - return true - } - throw new Error(`Klicktipp Subscription failed: ${res.response.statusText}`) - } - - /** - * Untag an email. Requires an api key. - * - * @param apikey The api key (listbuildng configuration). - * @param email The email address of the subscriber. - * - * @return TRUE on success - */ - async signout(apikey: string, email: string): Promise { - if (!apikey || apikey === '' || !email || email === '') { - throw new Error('Klicktipp Illegal Arguments') - } - - // untag - const data = { apikey, email } - const res = await this.httpRequest('/subscriber/signout', 'POST', data) - - if (!res.isAxiosError) { - return true - } - throw new Error(`Klicktipp Untagging failed: ${res.response.statusText}`) - } - - /** - * Unsubscribe an email. Requires an api key. - * - * @param apikey The api key (listbuildng configuration). - * @param email The email address of the subscriber. - * - * @return TRUE on success - */ - async signoff(apikey: string, email: string): Promise { - if (!apikey || apikey === '' || !email || email === '') { - throw new Error('Klicktipp Illegal Arguments') - } - - // unsubscribe - const data = { apikey, email } - const res = await this.httpRequest('/subscriber/signoff', 'POST', data) - - if (!res.isAxiosError) { - return true - } - throw new Error(`Klicktipp Unsubscription failed: ${res.response.statusText}`) - } - - async httpRequest(path: string, method?: Method, data?: any, usesession?: boolean): Promise { - if (method === undefined) { - method = 'GET' - } - const options: AxiosRequestConfig = { - baseURL: this.baseURL, - method, - url: path, - data, - headers: { - 'Content-Type': 'application/json', - Content: 'application/json', - Cookie: - usesession && this.sessionName !== '' ? `${this.sessionName}=${this.sessionId}` : '', - }, - } - - return axios(options) - .then((res) => res) - .catch((error) => error) - } -} From 4faa36da391955e49f6b93f25a2aadea085c842d Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 24 Mar 2023 00:40:27 +0100 Subject: [PATCH 06/13] change import to library --- backend/src/apis/KlicktippController.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/apis/KlicktippController.ts b/backend/src/apis/KlicktippController.ts index fe9ad563a..a291bb945 100644 --- a/backend/src/apis/KlicktippController.ts +++ b/backend/src/apis/KlicktippController.ts @@ -1,8 +1,10 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { KlicktippConnector } from './klicktippConnector' +import KlicktippConnector from 'klicktipp-api' import CONFIG from '@/config' const klicktippConnector = new KlicktippConnector() From a8a4d62847d12afc6ff31cb67525615b1f5cfc7b Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 24 Mar 2023 00:41:01 +0100 Subject: [PATCH 07/13] create typescript .d.ts file --- backend/@types/klicktipp-api/index.d.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 backend/@types/klicktipp-api/index.d.ts diff --git a/backend/@types/klicktipp-api/index.d.ts b/backend/@types/klicktipp-api/index.d.ts new file mode 100644 index 000000000..e34bfc6c6 --- /dev/null +++ b/backend/@types/klicktipp-api/index.d.ts @@ -0,0 +1 @@ +declare module 'klicktipp-api' From a78397caf53b5a2aee73b24eecf56b6903e92431 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 24 Mar 2023 00:41:40 +0100 Subject: [PATCH 08/13] change definition of @types to root --- backend/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 52241a0a6..146cd41e3 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -59,7 +59,7 @@ "@entity/*": ["../database/entity/*", "../../database/build/entity/*"] }, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - "typeRoots": ["src/federation/@types", "node_modules/@types"], /* List of folders to include type definitions from. */ + "typeRoots": ["@types", "node_modules/@types"], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ From c2ef4bca8e355026ed6584ea3139bb2a0f3241f3 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 24 Mar 2023 00:42:07 +0100 Subject: [PATCH 09/13] add klicktipp to yarn lock --- backend/yarn.lock | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/yarn.lock b/backend/yarn.lock index 3151557ab..e169a9ce7 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -4526,6 +4526,13 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +klicktipp-api@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/klicktipp-api/-/klicktipp-api-1.0.2.tgz#a7ba728887c4d9a1c257fa30b78cbe0be92a20ab" + integrity sha512-aQQpuznC0O2W7Oq2BxKDnuLAnGmKTMfudOQ0TAEf0TLv82KH2AsCXl0nbutJ2g1i3MH+sCyGE/r/nwnUhr4QeA== + dependencies: + axios "^0.21.1" + latest-version@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" From 1d908b3a26fac313e678c91d654a4b5a3b87267e Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 24 Mar 2023 00:45:05 +0100 Subject: [PATCH 10/13] refactor(backend): upgrade coverage to 85% --- backend/jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/jest.config.js b/backend/jest.config.js index 66a883b51..f2e2a556d 100644 --- a/backend/jest.config.js +++ b/backend/jest.config.js @@ -6,7 +6,7 @@ module.exports = { collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], coverageThreshold: { global: { - lines: 81, + lines: 85, }, }, setupFiles: ['/test/testSetup.ts'], From 783831b3de59eddaff219226cbea45732c6e829d Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 24 Mar 2023 00:57:43 +0100 Subject: [PATCH 11/13] Remove unnecessary eslint-disable --- backend/src/apis/KlicktippController.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/src/apis/KlicktippController.ts b/backend/src/apis/KlicktippController.ts index a291bb945..56dd067e9 100644 --- a/backend/src/apis/KlicktippController.ts +++ b/backend/src/apis/KlicktippController.ts @@ -2,8 +2,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import KlicktippConnector from 'klicktipp-api' import CONFIG from '@/config' From 43d01cfbf86392ff55751700286b6dfe90f2840c Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 24 Mar 2023 01:04:27 +0100 Subject: [PATCH 12/13] Revert "Remove unnecessary eslint-disable" This reverts commit 783831b3de59eddaff219226cbea45732c6e829d. --- backend/src/apis/KlicktippController.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/apis/KlicktippController.ts b/backend/src/apis/KlicktippController.ts index 56dd067e9..a291bb945 100644 --- a/backend/src/apis/KlicktippController.ts +++ b/backend/src/apis/KlicktippController.ts @@ -2,6 +2,8 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import KlicktippConnector from 'klicktipp-api' import CONFIG from '@/config' From eb38eaa6ced57b6126f2b2b794ac312beb4028b9 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 24 Mar 2023 01:06:45 +0100 Subject: [PATCH 13/13] Revert "Remove unnecessary eslint-disable" This reverts commit 783831b3de59eddaff219226cbea45732c6e829d. --- backend/src/apis/KlicktippController.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/apis/KlicktippController.ts b/backend/src/apis/KlicktippController.ts index 56dd067e9..a291bb945 100644 --- a/backend/src/apis/KlicktippController.ts +++ b/backend/src/apis/KlicktippController.ts @@ -2,6 +2,8 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import KlicktippConnector from 'klicktipp-api' import CONFIG from '@/config'