From 50aaf0ea8973f9181cb515243e73618ca6e2e49c Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 4 Apr 2024 16:06:59 +0200 Subject: [PATCH 1/8] implement humhub export user function --- backend/.env.dist | 5 + backend/.env.template | 5 + backend/package.json | 2 + backend/src/apis/humhub/HumHubClient.ts | 137 ++++++++++++++++ backend/src/apis/humhub/ImportUsers.ts | 150 ++++++++++++++++++ backend/src/apis/humhub/checkForChanges.ts | 33 ++++ backend/src/apis/humhub/convertLanguage.ts | 18 +++ backend/src/apis/humhub/model/Account.ts | 24 +++ backend/src/apis/humhub/model/GetUser.ts | 11 ++ backend/src/apis/humhub/model/Password.ts | 4 + backend/src/apis/humhub/model/PostUser.ts | 16 ++ .../src/apis/humhub/model/PostUserError.ts | 6 + backend/src/apis/humhub/model/Profile.ts | 21 +++ .../src/apis/humhub/model/UsersResponse.ts | 7 + backend/src/config/index.ts | 9 +- backend/src/typeorm/connection.ts | 1 + backend/yarn.lock | 61 ++++++- deployment/bare_metal/.env.dist | 5 + 18 files changed, 513 insertions(+), 2 deletions(-) create mode 100644 backend/src/apis/humhub/HumHubClient.ts create mode 100644 backend/src/apis/humhub/ImportUsers.ts create mode 100644 backend/src/apis/humhub/checkForChanges.ts create mode 100644 backend/src/apis/humhub/convertLanguage.ts create mode 100644 backend/src/apis/humhub/model/Account.ts create mode 100644 backend/src/apis/humhub/model/GetUser.ts create mode 100644 backend/src/apis/humhub/model/Password.ts create mode 100644 backend/src/apis/humhub/model/PostUser.ts create mode 100644 backend/src/apis/humhub/model/PostUserError.ts create mode 100644 backend/src/apis/humhub/model/Profile.ts create mode 100644 backend/src/apis/humhub/model/UsersResponse.ts diff --git a/backend/.env.dist b/backend/.env.dist index 4ec60d856..ffcc0882a 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -70,3 +70,8 @@ FEDERATION_XCOM_SENDCOINS_ENABLED=false # Coordinates of Illuminz test instance #GMS_URL=http://54.176.169.179:3071 GMS_URL=http://localhost:4044/ + +# HUMHUB +HUMHUB_ACTIVE=false +#HUMHUB_API_URL=https://community.gradido.net/ +#HUMHUB_JWT_KEY= \ No newline at end of file diff --git a/backend/.env.template b/backend/.env.template index 1cff23d5a..bb68d85f6 100644 --- a/backend/.env.template +++ b/backend/.env.template @@ -67,3 +67,8 @@ FEDERATION_XCOM_SENDCOINS_ENABLED=$FEDERATION_XCOM_SENDCOINS_ENABLED # GMS GMS_ACTIVE=$GMS_ACTIVE GMS_URL=$GMS_URL + +# HUMHUB +HUMHUB_ACTIVE=$HUMHUB_ACTIVE +HUMHUB_API_URL=$HUMHUB_API_URL +HUMHUB_JWT_KEY=$HUMHUB_JWT_KEY \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index 8f70ab11e..0f2d0ed20 100644 --- a/backend/package.json +++ b/backend/package.json @@ -18,6 +18,7 @@ "klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/executeKlicktipp.ts", "gmsusers": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/gmsUsers.ts", "gmsuserList": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/gmsUserList.ts", + "humhubUserExport": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/apis/humhub/ImportUsers.ts", "locales": "scripts/sort.sh" }, "dependencies": { @@ -47,6 +48,7 @@ "reflect-metadata": "^0.1.13", "sodium-native": "^3.3.0", "type-graphql": "^1.1.1", + "typed-rest-client": "^1.8.11", "uuid": "^8.3.2" }, "devDependencies": { diff --git a/backend/src/apis/humhub/HumHubClient.ts b/backend/src/apis/humhub/HumHubClient.ts new file mode 100644 index 000000000..cfaf17ab6 --- /dev/null +++ b/backend/src/apis/humhub/HumHubClient.ts @@ -0,0 +1,137 @@ +import { SignJWT } from 'jose' +import { IRequestOptions, RestClient } from 'typed-rest-client' + +import { CONFIG } from '@/config' +import { LogError } from '@/server/LogError' +import { backendLogger as logger } from '@/server/logger' + +import { GetUser } from './model/GetUser' +import { PostUser } from './model/PostUser' +import { PostUserError } from './model/PostUserError' +import { UsersResponse } from './model/UsersResponse' + +/** + * HumHubClient as singleton class + */ +export class HumHubClient { + // eslint-disable-next-line no-use-before-define + private static instance: HumHubClient + private restClient: RestClient + + // eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function + private constructor() { + this.restClient = new RestClient('gradido-backend', CONFIG.HUMHUB_API_URL) + logger.info('create rest client for', CONFIG.HUMHUB_API_URL) + } + + public static getInstance(): HumHubClient | undefined { + if (!CONFIG.HUMHUB_ACTIVE || !CONFIG.HUMHUB_API_URL) { + logger.info(`humhub are disabled via config...`) + return + } + if (!HumHubClient.instance) { + HumHubClient.instance = new HumHubClient() + } + return HumHubClient.instance + } + + protected async createRequestOptions( + queryParams?: Record, + ): Promise { + const requestOptions: IRequestOptions = { + additionalHeaders: { authorization: 'Bearer ' + (await this.createJWTToken()) }, + } + if (queryParams) { + requestOptions.queryParameters = { params: queryParams } + } + return requestOptions + } + + private async createJWTToken(): Promise { + const secret = new TextEncoder().encode(CONFIG.HUMHUB_JWT_KEY) + const token = await new SignJWT({ 'urn:gradido:claim': true, uid: 1 }) + .setProtectedHeader({ alg: 'HS512' }) + .setIssuedAt() + .setIssuer('urn:gradido:issuer') + .setAudience('urn:gradido:audience') + .setExpirationTime('5m') + .sign(secret) + return token + } + + /** + * Get all users from humhub + * https://marketplace.humhub.com/module/rest/docs/html/user.html#tag/User/paths/~1user/get + * @param page The number of page of the result set >= 0 + * @param limit The numbers of items to return per page, Default: 20, [1 .. 50] + * @returns list of users + */ + public async users(page = 0, limit = 20): Promise { + const options = await this.createRequestOptions({ page, limit }) + const response = await this.restClient.get('/api/v1/user', options) + if (response.statusCode !== 200) { + throw new LogError('error requesting users from humhub', response) + } + return response.result + } + + /** + * get user by email + * https://marketplace.humhub.com/module/rest/docs/html/user.html#tag/User/paths/~1user~1get-by-email/get + * @param email for user search + * @returns user object if found + */ + public async userByEmail(email: string): Promise { + const options = await this.createRequestOptions({ email }) + const response = await this.restClient.get('/api/v1/user/get-by-email', options) + // 404 = user not found + if (response.statusCode === 404) { + return null + } + return response.result + } + + /** + * create user + * https://marketplace.humhub.com/module/rest/docs/html/user.html#tag/User/paths/~1user/post + * @param user for saving on humhub instance + */ + public async createUser(user: PostUser): Promise { + const options = await this.createRequestOptions() + try { + const response = await this.restClient.create('/api/v1/user', user, options) + if (response.statusCode !== 200) { + throw new LogError('error creating user on humhub', { user, response }) + } + } catch (error) { + throw new LogError('error on creating new user', { + user, + error: JSON.stringify(error, null, 2), + }) + } + } + + /** + * update user + * https://marketplace.humhub.com/module/rest/docs/html/user.html#tag/User/operation/updateUser + * @param user user object to update + * @param humhubUserId humhub user id + * @returns updated user object on success + */ + public async updateUser(user: PostUser, humhubUserId: number): Promise { + const options = await this.createRequestOptions() + const response = await this.restClient.update( + `/api/v1/user/${humhubUserId}`, + user, + options, + ) + if (response.statusCode === 400) { + throw new LogError('Invalid user supplied', { user, response }) + } else if (response.statusCode === 404) { + throw new LogError('User not found', { user, response }) + } + return response.result + } +} + +// new RestClient('gradido', 'api/v1/') diff --git a/backend/src/apis/humhub/ImportUsers.ts b/backend/src/apis/humhub/ImportUsers.ts new file mode 100644 index 000000000..74dfe012a --- /dev/null +++ b/backend/src/apis/humhub/ImportUsers.ts @@ -0,0 +1,150 @@ +import { IsNull, Not } from '@dbTools/typeorm' +import { User } from '@entity/User' + +import { CONFIG } from '@/config' +import { LogError } from '@/server/LogError' +import { backendLogger as logger } from '@/server/logger' +import { Connection } from '@/typeorm/connection' +import { checkDBVersion } from '@/typeorm/DBVersion' + +import { checkForChanges } from './checkForChanges' +import { HumHubClient } from './HumHubClient' +import { GetUser } from './model/GetUser' +import { PostUser } from './model/PostUser' + +const USER_BULK_SIZE = 20 + +enum ExecutedHumhubAction { + UPDATE, + CREATE, + SKIP, +} + +function getUsersPage(page: number, limit: number): Promise<[User[], number]> { + return User.findAndCount({ + relations: { emailContact: true }, + skip: page * limit, + take: limit, + where: { emailContact: { email: Not(IsNull()) } }, + }) +} + +async function createOrUpdateOrSkipUser( + user: User, + humHubClient: HumHubClient, + humhubUsers: Map, +): Promise { + const postUser = new PostUser(user) + const humhubUser = humhubUsers.get(user.emailContact.email.trim()) + if (humhubUser) { + if (checkForChanges(humhubUser, user)) { + return ExecutedHumhubAction.SKIP + } + await humHubClient.updateUser(postUser, humhubUser.id) + return ExecutedHumhubAction.UPDATE + } else { + await humHubClient.createUser(postUser) + return ExecutedHumhubAction.CREATE + } +} + +/** + * @param client + * @returns user map indiced with email + */ +async function loadUsersFromHumHub(client: HumHubClient): Promise> { + const start = new Date().getTime() + const humhubUsers = new Map() + const firstPage = await client.users(0, 50) + if (!firstPage) { + throw new LogError('not a single user found on humhub, please check config and setup') + } + firstPage.results.forEach((user) => { + humhubUsers.set(user.account.email.trim(), user) + }) + let page = 1 + while (humhubUsers.size < firstPage.total) { + const usersPage = await client.users(page, 50) + if (!usersPage) { + throw new LogError('error requesting next users page from humhub') + } + usersPage.results.forEach((user) => { + humhubUsers.set(user.account.email.trim(), user) + }) + page++ + } + const elapsed = new Date().getTime() - start + logger.info('load users from humhub', { + total: humhubUsers.size, + timeSeconds: elapsed / 1000.0, + }) + return humhubUsers +} + +async function main() { + const start = new Date().getTime() + + // open mysql connection + const con = await Connection.getInstance() + if (!con?.isConnected) { + logger.fatal(`Couldn't open connection to database!`) + throw new Error(`Fatal: Couldn't open connection to database`) + } + + // check for correct database version + const dbVersion = await checkDBVersion(CONFIG.DB_VERSION) + if (!dbVersion) { + logger.fatal('Fatal: Database Version incorrect') + throw new Error('Fatal: Database Version incorrect') + } + + let userCount = 0 + let page = 0 + const humHubClient = HumHubClient.getInstance() + if (!humHubClient) { + throw new LogError('error creating humhub client') + } + const humhubUsers = await loadUsersFromHumHub(humHubClient) + + let dbUserCount = 0 + let updatedUserCount = 0 + let createdUserCount = 0 + let skippedUserCount = 0 + + do { + const [users, totalUsers] = await getUsersPage(page, USER_BULK_SIZE) + dbUserCount += users.length + userCount = users.length + page++ + const promises: Promise[] = [] + users.forEach((user: User) => + promises.push(createOrUpdateOrSkipUser(user, humHubClient, humhubUsers)), + ) + const executedActions = await Promise.all(promises) + executedActions.forEach((executedAction: ExecutedHumhubAction) => { + if (executedAction === ExecutedHumhubAction.CREATE) createdUserCount++ + else if (executedAction === ExecutedHumhubAction.UPDATE) updatedUserCount++ + else if (executedAction === ExecutedHumhubAction.SKIP) skippedUserCount++ + }) + // using process.stdout.write here so that carriage-return is working analog to c + // printf("\rchecked user: %d/%d", dbUserCount, totalUsers); + process.stdout.write(`checked user: ${dbUserCount}/${totalUsers}\r`) + } while (userCount === USER_BULK_SIZE) + + await con.destroy() + const elapsed = new Date().getTime() - start + logger.info('export user to humhub, statistics:', { + timeSeconds: elapsed / 1000.0, + gradidoUserCount: dbUserCount, + updatedUserCount, + createdUserCount, + skippedUserCount, + }) +} + +main().catch((e) => { + // eslint-disable-next-line no-console + console.error(e) + // eslint-disable-next-line n/no-process-exit + process.exit(1) +}) diff --git a/backend/src/apis/humhub/checkForChanges.ts b/backend/src/apis/humhub/checkForChanges.ts new file mode 100644 index 000000000..3889d090f --- /dev/null +++ b/backend/src/apis/humhub/checkForChanges.ts @@ -0,0 +1,33 @@ +import { User } from '@entity/User' + +import { Account } from './model/Account' +import { GetUser } from './model/GetUser' +import { Profile } from './model/Profile' + +function profileIsTheSame(profile: Profile, user: User): boolean { + const gradidoUserProfile = new Profile(user) + if (profile.firstname !== gradidoUserProfile.firstname) return false + if (profile.lastname !== gradidoUserProfile.lastname) return false + if (profile.gradido_address !== gradidoUserProfile.gradido_address) return false + return true +} + +function accountIsTheSame(account: Account, user: User): boolean { + const gradidoUserAccount = new Account(user) + if (account.username !== gradidoUserAccount.username) return false + if (account.email !== gradidoUserAccount.email) return false + if (account.language !== gradidoUserAccount.language) return false + return true +} + +/** + * compare if gradido user (db entity) differ from humhub user + * @param humhubUser + * @param gradidoUse + */ +export function checkForChanges(humhubUser: GetUser, gradidoUser: User): boolean { + return ( + profileIsTheSame(humhubUser.profile, gradidoUser) && + accountIsTheSame(humhubUser.account, gradidoUser) + ) +} diff --git a/backend/src/apis/humhub/convertLanguage.ts b/backend/src/apis/humhub/convertLanguage.ts new file mode 100644 index 000000000..9555b0a52 --- /dev/null +++ b/backend/src/apis/humhub/convertLanguage.ts @@ -0,0 +1,18 @@ +/** + * convert gradido language in valid humhub language + * humhub doesn't know en for example, only en-US and en-GB + * @param gradidoLanguage + */ +export function convertGradidoLanguageToHumhub(gradidoLanguage: string): string { + if (gradidoLanguage === 'en') { + return 'en-US' + } + return gradidoLanguage +} + +export function convertHumhubLanguageToGradido(humhubLanguage: string): string { + if (humhubLanguage === 'en-US') { + return 'en' + } + return humhubLanguage +} diff --git a/backend/src/apis/humhub/model/Account.ts b/backend/src/apis/humhub/model/Account.ts new file mode 100644 index 000000000..7b5e794bb --- /dev/null +++ b/backend/src/apis/humhub/model/Account.ts @@ -0,0 +1,24 @@ +import { User } from '@entity/User' + +import { convertGradidoLanguageToHumhub } from '@/apis/humhub/convertLanguage' + +export class Account { + public constructor(user: User) { + if (user.alias && user.alias.length > 2) { + this.username = user.alias + } else { + // Use the replace method to remove the part after '+' and before '@' + // source: https://people.cs.rutgers.edu/~watrous/plus-signs-in-email-addresses.html + // email address with + exist but humhub doesn't allow username with + + this.username = user.emailContact.email.replace(/\+(.*)@/, '@') + } + + this.email = user.emailContact.email + this.language = convertGradidoLanguageToHumhub(user.language) + } + + username: string + email: string + tags: string[] + language: string +} diff --git a/backend/src/apis/humhub/model/GetUser.ts b/backend/src/apis/humhub/model/GetUser.ts new file mode 100644 index 000000000..16231c224 --- /dev/null +++ b/backend/src/apis/humhub/model/GetUser.ts @@ -0,0 +1,11 @@ +import { Account } from './Account' +import { Profile } from './Profile' + +export class GetUser { + id: number + guid: string + // eslint-disable-next-line camelcase + display_name: string + account: Account + profile: Profile +} diff --git a/backend/src/apis/humhub/model/Password.ts b/backend/src/apis/humhub/model/Password.ts new file mode 100644 index 000000000..04cede68b --- /dev/null +++ b/backend/src/apis/humhub/model/Password.ts @@ -0,0 +1,4 @@ +export class Password { + newPassword: string + mustChangePassword: boolean +} diff --git a/backend/src/apis/humhub/model/PostUser.ts b/backend/src/apis/humhub/model/PostUser.ts new file mode 100644 index 000000000..a164440fd --- /dev/null +++ b/backend/src/apis/humhub/model/PostUser.ts @@ -0,0 +1,16 @@ +import { User } from '@entity/User' + +import { Account } from './Account' +import { Password } from './Password' +import { Profile } from './Profile' + +export class PostUser { + public constructor(user: User) { + this.account = new Account(user) + this.profile = new Profile(user) + } + + account: Account + profile: Profile + password: Password +} diff --git a/backend/src/apis/humhub/model/PostUserError.ts b/backend/src/apis/humhub/model/PostUserError.ts new file mode 100644 index 000000000..0e6b0e152 --- /dev/null +++ b/backend/src/apis/humhub/model/PostUserError.ts @@ -0,0 +1,6 @@ +export class PostUserError { + code: number + message: string + profile: string[] + account: string[] +} diff --git a/backend/src/apis/humhub/model/Profile.ts b/backend/src/apis/humhub/model/Profile.ts new file mode 100644 index 000000000..eadf6cf65 --- /dev/null +++ b/backend/src/apis/humhub/model/Profile.ts @@ -0,0 +1,21 @@ +/* eslint-disable camelcase */ +import { User } from '@entity/User' + +import { CONFIG } from '@/config' + +export class Profile { + public constructor(user: User) { + this.firstname = user.firstName + this.lastname = user.lastName + if (user.alias && user.alias.length > 2) { + this.gradido_address = CONFIG.COMMUNITY_NAME + '/' + user.alias + } else { + this.gradido_address = CONFIG.COMMUNITY_NAME + '/' + user.gradidoID + } + } + + firstname: string + lastname: string + gradido_address: string + about: string +} diff --git a/backend/src/apis/humhub/model/UsersResponse.ts b/backend/src/apis/humhub/model/UsersResponse.ts new file mode 100644 index 000000000..22379739c --- /dev/null +++ b/backend/src/apis/humhub/model/UsersResponse.ts @@ -0,0 +1,7 @@ +import { GetUser } from './GetUser' + +export class UsersResponse { + total: number + page: number + results: GetUser[] +} diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 0dbc0ea5a..7bb2bd695 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -19,7 +19,7 @@ const constants = { LOG_LEVEL: process.env.LOG_LEVEL ?? 'info', CONFIG_VERSION: { DEFAULT: 'DEFAULT', - EXPECTED: 'v21.2024-01-06', + EXPECTED: 'v22.2024-03-06', CURRENT: '', }, } @@ -147,6 +147,12 @@ const gms = { GMS_URL: process.env.GMS_HOST ?? 'http://localhost:4044/', } +const humhub = { + HUMHUB_ACTIVE: process.env.HUMHUB_ACTIVE === 'true' || false, + HUMHUB_API_URL: process.env.HUMHUB_API_URL ?? COMMUNITY_URL + '/community/', + HUMHUB_JWT_KEY: process.env.HUMHUB_JWT_KEY ?? '', +} + export const CONFIG = { ...constants, ...server, @@ -159,4 +165,5 @@ export const CONFIG = { ...webhook, ...federation, ...gms, + ...humhub, } diff --git a/backend/src/typeorm/connection.ts b/backend/src/typeorm/connection.ts index 104f6449d..70df8cae5 100644 --- a/backend/src/typeorm/connection.ts +++ b/backend/src/typeorm/connection.ts @@ -50,6 +50,7 @@ export class Connection { } catch (error) { // eslint-disable-next-line no-console console.log(error) + console.log('catch from createConnection') return null } } diff --git a/backend/yarn.lock b/backend/yarn.lock index 91186187b..d8e25a0f7 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1188,6 +1188,13 @@ "@types/mime" "^1" "@types/node" "*" +"@types/simple-get@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/simple-get/-/simple-get-4.0.3.tgz#91f8f03fd4e5d3720a1a6114c6a1bb5761326092" + integrity sha512-X3HNxcz8ZjpTB+eLf3dH0m/KVt6mln3XKKAeBLca3rMtjFeB67hy3SDAkfWj0ulb++nqSp8nmCfMLAq9FpBW9g== + dependencies: + "@types/node" "*" + "@types/sodium-native@^2.3.5": version "2.3.5" resolved "https://registry.yarnpkg.com/@types/sodium-native/-/sodium-native-2.3.5.tgz#5d2681e7b6b67bcbdc63cfb133e303ec9e942e43" @@ -2513,6 +2520,13 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -3696,7 +3710,7 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== "gradido-database@file:../database": - version "2.1.1" + version "2.2.0" dependencies: "@types/uuid" "^8.3.4" cross-env "^7.0.3" @@ -5268,6 +5282,11 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -6070,6 +6089,13 @@ qs@^6.11.0: dependencies: side-channel "^1.0.4" +qs@^6.9.1: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -6449,6 +6475,20 @@ signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -6916,6 +6956,11 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tunnel@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -6981,6 +7026,15 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" +typed-rest-client@^1.8.11: + version "1.8.11" + resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-1.8.11.tgz#6906f02e3c91e8d851579f255abf0fd60800a04d" + integrity sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA== + dependencies: + qs "^6.9.1" + tunnel "0.0.6" + underscore "^1.12.1" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -7051,6 +7105,11 @@ underscore.deep@~0.5.1: resolved "https://registry.yarnpkg.com/underscore.deep/-/underscore.deep-0.5.3.tgz#210969d58025339cecabd2a2ad8c3e8925e5c095" integrity sha512-4OuSOlFNkiVFVc3khkeG112Pdu1gbitMj7t9B9ENb61uFmN70Jq7Iluhi3oflcSgexkKfDdJ5XAJET2gEq6ikA== +underscore@^1.12.1: + version "1.13.6" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" + integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== + underscore@~1.13.1: version "1.13.4" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.4.tgz#7886b46bbdf07f768e0052f1828e1dcab40c0dee" diff --git a/deployment/bare_metal/.env.dist b/deployment/bare_metal/.env.dist index 9e6e911eb..8b1c8f510 100644 --- a/deployment/bare_metal/.env.dist +++ b/deployment/bare_metal/.env.dist @@ -124,3 +124,8 @@ WEBHOOK_ELOPAGE_SECRET=secret # Coordinates of Illuminz test instance #GMS_URL=http://54.176.169.179:3071 #GMS_URL=http://localhost:4044/ + +# HUMHUB +HUMHUB_ACTIVE=false +#HUMHUB_API_URL=https://community.gradido.net +#HUMHUB_JWT_KEY= \ No newline at end of file From 9d6386decbb7a1a2b92c0d76e2895ad29ba2ca09 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 4 Apr 2024 16:15:34 +0200 Subject: [PATCH 2/8] lost update --- backend/src/config/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index b71e59488..a73a3be70 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -19,11 +19,7 @@ const constants = { LOG_LEVEL: process.env.LOG_LEVEL ?? 'info', CONFIG_VERSION: { DEFAULT: 'DEFAULT', -<<<<<<< HEAD - EXPECTED: 'v22.2024-03-06', -======= EXPECTED: 'v22.2024-03-14', ->>>>>>> master CURRENT: '', }, } From 37e91f86fd5d1d85be016b544b447bea65ce81fe Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 15 Apr 2024 12:48:12 +0200 Subject: [PATCH 3/8] change default alias, add logic class for publishName enum --- backend/src/apis/humhub/model/Account.ts | 5 +- backend/src/data/PublishName.logic.ts | 67 ++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 backend/src/data/PublishName.logic.ts diff --git a/backend/src/apis/humhub/model/Account.ts b/backend/src/apis/humhub/model/Account.ts index 7b5e794bb..52777e8c8 100644 --- a/backend/src/apis/humhub/model/Account.ts +++ b/backend/src/apis/humhub/model/Account.ts @@ -7,10 +7,7 @@ export class Account { if (user.alias && user.alias.length > 2) { this.username = user.alias } else { - // Use the replace method to remove the part after '+' and before '@' - // source: https://people.cs.rutgers.edu/~watrous/plus-signs-in-email-addresses.html - // email address with + exist but humhub doesn't allow username with + - this.username = user.emailContact.email.replace(/\+(.*)@/, '@') + this.username = user.gradidoID } this.email = user.emailContact.email diff --git a/backend/src/data/PublishName.logic.ts b/backend/src/data/PublishName.logic.ts new file mode 100644 index 000000000..e68a29f86 --- /dev/null +++ b/backend/src/data/PublishName.logic.ts @@ -0,0 +1,67 @@ +import { User } from '@entity/User' + +import { GmsPublishNameType } from '@/graphql/enum/GmsPublishNameType'; + +export class PublishNameLogic { + constructor(private user: User) {} + + /** + * get first name based on publishNameType: GmsPublishNameType value + * @param publishNameType + * @returns user.firstName for GMS_PUBLISH_NAME_FIRST, GMS_PUBLISH_NAME_FIRST_INITIAL or GMS_PUBLISH_NAME_FULL + * first initial from user.firstName for GMS_PUBLISH_NAME_INITIALS or GMS_PUBLISH_NAME_ALIAS_OR_INITALS and empty alias + */ + public getFirstName(publishNameType: GmsPublishNameType): string | undefined { + if ( + [ + GmsPublishNameType.GMS_PUBLISH_NAME_FIRST, + GmsPublishNameType.GMS_PUBLISH_NAME_FIRST_INITIAL, + GmsPublishNameType.GMS_PUBLISH_NAME_FULL, + ].includes(publishNameType) + ) { + return this.user.firstName + } + if ( + (!this.user.alias && + publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS) || + publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_INITIALS + ) { + return this.user.firstName.substring(0, 1) + } + } + + /** + * get last name based on publishNameType: GmsPublishNameType value + * @param publishNameType + * @returns user.lastName for GMS_PUBLISH_NAME_FULL + * first initial from user.lastName for GMS_PUBLISH_NAME_FIRST_INITIAL, GMS_PUBLISH_NAME_INITIALS or GMS_PUBLISH_NAME_ALIAS_OR_INITALS and empty alias + */ + public getLastName(publishNameType: GmsPublishNameType): string | undefined { + if (publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_FULL) { + return this.user.lastName + } + if ( + (!this.user.alias && + publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS) || + publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_FIRST_INITIAL || + publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_INITIALS + ) { + return this.user.lastName.substring(0, 1) + } + } + + public getUsername(publishNameType: GmsPublishNameType): string { + if ( + this.user.alias && + publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS + ) { + return this.user.alias + } + const firstName = this.getFirstName(publishNameType) + const lastName = this.getLastName(publishNameType) + if (firstName && lastName) { + return `${firstName} ${lastName}` + } + return this.user.gradidoID + } +} From b0d2a1566eac4ce4072cd10cbd022afc319b061d Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 16 Apr 2024 16:21:28 +0200 Subject: [PATCH 4/8] better names, check humhubAllowed flag --- backend/src/apis/humhub/HumHubClient.ts | 12 ++++ backend/src/apis/humhub/ImportUsers.ts | 57 ++++++++++++------- ...rChanges.ts => compareHumhubUserDbUser.ts} | 3 +- backend/src/data/PublishName.logic.ts | 52 +++++++---------- backend/yarn.lock | 35 +----------- 5 files changed, 74 insertions(+), 85 deletions(-) rename backend/src/apis/humhub/{checkForChanges.ts => compareHumhubUserDbUser.ts} (89%) diff --git a/backend/src/apis/humhub/HumHubClient.ts b/backend/src/apis/humhub/HumHubClient.ts index cfaf17ab6..813946774 100644 --- a/backend/src/apis/humhub/HumHubClient.ts +++ b/backend/src/apis/humhub/HumHubClient.ts @@ -132,6 +132,18 @@ export class HumHubClient { } return response.result } + + public async deleteUser(humhubUserId: number): Promise { + const options = await this.createRequestOptions() + const response = await this.restClient.del(`/api/v1/user/${humhubUserId}`, options) + if (response.statusCode === 400) { + throw new LogError('invalid user supplied', { userId: humhubUserId, response }) + } else if (response.statusCode === 404) { + throw new LogError('User not found', { userId: humhubUserId, response }) + } else if (response.statusCode !== 200) { + throw new LogError('error deleting user', { userId: humhubUserId, response }) + } + } } // new RestClient('gradido', 'api/v1/') diff --git a/backend/src/apis/humhub/ImportUsers.ts b/backend/src/apis/humhub/ImportUsers.ts index 74dfe012a..c80db4344 100644 --- a/backend/src/apis/humhub/ImportUsers.ts +++ b/backend/src/apis/humhub/ImportUsers.ts @@ -7,7 +7,7 @@ import { backendLogger as logger } from '@/server/logger' import { Connection } from '@/typeorm/connection' import { checkDBVersion } from '@/typeorm/DBVersion' -import { checkForChanges } from './checkForChanges' +import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser' import { HumHubClient } from './HumHubClient' import { GetUser } from './model/GetUser' import { PostUser } from './model/PostUser' @@ -18,6 +18,7 @@ enum ExecutedHumhubAction { UPDATE, CREATE, SKIP, + DELETE, } function getUsersPage(page: number, limit: number): Promise<[User[], number]> { @@ -29,23 +30,44 @@ function getUsersPage(page: number, limit: number): Promise<[User[], number]> { }) } -async function createOrUpdateOrSkipUser( +/** + * Trigger action according to conditions + * | User exist on humhub | export to humhub allowed | changes in user data | ACTION + * | true | false | ignored | DELETE + * | true | true | true | UPDATE + * | true | true | false | SKIP + * | false | false | ignored | SKIP + * | false | true | ignored | CREATE + * @param user + * @param humHubClient + * @param humhubUsers + * @returns + */ +async function syncUser( user: User, humHubClient: HumHubClient, humhubUsers: Map, ): Promise { const postUser = new PostUser(user) const humhubUser = humhubUsers.get(user.emailContact.email.trim()) + if (humhubUser) { - if (checkForChanges(humhubUser, user)) { - return ExecutedHumhubAction.SKIP + if (!user.humhubAllowed) { + await humHubClient.deleteUser(humhubUser.id) + return ExecutedHumhubAction.DELETE + } + if (!isHumhubUserIdenticalToDbUser(humhubUser, user)) { + // if humhub allowed + await humHubClient.updateUser(postUser, humhubUser.id) + return ExecutedHumhubAction.UPDATE } - await humHubClient.updateUser(postUser, humhubUser.id) - return ExecutedHumhubAction.UPDATE } else { - await humHubClient.createUser(postUser) - return ExecutedHumhubAction.CREATE + if (user.humhubAllowed) { + await humHubClient.createUser(postUser) + return ExecutedHumhubAction.CREATE + } } + return ExecutedHumhubAction.SKIP } /** @@ -107,9 +129,7 @@ async function main() { const humhubUsers = await loadUsersFromHumHub(humHubClient) let dbUserCount = 0 - let updatedUserCount = 0 - let createdUserCount = 0 - let skippedUserCount = 0 + const executedHumhubActionsCount = [0, 0, 0, 0] do { const [users, totalUsers] = await getUsersPage(page, USER_BULK_SIZE) @@ -117,14 +137,10 @@ async function main() { userCount = users.length page++ const promises: Promise[] = [] - users.forEach((user: User) => - promises.push(createOrUpdateOrSkipUser(user, humHubClient, humhubUsers)), - ) + users.forEach((user: User) => promises.push(syncUser(user, humHubClient, humhubUsers))) const executedActions = await Promise.all(promises) executedActions.forEach((executedAction: ExecutedHumhubAction) => { - if (executedAction === ExecutedHumhubAction.CREATE) createdUserCount++ - else if (executedAction === ExecutedHumhubAction.UPDATE) updatedUserCount++ - else if (executedAction === ExecutedHumhubAction.SKIP) skippedUserCount++ + executedHumhubActionsCount[executedAction as number]++ }) // using process.stdout.write here so that carriage-return is working analog to c // printf("\rchecked user: %d/%d", dbUserCount, totalUsers); @@ -136,9 +152,10 @@ async function main() { logger.info('export user to humhub, statistics:', { timeSeconds: elapsed / 1000.0, gradidoUserCount: dbUserCount, - updatedUserCount, - createdUserCount, - skippedUserCount, + createdCount: executedHumhubActionsCount[ExecutedHumhubAction.CREATE], + updatedCount: executedHumhubActionsCount[ExecutedHumhubAction.UPDATE], + skippedCount: executedHumhubActionsCount[ExecutedHumhubAction.SKIP], + deletedCount: executedHumhubActionsCount[ExecutedHumhubAction.DELETE], }) } diff --git a/backend/src/apis/humhub/checkForChanges.ts b/backend/src/apis/humhub/compareHumhubUserDbUser.ts similarity index 89% rename from backend/src/apis/humhub/checkForChanges.ts rename to backend/src/apis/humhub/compareHumhubUserDbUser.ts index 3889d090f..95192be89 100644 --- a/backend/src/apis/humhub/checkForChanges.ts +++ b/backend/src/apis/humhub/compareHumhubUserDbUser.ts @@ -24,8 +24,9 @@ function accountIsTheSame(account: Account, user: User): boolean { * compare if gradido user (db entity) differ from humhub user * @param humhubUser * @param gradidoUse + * @return true if no differences */ -export function checkForChanges(humhubUser: GetUser, gradidoUser: User): boolean { +export function isHumhubUserIdenticalToDbUser(humhubUser: GetUser, gradidoUser: User): boolean { return ( profileIsTheSame(humhubUser.profile, gradidoUser) && accountIsTheSame(humhubUser.account, gradidoUser) diff --git a/backend/src/data/PublishName.logic.ts b/backend/src/data/PublishName.logic.ts index e68a29f86..22262f6ca 100644 --- a/backend/src/data/PublishName.logic.ts +++ b/backend/src/data/PublishName.logic.ts @@ -1,30 +1,31 @@ import { User } from '@entity/User' -import { GmsPublishNameType } from '@/graphql/enum/GmsPublishNameType'; +import { PublishNameType } from '@/graphql/enum/PublishNameType' export class PublishNameLogic { constructor(private user: User) {} /** - * get first name based on publishNameType: GmsPublishNameType value + * get first name based on publishNameType: PublishNameType value * @param publishNameType * @returns user.firstName for GMS_PUBLISH_NAME_FIRST, GMS_PUBLISH_NAME_FIRST_INITIAL or GMS_PUBLISH_NAME_FULL * first initial from user.firstName for GMS_PUBLISH_NAME_INITIALS or GMS_PUBLISH_NAME_ALIAS_OR_INITALS and empty alias */ - public getFirstName(publishNameType: GmsPublishNameType): string | undefined { + public getFirstName(publishNameType: PublishNameType): string | undefined { if ( [ - GmsPublishNameType.GMS_PUBLISH_NAME_FIRST, - GmsPublishNameType.GMS_PUBLISH_NAME_FIRST_INITIAL, - GmsPublishNameType.GMS_PUBLISH_NAME_FULL, + PublishNameType.PUBLISH_NAME_FIRST, + PublishNameType.PUBLISH_NAME_FIRST_INITIAL, + PublishNameType.PUBLISH_NAME_FULL, ].includes(publishNameType) ) { return this.user.firstName } if ( - (!this.user.alias && - publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS) || - publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_INITIALS + [ + PublishNameType.PUBLISH_NAME_INITIALS, + PublishNameType.PUBLISH_NAME_INITIAL_LAST, + ].includes(publishNameType) ) { return this.user.firstName.substring(0, 1) } @@ -36,32 +37,23 @@ export class PublishNameLogic { * @returns user.lastName for GMS_PUBLISH_NAME_FULL * first initial from user.lastName for GMS_PUBLISH_NAME_FIRST_INITIAL, GMS_PUBLISH_NAME_INITIALS or GMS_PUBLISH_NAME_ALIAS_OR_INITALS and empty alias */ - public getLastName(publishNameType: GmsPublishNameType): string | undefined { - if (publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_FULL) { + public getLastName(publishNameType: PublishNameType): string | undefined { + if ( + [ + PublishNameType.PUBLISH_NAME_LAST, + PublishNameType.PUBLISH_NAME_INITIAL_LAST, + PublishNameType.PUBLISH_NAME_FULL, + ].includes(publishNameType) + ) { return this.user.lastName } if ( - (!this.user.alias && - publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS) || - publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_FIRST_INITIAL || - publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_INITIALS + [ + PublishNameType.PUBLISH_NAME_FIRST_INITIAL, + PublishNameType.PUBLISH_NAME_INITIALS, + ].includes(publishNameType) ) { return this.user.lastName.substring(0, 1) } } - - public getUsername(publishNameType: GmsPublishNameType): string { - if ( - this.user.alias && - publishNameType === GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS - ) { - return this.user.alias - } - const firstName = this.getFirstName(publishNameType) - const lastName = this.getLastName(publishNameType) - if (firstName && lastName) { - return `${firstName} ${lastName}` - } - return this.user.gradidoID - } } diff --git a/backend/yarn.lock b/backend/yarn.lock index d8e25a0f7..26f861772 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1188,13 +1188,6 @@ "@types/mime" "^1" "@types/node" "*" -"@types/simple-get@^4.0.3": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/simple-get/-/simple-get-4.0.3.tgz#91f8f03fd4e5d3720a1a6114c6a1bb5761326092" - integrity sha512-X3HNxcz8ZjpTB+eLf3dH0m/KVt6mln3XKKAeBLca3rMtjFeB67hy3SDAkfWj0ulb++nqSp8nmCfMLAq9FpBW9g== - dependencies: - "@types/node" "*" - "@types/sodium-native@^2.3.5": version "2.3.5" resolved "https://registry.yarnpkg.com/@types/sodium-native/-/sodium-native-2.3.5.tgz#5d2681e7b6b67bcbdc63cfb133e303ec9e942e43" @@ -2520,13 +2513,6 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" -decompress-response@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" - integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== - dependencies: - mimic-response "^3.1.0" - dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -3710,7 +3696,7 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== "gradido-database@file:../database": - version "2.2.0" + version "2.2.1" dependencies: "@types/uuid" "^8.3.4" cross-env "^7.0.3" @@ -5282,11 +5268,6 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -mimic-response@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" - integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== - minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -6475,20 +6456,6 @@ signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== -simple-concat@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" - integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== - -simple-get@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" - integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== - dependencies: - decompress-response "^6.0.0" - once "^1.3.1" - simple-concat "^1.0.0" - sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" From 69510e7a736e18904b8d5a5eb14d2e17760d6993 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 19 Apr 2024 07:09:44 +0200 Subject: [PATCH 5/8] work on tests --- backend/src/apis/humhub/HumHubClient.ts | 1 - backend/src/apis/humhub/ImportUsers.ts | 50 +---------------------- backend/src/apis/humhub/syncUser.test.ts | 34 ++++++++++++++++ backend/src/apis/humhub/syncUser.ts | 52 ++++++++++++++++++++++++ backend/src/data/PublishName.logic.ts | 14 +++---- backend/src/typeorm/connection.ts | 1 - 6 files changed, 93 insertions(+), 59 deletions(-) create mode 100644 backend/src/apis/humhub/syncUser.test.ts create mode 100644 backend/src/apis/humhub/syncUser.ts diff --git a/backend/src/apis/humhub/HumHubClient.ts b/backend/src/apis/humhub/HumHubClient.ts index 813946774..b69944773 100644 --- a/backend/src/apis/humhub/HumHubClient.ts +++ b/backend/src/apis/humhub/HumHubClient.ts @@ -7,7 +7,6 @@ import { backendLogger as logger } from '@/server/logger' import { GetUser } from './model/GetUser' import { PostUser } from './model/PostUser' -import { PostUserError } from './model/PostUserError' import { UsersResponse } from './model/UsersResponse' /** diff --git a/backend/src/apis/humhub/ImportUsers.ts b/backend/src/apis/humhub/ImportUsers.ts index c80db4344..b57350abc 100644 --- a/backend/src/apis/humhub/ImportUsers.ts +++ b/backend/src/apis/humhub/ImportUsers.ts @@ -7,20 +7,12 @@ import { backendLogger as logger } from '@/server/logger' import { Connection } from '@/typeorm/connection' import { checkDBVersion } from '@/typeorm/DBVersion' -import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser' import { HumHubClient } from './HumHubClient' import { GetUser } from './model/GetUser' -import { PostUser } from './model/PostUser' +import { ExecutedHumhubAction, syncUser } from './syncUser' const USER_BULK_SIZE = 20 -enum ExecutedHumhubAction { - UPDATE, - CREATE, - SKIP, - DELETE, -} - function getUsersPage(page: number, limit: number): Promise<[User[], number]> { return User.findAndCount({ relations: { emailContact: true }, @@ -30,46 +22,6 @@ function getUsersPage(page: number, limit: number): Promise<[User[], number]> { }) } -/** - * Trigger action according to conditions - * | User exist on humhub | export to humhub allowed | changes in user data | ACTION - * | true | false | ignored | DELETE - * | true | true | true | UPDATE - * | true | true | false | SKIP - * | false | false | ignored | SKIP - * | false | true | ignored | CREATE - * @param user - * @param humHubClient - * @param humhubUsers - * @returns - */ -async function syncUser( - user: User, - humHubClient: HumHubClient, - humhubUsers: Map, -): Promise { - const postUser = new PostUser(user) - const humhubUser = humhubUsers.get(user.emailContact.email.trim()) - - if (humhubUser) { - if (!user.humhubAllowed) { - await humHubClient.deleteUser(humhubUser.id) - return ExecutedHumhubAction.DELETE - } - if (!isHumhubUserIdenticalToDbUser(humhubUser, user)) { - // if humhub allowed - await humHubClient.updateUser(postUser, humhubUser.id) - return ExecutedHumhubAction.UPDATE - } - } else { - if (user.humhubAllowed) { - await humHubClient.createUser(postUser) - return ExecutedHumhubAction.CREATE - } - } - return ExecutedHumhubAction.SKIP -} - /** * @param client * @returns user map indiced with email diff --git a/backend/src/apis/humhub/syncUser.test.ts b/backend/src/apis/humhub/syncUser.test.ts new file mode 100644 index 000000000..aebacc4cf --- /dev/null +++ b/backend/src/apis/humhub/syncUser.test.ts @@ -0,0 +1,34 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { User } from '@entity/User' +import { mocked } from 'ts-jest/utils' + +import { HumHubClient } from './HumHubClient' +import { GetUser } from './model/GetUser' +import { syncUser, ExecutedHumhubAction } from './syncUser' + +const defaultUser = User.create() + +jest.mock('./HumHubClient', () => { + return { + HumHubClient: jest.fn().mockImplementation(() => { + return { + createUser: () => {}, + updateUser: () => {}, + deleteUser: () => {}, + getInstance: () => { return new HumHubClient() } + } + }), + } +}) + +describe('syncUser function', () => { + test('When humhubUser exists and user.humhubAllowed is false, should return DELETE action', async () => { + const mockedHumhubClient = mocked(HumHubClient) + const humhubUsers = new Map() + + defaultUser.humhubAllowed = false + const result = await syncUser(defaultUser, new HumHubClient(), humhubUsers) + + expect(result).toBe(ExecutedHumhubAction.DELETE) + }) +}) diff --git a/backend/src/apis/humhub/syncUser.ts b/backend/src/apis/humhub/syncUser.ts new file mode 100644 index 000000000..33b04b6c0 --- /dev/null +++ b/backend/src/apis/humhub/syncUser.ts @@ -0,0 +1,52 @@ +import { User } from '@entity/User' + +import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser' +import { HumHubClient } from './HumHubClient' +import { GetUser } from './model/GetUser' +import { PostUser } from './model/PostUser' + +export enum ExecutedHumhubAction { + UPDATE, + CREATE, + SKIP, + DELETE, +} +/** + * Trigger action according to conditions + * | User exist on humhub | export to humhub allowed | changes in user data | ACTION + * | true | false | ignored | DELETE + * | true | true | true | UPDATE + * | true | true | false | SKIP + * | false | false | ignored | SKIP + * | false | true | ignored | CREATE + * @param user + * @param humHubClient + * @param humhubUsers + * @returns + */ +export async function syncUser( + user: User, + humHubClient: HumHubClient, + humhubUsers: Map, +): Promise { + const postUser = new PostUser(user) + const humhubUser = humhubUsers.get(user.emailContact.email.trim()) + + if (humhubUser) { + if (!user.humhubAllowed) { + await humHubClient.deleteUser(humhubUser.id) + return ExecutedHumhubAction.DELETE + } + if (!isHumhubUserIdenticalToDbUser(humhubUser, user)) { + // if humhub allowed + await humHubClient.updateUser(postUser, humhubUser.id) + return ExecutedHumhubAction.UPDATE + } + } else { + if (user.humhubAllowed) { + await humHubClient.createUser(postUser) + return ExecutedHumhubAction.CREATE + } + } + return ExecutedHumhubAction.SKIP +} diff --git a/backend/src/data/PublishName.logic.ts b/backend/src/data/PublishName.logic.ts index 22262f6ca..e17708ceb 100644 --- a/backend/src/data/PublishName.logic.ts +++ b/backend/src/data/PublishName.logic.ts @@ -22,10 +22,9 @@ export class PublishNameLogic { return this.user.firstName } if ( - [ - PublishNameType.PUBLISH_NAME_INITIALS, - PublishNameType.PUBLISH_NAME_INITIAL_LAST, - ].includes(publishNameType) + [PublishNameType.PUBLISH_NAME_INITIALS, PublishNameType.PUBLISH_NAME_INITIAL_LAST].includes( + publishNameType, + ) ) { return this.user.firstName.substring(0, 1) } @@ -48,10 +47,9 @@ export class PublishNameLogic { return this.user.lastName } if ( - [ - PublishNameType.PUBLISH_NAME_FIRST_INITIAL, - PublishNameType.PUBLISH_NAME_INITIALS, - ].includes(publishNameType) + [PublishNameType.PUBLISH_NAME_FIRST_INITIAL, PublishNameType.PUBLISH_NAME_INITIALS].includes( + publishNameType, + ) ) { return this.user.lastName.substring(0, 1) } diff --git a/backend/src/typeorm/connection.ts b/backend/src/typeorm/connection.ts index 70df8cae5..104f6449d 100644 --- a/backend/src/typeorm/connection.ts +++ b/backend/src/typeorm/connection.ts @@ -50,7 +50,6 @@ export class Connection { } catch (error) { // eslint-disable-next-line no-console console.log(error) - console.log('catch from createConnection') return null } } From f1ae304bbdad13910984651565d97f0aad93cb5e Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 19 Apr 2024 11:14:03 +0200 Subject: [PATCH 6/8] refactor and test --- backend/jest.config.js | 2 +- backend/src/apis/humhub/ImportUsers.ts | 2 +- .../humhub/compareHumhubUserDbUser.test.ts | 64 ++++++++++ .../src/apis/humhub/convertLanguage.test.ts | 31 +++++ backend/src/apis/humhub/model/Account.ts | 1 - backend/src/apis/humhub/model/GetUser.ts | 8 ++ backend/src/apis/humhub/model/Profile.ts | 8 +- backend/src/apis/humhub/syncUser.test.ts | 111 +++++++++++++++--- backend/src/apis/humhub/syncUser.ts | 7 +- backend/src/data/PublishName.logic.ts | 14 ++- 10 files changed, 218 insertions(+), 30 deletions(-) create mode 100644 backend/src/apis/humhub/compareHumhubUserDbUser.test.ts create mode 100644 backend/src/apis/humhub/convertLanguage.test.ts diff --git a/backend/jest.config.js b/backend/jest.config.js index 6140da0aa..867aeea5e 100644 --- a/backend/jest.config.js +++ b/backend/jest.config.js @@ -7,7 +7,7 @@ module.exports = { collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], coverageThreshold: { global: { - lines: 83, + lines: 82, }, }, setupFiles: ['/test/testSetup.ts'], diff --git a/backend/src/apis/humhub/ImportUsers.ts b/backend/src/apis/humhub/ImportUsers.ts index b57350abc..de33ba779 100644 --- a/backend/src/apis/humhub/ImportUsers.ts +++ b/backend/src/apis/humhub/ImportUsers.ts @@ -89,7 +89,7 @@ async function main() { userCount = users.length page++ const promises: Promise[] = [] - users.forEach((user: User) => promises.push(syncUser(user, humHubClient, humhubUsers))) + users.forEach((user: User) => promises.push(syncUser(user, humhubUsers))) const executedActions = await Promise.all(promises) executedActions.forEach((executedAction: ExecutedHumhubAction) => { executedHumhubActionsCount[executedAction as number]++ diff --git a/backend/src/apis/humhub/compareHumhubUserDbUser.test.ts b/backend/src/apis/humhub/compareHumhubUserDbUser.test.ts new file mode 100644 index 000000000..cc636d17a --- /dev/null +++ b/backend/src/apis/humhub/compareHumhubUserDbUser.test.ts @@ -0,0 +1,64 @@ +/* eslint-disable prettier/prettier */ +import { communityDbUser } from '@/util/communityUser' + +import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser' +import { GetUser } from './model/GetUser' + +const defaultUser = communityDbUser + +describe('isHumhubUserIdenticalToDbUser', () => { + beforeEach(() => { + defaultUser.firstName = 'first name' + defaultUser.lastName = 'last name' + defaultUser.alias = 'alias' + defaultUser.emailContact.email = 'email@gmail.com' + defaultUser.language = 'en' + }) + + it('Should return true because humhubUser was created from entity user', () => { + const humhubUser = new GetUser(defaultUser, 1) + const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser) + expect(result).toBe(true) + }) + + it('Should return false because first name differ', () => { + const humhubUser = new GetUser(defaultUser, 1) + humhubUser.profile.firstname = 'changed first name' + const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser) + expect(result).toBe(false) + }) + it('Should return false because last name differ', () => { + const humhubUser = new GetUser(defaultUser, 1) + humhubUser.profile.lastname = 'changed last name' + const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser) + expect(result).toBe(false) + }) + it('Should return false because username differ', () => { + const humhubUser = new GetUser(defaultUser, 1) + humhubUser.account.username = 'changed username' + const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser) + expect(result).toBe(false) + }) + + it('Should return false because email differ', () => { + const humhubUser = new GetUser(defaultUser, 1) + humhubUser.account.email = 'new@gmail.com' + const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser) + expect(result).toBe(false) + }) + + it('Should return false because language differ', () => { + const humhubUser = new GetUser(defaultUser, 1) + humhubUser.account.language = 'de' + const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser) + expect(result).toBe(false) + }) + + it('Should return false because gradido_address differ', () => { + const humhubUser = new GetUser(defaultUser, 1) + // eslint-disable-next-line camelcase + humhubUser.profile.gradido_address = 'changed gradido address' + const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser) + expect(result).toBe(false) + }) +}) diff --git a/backend/src/apis/humhub/convertLanguage.test.ts b/backend/src/apis/humhub/convertLanguage.test.ts new file mode 100644 index 000000000..052e63075 --- /dev/null +++ b/backend/src/apis/humhub/convertLanguage.test.ts @@ -0,0 +1,31 @@ +import { convertGradidoLanguageToHumhub, convertHumhubLanguageToGradido } from './convertLanguage' + +describe('convertGradidoLanguageToHumhub', () => { + it('Should convert "en" to "en-US"', () => { + const result = convertGradidoLanguageToHumhub('en') + expect(result).toBe('en-US') + }) + + it('Should return the same language for other values', () => { + const languages = ['de', 'fr', 'es', 'pt'] + languages.forEach((lang) => { + const result = convertGradidoLanguageToHumhub(lang) + expect(result).toBe(lang) + }) + }) +}) + +describe('convertHumhubLanguageToGradido', () => { + it('Should convert "en-US" to "en"', () => { + const result = convertHumhubLanguageToGradido('en-US') + expect(result).toBe('en') + }) + + it('Should return the same language for other values', () => { + const languages = ['de', 'fr', 'es', 'pt'] + languages.forEach((lang) => { + const result = convertHumhubLanguageToGradido(lang) + expect(result).toBe(lang) + }) + }) +}) diff --git a/backend/src/apis/humhub/model/Account.ts b/backend/src/apis/humhub/model/Account.ts index 52777e8c8..0926af2c2 100644 --- a/backend/src/apis/humhub/model/Account.ts +++ b/backend/src/apis/humhub/model/Account.ts @@ -16,6 +16,5 @@ export class Account { username: string email: string - tags: string[] language: string } diff --git a/backend/src/apis/humhub/model/GetUser.ts b/backend/src/apis/humhub/model/GetUser.ts index 16231c224..7d9814fc3 100644 --- a/backend/src/apis/humhub/model/GetUser.ts +++ b/backend/src/apis/humhub/model/GetUser.ts @@ -1,7 +1,15 @@ +import { User } from '@entity/User' + import { Account } from './Account' import { Profile } from './Profile' export class GetUser { + public constructor(user: User, id: number) { + this.id = id + this.account = new Account(user) + this.profile = new Profile(user) + } + id: number guid: string // eslint-disable-next-line camelcase diff --git a/backend/src/apis/humhub/model/Profile.ts b/backend/src/apis/humhub/model/Profile.ts index eadf6cf65..424c48026 100644 --- a/backend/src/apis/humhub/model/Profile.ts +++ b/backend/src/apis/humhub/model/Profile.ts @@ -2,11 +2,14 @@ import { User } from '@entity/User' import { CONFIG } from '@/config' +import { PublishNameLogic } from '@/data/PublishName.logic' +import { PublishNameType } from '@/graphql/enum/PublishNameType' export class Profile { public constructor(user: User) { - this.firstname = user.firstName - this.lastname = user.lastName + const publishNameLogic = new PublishNameLogic(user) + this.firstname = publishNameLogic.getFirstName(user.humhubPublishName as PublishNameType) + this.lastname = publishNameLogic.getLastName(user.humhubPublishName as PublishNameType) if (user.alias && user.alias.length > 2) { this.gradido_address = CONFIG.COMMUNITY_NAME + '/' + user.alias } else { @@ -17,5 +20,4 @@ export class Profile { firstname: string lastname: string gradido_address: string - about: string } diff --git a/backend/src/apis/humhub/syncUser.test.ts b/backend/src/apis/humhub/syncUser.test.ts index aebacc4cf..141714854 100644 --- a/backend/src/apis/humhub/syncUser.test.ts +++ b/backend/src/apis/humhub/syncUser.test.ts @@ -1,34 +1,111 @@ /* eslint-disable @typescript-eslint/no-empty-function */ import { User } from '@entity/User' -import { mocked } from 'ts-jest/utils' +import { UserContact } from '@entity/UserContact' + +import { CONFIG } from '@/config' import { HumHubClient } from './HumHubClient' import { GetUser } from './model/GetUser' import { syncUser, ExecutedHumhubAction } from './syncUser' -const defaultUser = User.create() +const defaultUser = new User() +defaultUser.emailContact = new UserContact() +defaultUser.emailContact.email = 'email@gmail.com' -jest.mock('./HumHubClient', () => { - return { - HumHubClient: jest.fn().mockImplementation(() => { - return { - createUser: () => {}, - updateUser: () => {}, - deleteUser: () => {}, - getInstance: () => { return new HumHubClient() } - } - }), - } -}) +CONFIG.HUMHUB_ACTIVE = true +CONFIG.HUMHUB_API_URL = 'http://localhost' + +let humhubClient: HumHubClient | undefined +let humhubClientSpy: { + createUser: jest.SpyInstance + updateUser: jest.SpyInstance + deleteUser: jest.SpyInstance +} describe('syncUser function', () => { - test('When humhubUser exists and user.humhubAllowed is false, should return DELETE action', async () => { - const mockedHumhubClient = mocked(HumHubClient) + beforeAll(() => { + humhubClient = HumHubClient.getInstance() + if (!humhubClient) { + throw new Error('error creating humhub client') + } + humhubClientSpy = { + createUser: jest.spyOn(humhubClient, 'createUser'), + updateUser: jest.spyOn(humhubClient, 'updateUser'), + deleteUser: jest.spyOn(humhubClient, 'deleteUser'), + } + humhubClientSpy.createUser.mockImplementation(() => Promise.resolve()) + humhubClientSpy.updateUser.mockImplementation(() => Promise.resolve()) + humhubClientSpy.deleteUser.mockImplementation(() => Promise.resolve()) + }) + + afterEach(() => { + humhubClientSpy.createUser.mockClear() + humhubClientSpy.updateUser.mockClear() + humhubClientSpy.deleteUser.mockClear() + }) + afterAll(() => { + jest.resetAllMocks() + }) + + /* + * Trigger action according to conditions + * | User exist on humhub | export to humhub allowed | changes in user data | ACTION + * | true | false | ignored | DELETE + * | true | true | true | UPDATE + * | true | true | false | SKIP + * | false | false | ignored | SKIP + * | false | true | ignored | CREATE + * */ + + it('When humhubUser exists and user.humhubAllowed is false, should return DELETE action', async () => { const humhubUsers = new Map() + humhubUsers.set(defaultUser.emailContact.email, new GetUser(defaultUser, 1)) defaultUser.humhubAllowed = false - const result = await syncUser(defaultUser, new HumHubClient(), humhubUsers) + const result = await syncUser(defaultUser, humhubUsers) expect(result).toBe(ExecutedHumhubAction.DELETE) }) + + it('When humhubUser exists and user.humhubAllowed is true and there are changes in user data, should return UPDATE action', async () => { + const humhubUsers = new Map() + const humhubUser = new GetUser(defaultUser, 1) + humhubUser.account.username = 'test username' + humhubUsers.set(defaultUser.emailContact.email, humhubUser) + + defaultUser.humhubAllowed = true + const result = await syncUser(defaultUser, humhubUsers) + + expect(result).toBe(ExecutedHumhubAction.UPDATE) + }) + + it('When humhubUser exists and user.humhubAllowed is true and there are no changes in user data, should return SKIP action', async () => { + const humhubUsers = new Map() + const humhubUser = new GetUser(defaultUser, 1) + humhubUsers.set(defaultUser.emailContact.email, humhubUser) + + defaultUser.humhubAllowed = true + const result = await syncUser(defaultUser, humhubUsers) + + expect(result).toBe(ExecutedHumhubAction.SKIP) + }) + + it('When humhubUser not exists and user.humhubAllowed is false, should return SKIP action', async () => { + const humhubUsers = new Map() + + defaultUser.humhubAllowed = false + const result = await syncUser(defaultUser, humhubUsers) + + expect(result).toBe(ExecutedHumhubAction.SKIP) + }) + + it('When humhubUser not exists and user.humhubAllowed is true, should return CREATE action', async () => { + const humhubUsers = new Map() + + defaultUser.humhubAllowed = true + const result = await syncUser(defaultUser, humhubUsers) + + expect(result).toBe(ExecutedHumhubAction.CREATE) + }) + }) diff --git a/backend/src/apis/humhub/syncUser.ts b/backend/src/apis/humhub/syncUser.ts index 33b04b6c0..fc6fcc99b 100644 --- a/backend/src/apis/humhub/syncUser.ts +++ b/backend/src/apis/humhub/syncUser.ts @@ -1,5 +1,7 @@ import { User } from '@entity/User' +import { LogError } from '@/server/LogError' + import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser' import { HumHubClient } from './HumHubClient' import { GetUser } from './model/GetUser' @@ -26,11 +28,14 @@ export enum ExecutedHumhubAction { */ export async function syncUser( user: User, - humHubClient: HumHubClient, humhubUsers: Map, ): Promise { const postUser = new PostUser(user) const humhubUser = humhubUsers.get(user.emailContact.email.trim()) + const humHubClient = HumHubClient.getInstance() + if (!humHubClient) { + throw new LogError('Error creating humhub client') + } if (humhubUser) { if (!user.humhubAllowed) { diff --git a/backend/src/data/PublishName.logic.ts b/backend/src/data/PublishName.logic.ts index e17708ceb..831c27e0d 100644 --- a/backend/src/data/PublishName.logic.ts +++ b/backend/src/data/PublishName.logic.ts @@ -8,10 +8,10 @@ export class PublishNameLogic { /** * get first name based on publishNameType: PublishNameType value * @param publishNameType - * @returns user.firstName for GMS_PUBLISH_NAME_FIRST, GMS_PUBLISH_NAME_FIRST_INITIAL or GMS_PUBLISH_NAME_FULL - * first initial from user.firstName for GMS_PUBLISH_NAME_INITIALS or GMS_PUBLISH_NAME_ALIAS_OR_INITALS and empty alias + * @returns user.firstName for PUBLISH_NAME_FIRST, PUBLISH_NAME_FIRST_INITIAL or PUBLISH_NAME_FULL + * first initial from user.firstName for PUBLISH_NAME_INITIALS or PUBLISH_NAME_INITIAL_LAST */ - public getFirstName(publishNameType: PublishNameType): string | undefined { + public getFirstName(publishNameType: PublishNameType): string { if ( [ PublishNameType.PUBLISH_NAME_FIRST, @@ -28,15 +28,16 @@ export class PublishNameLogic { ) { return this.user.firstName.substring(0, 1) } + return '' } /** * get last name based on publishNameType: GmsPublishNameType value * @param publishNameType - * @returns user.lastName for GMS_PUBLISH_NAME_FULL - * first initial from user.lastName for GMS_PUBLISH_NAME_FIRST_INITIAL, GMS_PUBLISH_NAME_INITIALS or GMS_PUBLISH_NAME_ALIAS_OR_INITALS and empty alias + * @returns user.lastName for PUBLISH_NAME_LAST, PUBLISH_NAME_INITIAL_LAST, PUBLISH_NAME_FULL + * first initial from user.lastName for PUBLISH_NAME_FIRST_INITIAL, PUBLISH_NAME_INITIALS */ - public getLastName(publishNameType: PublishNameType): string | undefined { + public getLastName(publishNameType: PublishNameType): string { if ( [ PublishNameType.PUBLISH_NAME_LAST, @@ -53,5 +54,6 @@ export class PublishNameLogic { ) { return this.user.lastName.substring(0, 1) } + return '' } } From 24224e96383af7e272cd99ada4ae08d5b7d0052c Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 19 Apr 2024 11:33:43 +0200 Subject: [PATCH 7/8] lint --- backend/src/apis/humhub/syncUser.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/apis/humhub/syncUser.test.ts b/backend/src/apis/humhub/syncUser.test.ts index 141714854..d99d15156 100644 --- a/backend/src/apis/humhub/syncUser.test.ts +++ b/backend/src/apis/humhub/syncUser.test.ts @@ -107,5 +107,4 @@ describe('syncUser function', () => { expect(result).toBe(ExecutedHumhubAction.CREATE) }) - }) From 74f460a765cf74793b64aa2056288ab26956a6d3 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 19 Apr 2024 19:03:45 +0200 Subject: [PATCH 8/8] rename --- backend/package.json | 2 +- backend/src/apis/humhub/{ImportUsers.ts => ExportUsers.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename backend/src/apis/humhub/{ImportUsers.ts => ExportUsers.ts} (100%) diff --git a/backend/package.json b/backend/package.json index c3b74115d..7e70b7b8c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -18,7 +18,7 @@ "klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/executeKlicktipp.ts", "gmsusers": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/gmsUsers.ts", "gmsuserList": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/gmsUserList.ts", - "humhubUserExport": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/apis/humhub/ImportUsers.ts", + "humhubUserExport": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/apis/humhub/ExportUsers.ts", "locales": "scripts/sort.sh" }, "dependencies": { diff --git a/backend/src/apis/humhub/ImportUsers.ts b/backend/src/apis/humhub/ExportUsers.ts similarity index 100% rename from backend/src/apis/humhub/ImportUsers.ts rename to backend/src/apis/humhub/ExportUsers.ts