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"