mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into refactor-receiver-in-send-coins
This commit is contained in:
commit
351c4a0e35
@ -11,11 +11,11 @@
|
|||||||
"build": "tsc --build && mkdir -p build/src/emails/templates/ && cp -r src/emails/templates/* build/src/emails/templates/ && mkdir -p build/src/locales/ && cp -r src/locales/*.json build/src/locales/",
|
"build": "tsc --build && mkdir -p build/src/emails/templates/ && cp -r src/emails/templates/* build/src/emails/templates/ && mkdir -p build/src/locales/ && cp -r src/locales/*.json build/src/locales/",
|
||||||
"clean": "tsc --build --clean",
|
"clean": "tsc --build --clean",
|
||||||
"start": "cross-env TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js",
|
"start": "cross-env TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js",
|
||||||
"dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r tsconfig-paths/register src/index.ts",
|
"dev": "cross-env TZ=UTC nodemon -w src --ext ts,pug,json,css --exec ts-node -r tsconfig-paths/register src/index.ts",
|
||||||
"lint": "eslint --max-warnings=0 .",
|
"lint": "eslint --max-warnings=0 .",
|
||||||
"test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --forceExit --detectOpenHandles",
|
"test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --forceExit --detectOpenHandles",
|
||||||
"seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts",
|
"seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts",
|
||||||
"klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/klicktipp.ts",
|
"klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/executeKlicktipp.ts",
|
||||||
"locales": "scripts/sort.sh"
|
"locales": "scripts/sort.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
|
||||||
import { CONFIG } from '@/config'
|
import { CONFIG } from '@/config'
|
||||||
|
import { backendLogger as logger } from '@/server/logger'
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-relative-parent-imports
|
// eslint-disable-next-line import/no-relative-parent-imports
|
||||||
import KlicktippConnector from 'klicktipp-api'
|
import KlicktippConnector from 'klicktipp-api'
|
||||||
@ -41,9 +41,12 @@ export const getKlickTippUser = async (email: string): Promise<any> => {
|
|||||||
if (!CONFIG.KLICKTIPP) return true
|
if (!CONFIG.KLICKTIPP) return true
|
||||||
const isLogin = await loginKlicktippUser()
|
const isLogin = await loginKlicktippUser()
|
||||||
if (isLogin) {
|
if (isLogin) {
|
||||||
const subscriberId = await klicktippConnector.subscriberSearch(email)
|
try {
|
||||||
const result = await klicktippConnector.subscriberGet(subscriberId)
|
return klicktippConnector.subscriberGet(await klicktippConnector.subscriberSearch(email))
|
||||||
return result
|
} catch (e) {
|
||||||
|
logger.error('Could not find subscriber', email)
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -62,8 +65,18 @@ export const addFieldsToSubscriber = async (
|
|||||||
if (!CONFIG.KLICKTIPP) return true
|
if (!CONFIG.KLICKTIPP) return true
|
||||||
const isLogin = await loginKlicktippUser()
|
const isLogin = await loginKlicktippUser()
|
||||||
if (isLogin) {
|
if (isLogin) {
|
||||||
const subscriberId = await klicktippConnector.subscriberSearch(email)
|
try {
|
||||||
return klicktippConnector.subscriberUpdate(subscriberId, fields, newemail, newsmsnumber)
|
logger.info('Updating of subscriber', email)
|
||||||
|
return klicktippConnector.subscriberUpdate(
|
||||||
|
await klicktippConnector.subscriberSearch(email),
|
||||||
|
fields,
|
||||||
|
newemail,
|
||||||
|
newsmsnumber,
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('Could not update subscriber', email, fields, e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
17
backend/src/graphql/resolver/util/eventList.ts
Normal file
17
backend/src/graphql/resolver/util/eventList.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { User } from '@entity/User'
|
||||||
|
import { UserContact } from '@entity/UserContact'
|
||||||
|
|
||||||
|
export const lastDateTimeEvents = async (
|
||||||
|
eventType: string,
|
||||||
|
): Promise<{ email: string; value: Date }[]> => {
|
||||||
|
return DbEvent.createQueryBuilder('event')
|
||||||
|
.select('MAX(event.created_at)', 'value')
|
||||||
|
.leftJoin(User, 'user', 'affected_user_id = user.id')
|
||||||
|
.leftJoin(UserContact, 'usercontact', 'user.id = usercontact.user_id')
|
||||||
|
.addSelect('usercontact.email', 'email')
|
||||||
|
.where('event.type = :eventType', { eventType })
|
||||||
|
.andWhere('usercontact.email IS NOT NULL')
|
||||||
|
.groupBy('event.affected_user_id')
|
||||||
|
.getRawMany()
|
||||||
|
}
|
||||||
@ -1,14 +1,14 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||||
/* eslint-disable @typescript-eslint/unbound-method */
|
/* eslint-disable @typescript-eslint/unbound-method */
|
||||||
import { Connection } from '@dbTools/typeorm'
|
import { Connection as DbConnection } from '@dbTools/typeorm'
|
||||||
import { ApolloServer } from 'apollo-server-express'
|
import { ApolloServer } from 'apollo-server-express'
|
||||||
import express, { Express, json, urlencoded } from 'express'
|
import express, { Express, json, urlencoded } from 'express'
|
||||||
import { Logger } from 'log4js'
|
import { Logger } from 'log4js'
|
||||||
|
|
||||||
import { CONFIG } from '@/config'
|
import { CONFIG } from '@/config'
|
||||||
import { schema } from '@/graphql/schema'
|
import { schema } from '@/graphql/schema'
|
||||||
import { connection } from '@/typeorm/connection'
|
import { Connection } from '@/typeorm/connection'
|
||||||
import { checkDBVersion } from '@/typeorm/DBVersion'
|
import { checkDBVersion } from '@/typeorm/DBVersion'
|
||||||
import { elopageWebhook } from '@/webhook/elopage'
|
import { elopageWebhook } from '@/webhook/elopage'
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ import { plugins } from './plugins'
|
|||||||
interface ServerDef {
|
interface ServerDef {
|
||||||
apollo: ApolloServer
|
apollo: ApolloServer
|
||||||
app: Express
|
app: Express
|
||||||
con: Connection
|
con: DbConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createServer = async (
|
export const createServer = async (
|
||||||
@ -37,7 +37,7 @@ export const createServer = async (
|
|||||||
logger.debug('createServer...')
|
logger.debug('createServer...')
|
||||||
|
|
||||||
// open mysql connection
|
// open mysql connection
|
||||||
const con = await connection()
|
const con = await Connection.getInstance()
|
||||||
if (!con?.isConnected) {
|
if (!con?.isConnected) {
|
||||||
logger.fatal(`Couldn't open connection to database!`)
|
logger.fatal(`Couldn't open connection to database!`)
|
||||||
throw new Error(`Fatal: Couldn't open connection to database`)
|
throw new Error(`Fatal: Couldn't open connection to database`)
|
||||||
|
|||||||
@ -1,13 +1,33 @@
|
|||||||
// TODO This is super weird - since the entities are defined in another project they have their own globals.
|
// TODO This is super weird - since the entities are defined in another project they have their own globals.
|
||||||
// We cannot use our connection here, but must use the external typeorm installation
|
// We cannot use our connection here, but must use the external typeorm installation
|
||||||
import { Connection, createConnection, FileLogger } from '@dbTools/typeorm'
|
import { Connection as DbConnection, createConnection, FileLogger } from '@dbTools/typeorm'
|
||||||
import { entities } from '@entity/index'
|
import { entities } from '@entity/index'
|
||||||
|
|
||||||
import { CONFIG } from '@/config'
|
import { CONFIG } from '@/config'
|
||||||
|
|
||||||
export const connection = async (): Promise<Connection | null> => {
|
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||||
|
export class Connection {
|
||||||
|
private static instance: DbConnection
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Singleton's constructor should always be private to prevent direct
|
||||||
|
* construction calls with the `new` operator.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The static method that controls the access to the singleton instance.
|
||||||
|
*
|
||||||
|
* This implementation let you subclass the Singleton class while keeping
|
||||||
|
* just one instance of each subclass around.
|
||||||
|
*/
|
||||||
|
public static async getInstance(): Promise<DbConnection | null> {
|
||||||
|
if (Connection.instance) {
|
||||||
|
return Connection.instance
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return createConnection({
|
Connection.instance = await createConnection({
|
||||||
name: 'default',
|
name: 'default',
|
||||||
type: 'mysql',
|
type: 'mysql',
|
||||||
host: CONFIG.DB_HOST,
|
host: CONFIG.DB_HOST,
|
||||||
@ -25,9 +45,11 @@ export const connection = async (): Promise<Connection | null> => {
|
|||||||
charset: 'utf8mb4_unicode_ci',
|
charset: 'utf8mb4_unicode_ci',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
return Connection.instance
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error)
|
console.log(error)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
backend/src/util/executeKlicktipp.ts
Normal file
16
backend/src/util/executeKlicktipp.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { Connection } from '@/typeorm/connection'
|
||||||
|
|
||||||
|
import { exportEventDataToKlickTipp } from './klicktipp'
|
||||||
|
|
||||||
|
async function executeKlicktipp(): Promise<boolean> {
|
||||||
|
const connection = await Connection.getInstance()
|
||||||
|
if (connection) {
|
||||||
|
await exportEventDataToKlickTipp()
|
||||||
|
await connection.close()
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void executeKlicktipp()
|
||||||
65
backend/src/util/klicktipp.test.ts
Normal file
65
backend/src/util/klicktipp.test.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
|
import { Connection } from '@dbTools/typeorm'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||||
|
|
||||||
|
import { testEnvironment, cleanDB, resetToken } from '@test/helpers'
|
||||||
|
|
||||||
|
import { addFieldsToSubscriber } from '@/apis/KlicktippController'
|
||||||
|
import { creations } from '@/seeds/creation'
|
||||||
|
import { creationFactory } from '@/seeds/factory/creation'
|
||||||
|
import { userFactory } from '@/seeds/factory/user'
|
||||||
|
import { login } from '@/seeds/graphql/mutations'
|
||||||
|
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||||
|
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||||
|
|
||||||
|
import { exportEventDataToKlickTipp } from './klicktipp'
|
||||||
|
|
||||||
|
jest.mock('@/apis/KlicktippController')
|
||||||
|
|
||||||
|
let mutate: ApolloServerTestClient['mutate'], con: Connection
|
||||||
|
let testEnv: {
|
||||||
|
mutate: ApolloServerTestClient['mutate']
|
||||||
|
query: ApolloServerTestClient['query']
|
||||||
|
con: Connection
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
testEnv = await testEnvironment()
|
||||||
|
mutate = testEnv.mutate
|
||||||
|
con = testEnv.con
|
||||||
|
await DbEvent.clear()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await cleanDB()
|
||||||
|
await con.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('klicktipp', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await userFactory(testEnv, bibiBloxberg)
|
||||||
|
await userFactory(testEnv, peterLustig)
|
||||||
|
const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de')
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
await creationFactory(testEnv, bibisCreation!)
|
||||||
|
await mutate({
|
||||||
|
mutation: login,
|
||||||
|
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
resetToken()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('exportEventDataToKlickTipp', () => {
|
||||||
|
it('calls the KlicktippController', async () => {
|
||||||
|
await exportEventDataToKlickTipp()
|
||||||
|
expect(addFieldsToSubscriber).toBeCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -1,14 +1,11 @@
|
|||||||
|
// eslint-disable @typescript-eslint/no-explicit-any
|
||||||
import { User } from '@entity/User'
|
import { User } from '@entity/User'
|
||||||
|
|
||||||
import { getKlickTippUser } from '@/apis/KlicktippController'
|
import { getKlickTippUser, addFieldsToSubscriber } from '@/apis/KlicktippController'
|
||||||
import { LogError } from '@/server/LogError'
|
import { EventType } from '@/event/EventType'
|
||||||
import { connection } from '@/typeorm/connection'
|
import { lastDateTimeEvents } from '@/graphql/resolver/util/eventList'
|
||||||
|
|
||||||
export async function retrieveNotRegisteredEmails(): Promise<string[]> {
|
export async function retrieveNotRegisteredEmails(): Promise<string[]> {
|
||||||
const con = await connection()
|
|
||||||
if (!con) {
|
|
||||||
throw new LogError('No connection to database')
|
|
||||||
}
|
|
||||||
const users = await User.find({ relations: ['emailContact'] })
|
const users = await User.find({ relations: ['emailContact'] })
|
||||||
const notRegisteredUser = []
|
const notRegisteredUser = []
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
@ -20,10 +17,39 @@ export async function retrieveNotRegisteredEmails(): Promise<string[]> {
|
|||||||
console.log(`${user.emailContact.email}`)
|
console.log(`${user.emailContact.email}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await con.close()
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('User die nicht bei KlickTipp vorhanden sind: ', notRegisteredUser)
|
console.log('User die nicht bei KlickTipp vorhanden sind: ', notRegisteredUser)
|
||||||
return notRegisteredUser
|
return notRegisteredUser
|
||||||
}
|
}
|
||||||
|
|
||||||
void retrieveNotRegisteredEmails()
|
async function klickTippSendFieldToUser(
|
||||||
|
events: { email: string; value: Date }[],
|
||||||
|
field: string,
|
||||||
|
): Promise<void> {
|
||||||
|
for (const event of events) {
|
||||||
|
const time = event.value.setSeconds(0)
|
||||||
|
await addFieldsToSubscriber(event.email, { [field]: Math.trunc(time / 1000) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function exportEventDataToKlickTipp(): Promise<boolean> {
|
||||||
|
const lastLoginEvents = await lastDateTimeEvents(EventType.USER_LOGIN)
|
||||||
|
await klickTippSendFieldToUser(lastLoginEvents, 'field186060')
|
||||||
|
|
||||||
|
const registeredEvents = await lastDateTimeEvents(EventType.USER_ACTIVATE_ACCOUNT)
|
||||||
|
await klickTippSendFieldToUser(registeredEvents, 'field186061')
|
||||||
|
|
||||||
|
const receiveTransactionEvents = await lastDateTimeEvents(EventType.TRANSACTION_RECEIVE)
|
||||||
|
await klickTippSendFieldToUser(receiveTransactionEvents, 'field185674')
|
||||||
|
|
||||||
|
const contributionCreateEvents = await lastDateTimeEvents(EventType.TRANSACTION_SEND)
|
||||||
|
await klickTippSendFieldToUser(contributionCreateEvents, 'field185673')
|
||||||
|
|
||||||
|
const linkRedeemedEvents = await lastDateTimeEvents(EventType.TRANSACTION_LINK_REDEEM)
|
||||||
|
await klickTippSendFieldToUser(linkRedeemedEvents, 'field185676')
|
||||||
|
|
||||||
|
const confirmContributionEvents = await lastDateTimeEvents(EventType.ADMIN_CONTRIBUTION_CONFIRM)
|
||||||
|
await klickTippSendFieldToUser(confirmContributionEvents, 'field185675')
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user