diff --git a/backend/src/apis/gms/GmsClient.ts b/backend/src/apis/gms/GmsClient.ts index 8d4876ead..b1cdade2d 100644 --- a/backend/src/apis/gms/GmsClient.ts +++ b/backend/src/apis/gms/GmsClient.ts @@ -117,31 +117,70 @@ export async function userByUuid(uuid: string): Promise { - const baseUrl = CONFIG.GMS_URL.endsWith('/') ? CONFIG.GMS_URL : CONFIG.GMS_URL.concat('/') - const service = 'community-user' - const config = { - headers: { - accept: 'application/json', - language: 'en', - timezone: 'UTC', - connection: 'keep-alive', - authorization: apiKey, - }, - } - try { - const result = await axios.post(baseUrl.concat(service), user, config) - logger.debug('POST-Response of community-user:', result) - if (result.status !== 200) { - throw new LogError('HTTP Status Error in community-user:', result.status, result.statusText) + if (CONFIG.GMS_ACTIVE) { + const baseUrl = CONFIG.GMS_URL.endsWith('/') ? CONFIG.GMS_URL : CONFIG.GMS_URL.concat('/') + const service = 'community-user' + const config = { + headers: { + accept: 'application/json', + language: 'en', + timezone: 'UTC', + connection: 'keep-alive', + authorization: apiKey, + }, } - logger.debug('responseData:', result.data.responseData) - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - // const gmsUser = JSON.parse(result.data.responseData) - // logger.debug('gmsUser:', gmsUser) - return true - } catch (error: any) { - logger.error('Error in Get community-user:', error) - throw new LogError(error.message) + try { + const result = await axios.post(baseUrl.concat(service), user, config) + logger.debug('POST-Response of community-user:', result) + if (result.status !== 200) { + throw new LogError('HTTP Status Error in community-user:', result.status, result.statusText) + } + logger.debug('responseData:', result.data.responseData) + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + // const gmsUser = JSON.parse(result.data.responseData) + // logger.debug('gmsUser:', gmsUser) + return true + } catch (error: any) { + logger.error('Error in post community-user:', error) + throw new LogError(error.message) + } + } else { + logger.info('GMS-Communication disabled per ConfigKey GMS_ACTIVE=false!') + return false + } +} + +export async function updateGmsUser(apiKey: string, user: GmsUser): Promise { + if (CONFIG.GMS_ACTIVE) { + const baseUrl = CONFIG.GMS_URL.endsWith('/') ? CONFIG.GMS_URL : CONFIG.GMS_URL.concat('/') + const service = 'community-user' + const config = { + headers: { + accept: 'application/json', + language: 'en', + timezone: 'UTC', + connection: 'keep-alive', + authorization: apiKey, + }, + } + try { + const result = await axios.patch(baseUrl.concat(service), user, config) + logger.debug('PATCH-Response of community-user:', result) + if (result.status !== 200) { + throw new LogError('HTTP Status Error in community-user:', result.status, result.statusText) + } + logger.debug('responseData:', result.data.responseData) + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + // const gmsUser = JSON.parse(result.data.responseData) + // logger.debug('gmsUser:', gmsUser) + return true + } catch (error: any) { + logger.error('Error in patch community-user:', error) + throw new LogError(error.message) + } + } else { + logger.info('GMS-Communication disabled per ConfigKey GMS_ACTIVE=false!') + return false } } diff --git a/backend/src/apis/gms/model/GmsUser.ts b/backend/src/apis/gms/model/GmsUser.ts index 7f7db7660..0b16f00fb 100644 --- a/backend/src/apis/gms/model/GmsUser.ts +++ b/backend/src/apis/gms/model/GmsUser.ts @@ -8,6 +8,7 @@ export class GmsUser { constructor(user: dbUser) { this.userUuid = user.gradidoID // this.communityUuid = user.communityUuid + this.language = user.language this.email = this.getGmsEmail(user) this.countryCode = this.getGmsCountryCode(user) this.mobile = this.getGmsPhone(user) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 43e97b920..dd35d180e 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -143,6 +143,7 @@ const federation = { const gms = { GMS_ACTIVE: process.env.GMS_ACTIVE === 'true' || false, + GMS_CREATE_USER_THROW_ERRORS: process.env.GMS_CREATE_USER_THROW_ERRORS === 'true' || false, // koordinates of Illuminz-instance of GMS GMS_URL: process.env.GMS_HOST ?? 'http://localhost:4044/', // used as secret postfix attached at the gms community-auth-url endpoint ('/hook/gms/' + 'secret') diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 791ed19dc..a096fda44 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -28,6 +28,8 @@ import { SearchAdminUsersResult } from '@model/AdminUser' import { User } from '@model/User' import { UserAdmin, SearchUsersResult } from '@model/UserAdmin' +import { updateGmsUser } from '@/apis/gms/GmsClient' +import { GmsUser } from '@/apis/gms/model/GmsUser' import { subscribe } from '@/apis/KlicktippController' import { encode } from '@/auth/JWT' import { RIGHTS } from '@/auth/RIGHTS' @@ -67,6 +69,7 @@ import { randombytes_random } from 'sodium-native' import { FULL_CREATION_AVAILABLE } from './const/const' import { getHomeCommunity } from './util/communities' +import { compareGmsRelevantUserSettings } from './util/compareGmsRelevantUserSettings' import { getUserCreations } from './util/creations' import { findUserByIdentifier } from './util/findUserByIdentifier' import { findUsers } from './util/findUsers' @@ -374,7 +377,11 @@ export class UserResolver { await sendUserToGms(dbUser, homeCom) } } catch (err) { - logger.error('Error publishing new created user to GMS:', err) + if (CONFIG.GMS_CREATE_USER_THROW_ERRORS) { + throw new LogError('Error publishing new created user to GMS:', err) + } else { + logger.error('Error publishing new created user to GMS:', err) + } } } return new User(dbUser) @@ -394,7 +401,6 @@ export class UserResolver { logger.warn(`no user found with ${email}`) return true } - if (!canEmailResend(user.emailContact.updatedAt || user.emailContact.createdAt)) { throw new LogError( `Email already sent less than ${printTimeDuration(CONFIG.EMAIL_CODE_REQUEST_TIME)} ago`, @@ -540,8 +546,10 @@ export class UserResolver { @Authorized([RIGHTS.UPDATE_USER_INFOS]) @Mutation(() => Boolean) async updateUserInfos( - @Args() - { + @Args() updateUserInfosArgs: UpdateUserInfosArgs, + @Ctx() context: Context, + ): Promise { + const { firstName, lastName, alias, @@ -554,13 +562,13 @@ export class UserResolver { gmsPublishName, gmsLocation, gmsPublishLocation, - }: UpdateUserInfosArgs, - @Ctx() context: Context, - ): Promise { + } = updateUserInfosArgs logger.info( `updateUserInfos(${firstName}, ${lastName}, ${alias}, ${language}, ***, ***, ${hideAmountGDD}, ${hideAmountGDT}, ${gmsAllowed}, ${gmsPublishName}, ${gmsLocation}, ${gmsPublishLocation})...`, ) const user = getUser(context) + const updateUserInGMS = compareGmsRelevantUserSettings(user, updateUserInfosArgs) + // try { if (firstName) { user.firstName = firstName @@ -643,6 +651,17 @@ export class UserResolver { logger.info('updateUserInfos() successfully finished...') await EVENT_USER_INFO_UPDATE(user) + // validate if user settings are changed with relevance to update gms-user + if (CONFIG.GMS_ACTIVE && updateUserInGMS) { + logger.debug(`changed user-settings relevant for gms-user update...`) + const homeCom = await getHomeCommunity() + if (homeCom.gmsApiKey !== null) { + logger.debug(`gms-user update...`, user) + await updateGmsUser(homeCom.gmsApiKey, new GmsUser(user)) + logger.debug(`gms-user update successfully.`) + } + } + return true } diff --git a/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts b/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts new file mode 100644 index 000000000..e40cdcdfe --- /dev/null +++ b/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts @@ -0,0 +1,90 @@ +import { Point } from '@dbTools/typeorm' +import { User as DbUser } from '@entity/User' + +import { UpdateUserInfosArgs } from '@/graphql/arg/UpdateUserInfosArgs' +import { GmsPublishNameType } from '@/graphql/enum/GmsPublishNameType' +import { LogError } from '@/server/LogError' +import { backendLogger as logger } from '@/server/logger' + +import { Point2Location } from './Location2Point' + +export function compareGmsRelevantUserSettings( + orgUser: DbUser, + updateUserInfosArgs: UpdateUserInfosArgs, +): boolean { + if (!orgUser) { + throw new LogError('comparison without any user is impossible') + } + logger.debug('compareGmsRelevantUserSettings:', orgUser, updateUserInfosArgs) + // nach GMS updaten, wenn alias gesetzt wird oder ist und PublishLevel die alias-Übermittlung erlaubt + if ( + updateUserInfosArgs.alias && + orgUser.alias !== updateUserInfosArgs.alias && + ((updateUserInfosArgs.gmsPublishName && + updateUserInfosArgs.gmsPublishName.valueOf === + GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS.valueOf) || + (!updateUserInfosArgs.gmsPublishName && + orgUser.gmsPublishName && + orgUser.gmsPublishName.valueOf === + GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS.valueOf)) + ) { + return true + } + if ( + (updateUserInfosArgs.firstName && orgUser.firstName !== updateUserInfosArgs.firstName) || + (updateUserInfosArgs.lastName && orgUser.lastName !== updateUserInfosArgs.lastName) + ) { + return true + } + if ( + updateUserInfosArgs.gmsAllowed !== undefined && + updateUserInfosArgs.gmsAllowed && + orgUser.gmsAllowed !== updateUserInfosArgs.gmsAllowed + ) { + return true + } + if ( + updateUserInfosArgs.gmsPublishLocation && + orgUser.gmsPublishLocation !== updateUserInfosArgs.gmsPublishLocation + ) { + return true + } + if ( + updateUserInfosArgs.gmsPublishName && + orgUser.gmsPublishName !== updateUserInfosArgs.gmsPublishName + ) { + return true + } + if (updateUserInfosArgs.language && orgUser.language !== updateUserInfosArgs.language) { + return true + } + if ( + updateUserInfosArgs.gmsLocation && + orgUser.location === null && + updateUserInfosArgs.gmsLocation !== null + ) { + return true + } + if ( + updateUserInfosArgs.gmsLocation && + orgUser.location !== null && + updateUserInfosArgs.gmsLocation === null + ) { + return true + } + if ( + updateUserInfosArgs.gmsLocation && + orgUser.location !== null && + updateUserInfosArgs.gmsLocation !== null + ) { + const orgLocation = Point2Location(orgUser.location as Point) + const changedLocation = updateUserInfosArgs.gmsLocation + if ( + orgLocation.latitude !== changedLocation.latitude || + orgLocation.longitude !== changedLocation.longitude + ) { + return true + } + } + return false +} diff --git a/backend/src/graphql/resolver/util/sendUserToGms.ts b/backend/src/graphql/resolver/util/sendUserToGms.ts index 335141ffe..c21550b91 100644 --- a/backend/src/graphql/resolver/util/sendUserToGms.ts +++ b/backend/src/graphql/resolver/util/sendUserToGms.ts @@ -3,6 +3,7 @@ import { User as DbUser } from '@entity/User' import { createGmsUser } from '@/apis/gms/GmsClient' import { GmsUser } from '@/apis/gms/model/GmsUser' +import { CONFIG } from '@/config' import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' @@ -22,6 +23,10 @@ export async function sendUserToGms(user: DbUser, homeCom: DbCommunity): Promise logger.debug('mark user as gms published:', user) } } catch (err) { - logger.warn('publishing user fails with ', err) + if (CONFIG.GMS_CREATE_USER_THROW_ERRORS) { + throw new LogError('publishing user fails with ', err) + } else { + logger.warn('publishing user fails with ', err) + } } } diff --git a/backend/src/seeds/factory/user.ts b/backend/src/seeds/factory/user.ts index 3ddddf336..f0bda3a3f 100644 --- a/backend/src/seeds/factory/user.ts +++ b/backend/src/seeds/factory/user.ts @@ -39,8 +39,12 @@ export const userFactory = async ( dbUser = await User.findOneOrFail({ where: { id }, relations: ['userRoles'] }) if (user.createdAt || user.deletedAt || user.role) { - if (user.createdAt) dbUser.createdAt = user.createdAt - if (user.deletedAt) dbUser.deletedAt = user.deletedAt + if (user.createdAt) { + dbUser.createdAt = user.createdAt + } + if (user.deletedAt) { + dbUser.deletedAt = user.deletedAt + } if (user.role && (user.role === RoleNames.ADMIN || user.role === RoleNames.MODERATOR)) { await setUserRole(dbUser, user.role) }