From 9ef45c0b6a064a7a5d4e6efee3d47eac14996ed3 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Mon, 8 Jan 2024 22:29:52 +0100 Subject: [PATCH 01/15] updateUserInfo with gms attributes --- backend/src/apis/gms/model/GmsEnums.ts | 36 --------------- backend/src/apis/gms/model/GmsUser.ts | 6 ++- .../src/graphql/arg/UpdateUserInfosArgs.ts | 20 ++++++++- .../graphql/enum/GmsPublishLocationType.ts | 12 +++++ .../src/graphql/enum/GmsPublishNameType.ts | 14 ++++++ .../src/graphql/enum/GmsPublishPhoneType.ts | 12 +++++ backend/src/graphql/model/Location.ts | 31 +++++++++++++ .../src/graphql/resolver/UserResolver.test.ts | 45 ++++++++++++++++++- backend/src/graphql/resolver/UserResolver.ts | 15 ++++++- backend/src/graphql/scalar/Geometry.ts | 33 ++++++++++++++ backend/src/seeds/graphql/mutations.ts | 8 ++++ 11 files changed, 190 insertions(+), 42 deletions(-) delete mode 100644 backend/src/apis/gms/model/GmsEnums.ts create mode 100644 backend/src/graphql/enum/GmsPublishLocationType.ts create mode 100644 backend/src/graphql/enum/GmsPublishNameType.ts create mode 100644 backend/src/graphql/enum/GmsPublishPhoneType.ts create mode 100644 backend/src/graphql/model/Location.ts create mode 100644 backend/src/graphql/scalar/Geometry.ts diff --git a/backend/src/apis/gms/model/GmsEnums.ts b/backend/src/apis/gms/model/GmsEnums.ts deleted file mode 100644 index 1342327d1..000000000 --- a/backend/src/apis/gms/model/GmsEnums.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { registerEnumType } from 'type-graphql' - -export enum GmsPublishNameType { - GMS_PUBLISH_NAME_ALIAS_OR_INITALS = 0, - GMS_PUBLISH_NAME_INITIALS = 1, - GMS_PUBLISH_NAME_FIRST = 2, - GMS_PUBLISH_NAME_FIRST_INITIAL = 3, - GMS_PUBLISH_NAME_FULL = 4, -} - -registerEnumType(GmsPublishNameType, { - name: 'GmsPublishNameType', // this one is mandatory - description: 'Type of name publishing', // this one is optional -}) - -export enum GmsPublishPhoneType { - GMS_PUBLISH_PHONE_NOTHING = 0, - GMS_PUBLISH_PHONE_COUNTRY = 1, - GMS_PUBLISH_PHONE_FULL = 2, -} - -registerEnumType(GmsPublishPhoneType, { - name: 'GmsPublishPhoneType', // this one is mandatory - description: 'Type of Phone publishing', // this one is optional -}) - -export enum GmsLocationType { - GMS_LOCATION_TYPE_EXACT = 0, - GMS_LOCATION_TYPE_APPROXIMATE = 1, - GMS_LOCATION_TYPE_RANDOM = 2, -} - -registerEnumType(GmsLocationType, { - name: 'GmsLocationType', // this one is mandatory - description: 'Type of location treatment in GMS', // this one is optional -}) diff --git a/backend/src/apis/gms/model/GmsUser.ts b/backend/src/apis/gms/model/GmsUser.ts index 2fad3bd1e..7f7db7660 100644 --- a/backend/src/apis/gms/model/GmsUser.ts +++ b/backend/src/apis/gms/model/GmsUser.ts @@ -1,6 +1,8 @@ import { User as dbUser } from '@entity/User' -import { GmsLocationType, GmsPublishNameType, GmsPublishPhoneType } from './GmsEnums' +import { GmsPublishLocationType } from '@/graphql/enum/GmsPublishLocationType' +import { GmsPublishNameType } from '@/graphql/enum/GmsPublishNameType' +import { GmsPublishPhoneType } from '@/graphql/enum/GmsPublishPhoneType' export class GmsUser { constructor(user: dbUser) { @@ -12,7 +14,7 @@ export class GmsUser { this.firstName = this.getGmsFirstName(user) this.lastName = this.getGmsLastName(user) this.alias = this.getGmsAlias(user) - this.type = GmsLocationType.GMS_LOCATION_TYPE_RANDOM + this.type = GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM this.location = null } diff --git a/backend/src/graphql/arg/UpdateUserInfosArgs.ts b/backend/src/graphql/arg/UpdateUserInfosArgs.ts index 6b2ab1032..ab1263eee 100644 --- a/backend/src/graphql/arg/UpdateUserInfosArgs.ts +++ b/backend/src/graphql/arg/UpdateUserInfosArgs.ts @@ -1,6 +1,8 @@ -import { IsBoolean, IsInt, IsString } from 'class-validator' +import { IsBoolean, IsInt, IsObject, IsString } from 'class-validator' import { ArgsType, Field, Int } from 'type-graphql' +import { Location } from '@model/Location' + @ArgsType() export class UpdateUserInfosArgs { @Field({ nullable: true }) @@ -38,4 +40,20 @@ export class UpdateUserInfosArgs { @Field({ nullable: true }) @IsBoolean() hideAmountGDT?: boolean + + @Field({ nullable: false }) + @IsBoolean() + gmsAllowed: boolean + + @Field(() => Int, { nullable: false }) + @IsInt() + gmsPublishName: number + + @Field(() => Location, { nullable: true }) + @IsObject() + gmsLocation?: Location | null + + @Field(() => Int, { nullable: false }) + @IsInt() + gmsPublishLocation: number } diff --git a/backend/src/graphql/enum/GmsPublishLocationType.ts b/backend/src/graphql/enum/GmsPublishLocationType.ts new file mode 100644 index 000000000..afb9c246d --- /dev/null +++ b/backend/src/graphql/enum/GmsPublishLocationType.ts @@ -0,0 +1,12 @@ +import { registerEnumType } from 'type-graphql' + +export enum GmsPublishLocationType { + GMS_LOCATION_TYPE_EXACT = 0, + GMS_LOCATION_TYPE_APPROXIMATE = 1, + GMS_LOCATION_TYPE_RANDOM = 2, +} + +registerEnumType(GmsPublishLocationType, { + name: 'GmsPublishLocationType', // this one is mandatory + description: 'Type of location treatment in GMS', // this one is optional +}) diff --git a/backend/src/graphql/enum/GmsPublishNameType.ts b/backend/src/graphql/enum/GmsPublishNameType.ts new file mode 100644 index 000000000..08aaaf8ef --- /dev/null +++ b/backend/src/graphql/enum/GmsPublishNameType.ts @@ -0,0 +1,14 @@ +import { registerEnumType } from 'type-graphql' + +export enum GmsPublishNameType { + GMS_PUBLISH_NAME_ALIAS_OR_INITALS = 0, + GMS_PUBLISH_NAME_INITIALS = 1, + GMS_PUBLISH_NAME_FIRST = 2, + GMS_PUBLISH_NAME_FIRST_INITIAL = 3, + GMS_PUBLISH_NAME_FULL = 4, +} + +registerEnumType(GmsPublishNameType, { + name: 'GmsPublishNameType', // this one is mandatory + description: 'Type of name publishing', // this one is optional +}) diff --git a/backend/src/graphql/enum/GmsPublishPhoneType.ts b/backend/src/graphql/enum/GmsPublishPhoneType.ts new file mode 100644 index 000000000..a9821d8ff --- /dev/null +++ b/backend/src/graphql/enum/GmsPublishPhoneType.ts @@ -0,0 +1,12 @@ +import { registerEnumType } from 'type-graphql' + +export enum GmsPublishPhoneType { + GMS_PUBLISH_PHONE_NOTHING = 0, + GMS_PUBLISH_PHONE_COUNTRY = 1, + GMS_PUBLISH_PHONE_FULL = 2, +} + +registerEnumType(GmsPublishPhoneType, { + name: 'GmsPublishPhoneType', // this one is mandatory + description: 'Type of Phone publishing', // this one is optional +}) diff --git a/backend/src/graphql/model/Location.ts b/backend/src/graphql/model/Location.ts new file mode 100644 index 000000000..281ee9ff4 --- /dev/null +++ b/backend/src/graphql/model/Location.ts @@ -0,0 +1,31 @@ +import { Point } from '@dbTools/typeorm' +import { ArgsType, Field, InputType, Int } from 'type-graphql' + +@InputType() +@ArgsType() +// @ObjectType() +export class Location { + constructor(lon: number, lat: number) { + this.longitude = lon + this.latitude = lat + } + + @Field(() => Int) + longitude: number + + @Field(() => Int) + latitude: number + + // point is no Field and not part of the graphql type + private point: Point + + public getPoint(): Point { + const pointStr = '{ "type": "Point", "coordinates": [' + .concat(this.longitude.toString()) + .concat(', ') + .concat(this.latitude.toString()) + .concat('] }') + this.point = JSON.parse(pointStr) as Point + return this.point + } +} diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index a4892496b..7a81b7017 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -16,6 +16,8 @@ import { ApolloServerTestClient } from 'apollo-server-testing' import { GraphQLError } from 'graphql' import { v4 as uuidv4, validate as validateUUID, version as versionUUID } from 'uuid' +import { GmsPublishLocationType } from '@enum/GmsPublishLocationType' +import { GmsPublishNameType } from '@enum/GmsPublishNameType' import { OptInType } from '@enum/OptInType' import { PasswordEncryptionType } from '@enum/PasswordEncryptionType' import { RoleNames } from '@enum/RoleNames' @@ -1165,7 +1167,16 @@ describe('UserResolver', () => { it('throws an error', async () => { jest.clearAllMocks() resetToken() - await expect(mutate({ mutation: updateUserInfos })).resolves.toEqual( + await expect( + mutate({ + mutation: updateUserInfos, + variables: { + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, + }, + }), + ).resolves.toEqual( expect.objectContaining({ errors: [new GraphQLError('401 Unauthorized')], }), @@ -1190,7 +1201,16 @@ describe('UserResolver', () => { }) it('returns true', async () => { - await expect(mutate({ mutation: updateUserInfos })).resolves.toEqual( + await expect( + mutate({ + mutation: updateUserInfos, + variables: { + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, + }, + }), + ).resolves.toEqual( expect.objectContaining({ data: { updateUserInfos: true, @@ -1207,6 +1227,9 @@ describe('UserResolver', () => { firstName: 'Benjamin', lastName: 'Blümchen', locale: 'en', + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }) await expect(User.find()).resolves.toEqual([ @@ -1244,6 +1267,9 @@ describe('UserResolver', () => { mutation: updateUserInfos, variables: { alias: 'bibi_Bloxberg', + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }) await expect(User.find()).resolves.toEqual([ @@ -1263,6 +1289,9 @@ describe('UserResolver', () => { mutation: updateUserInfos, variables: { locale: 'not-valid', + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }), ).resolves.toEqual( @@ -1287,6 +1316,9 @@ describe('UserResolver', () => { variables: { password: 'wrong password', passwordNew: 'Aa12345_', + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }), ).resolves.toEqual( @@ -1310,6 +1342,9 @@ describe('UserResolver', () => { variables: { password: 'Aa12345_', passwordNew: 'Aa12345', + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }), ).resolves.toEqual( @@ -1338,6 +1373,9 @@ describe('UserResolver', () => { variables: { password: 'Aa12345_', passwordNew: 'Bb12345_', + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }), ).resolves.toEqual( @@ -2585,6 +2623,9 @@ describe('UserResolver', () => { mutation: updateUserInfos, variables: { alias: 'bibi', + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }) }) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 45053bda4..fa00a4d57 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -547,10 +547,16 @@ export class UserResolver { passwordNew, hideAmountGDD, hideAmountGDT, + gmsAllowed, + gmsPublishName, + gmsLocation, + gmsPublishLocation, }: UpdateUserInfosArgs, @Ctx() context: Context, ): Promise { - logger.info(`updateUserInfos(${firstName}, ${lastName}, ${language}, ***, ***)...`) + logger.info( + `updateUserInfos(${firstName}, ${lastName}, ${language}, ***, ***, ${gmsAllowed}, ${gmsPublishName}, ${gmsLocation}, ${gmsPublishLocation})...`, + ) const user = getUser(context) if (firstName) { @@ -599,6 +605,13 @@ export class UserResolver { user.hideAmountGDT = hideAmountGDT } + user.gmsAllowed = gmsAllowed + user.gmsPublishName = gmsPublishName + if (gmsLocation) { + user.location = gmsLocation.getPoint() + } + user.gmsPublishLocation = gmsPublishLocation + const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction('REPEATABLE READ') diff --git a/backend/src/graphql/scalar/Geometry.ts b/backend/src/graphql/scalar/Geometry.ts new file mode 100644 index 000000000..c2cd48fdb --- /dev/null +++ b/backend/src/graphql/scalar/Geometry.ts @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { Geometry as DbGeometry } from '@dbTools/typeorm' +import { GraphQLScalarType, Kind } from 'graphql' + +import { Location } from '@model/Location' + +export const GeometryScalar = new GraphQLScalarType({ + name: 'Geometry', + description: + 'The `Geometry` scalar type to represent longitude and latitude values of a geo location', + + serialize(value: DbGeometry): Location { + // Check type of value + if (value.type !== 'Point') { + throw new Error(`GeometryScalar can only serialize Geometry type 'Point' values`) + } + + return new Location(value.coordinates[0], value.coordinates[1]) + }, + + parseValue(value): DbGeometry { + const geometry: DbGeometry = JSON.parse(value) as DbGeometry + return geometry + }, + + parseLiteral(ast) { + if (ast.kind !== Kind.STRING) { + throw new TypeError(`${String(ast)} is not a valid Geometry value.`) + } + + return JSON.parse(ast.value) as DbGeometry + }, +}) diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 554cc4def..a658c84d7 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -34,6 +34,10 @@ export const updateUserInfos = gql` $locale: String $hideAmountGDD: Boolean $hideAmountGDT: Boolean + $gmsAllowed: Boolean! + $gmsPublishName: Int! + $gmsLocation: Location + $gmsPublishLocation: Int! ) { updateUserInfos( firstName: $firstName @@ -44,6 +48,10 @@ export const updateUserInfos = gql` language: $locale hideAmountGDD: $hideAmountGDD hideAmountGDT: $hideAmountGDT + gmsAllowed: $gmsAllowed + gmsPublishName: $gmsPublishName + gmsLocation: $gmsLocation + gmsPublishLocation: $gmsPublishLocation ) } ` From f875214eff6b7a76cc47b61ccc25e5738f4b4ab3 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Tue, 23 Jan 2024 22:07:47 +0100 Subject: [PATCH 02/15] test for updateUserInfo with geolocation but db-driver error --- .../src/graphql/arg/UpdateUserInfosArgs.ts | 11 +- backend/src/graphql/model/Location.ts | 23 +--- .../src/graphql/resolver/UserResolver.test.ts | 91 ++++++++++++++ backend/src/graphql/resolver/UserResolver.ts | 113 ++++++++++-------- backend/src/graphql/validator/Location.ts | 31 +++++ 5 files changed, 195 insertions(+), 74 deletions(-) create mode 100644 backend/src/graphql/validator/Location.ts diff --git a/backend/src/graphql/arg/UpdateUserInfosArgs.ts b/backend/src/graphql/arg/UpdateUserInfosArgs.ts index ab1263eee..40e63f488 100644 --- a/backend/src/graphql/arg/UpdateUserInfosArgs.ts +++ b/backend/src/graphql/arg/UpdateUserInfosArgs.ts @@ -1,8 +1,13 @@ -import { IsBoolean, IsInt, IsObject, IsString } from 'class-validator' -import { ArgsType, Field, Int } from 'type-graphql' +import { IsBoolean, IsInt, IsString } from 'class-validator' +import { ArgsType, Field, InputType, Int } from 'type-graphql' import { Location } from '@model/Location' +import { isValidLocation } from '../validator/Location' + +// import { isValidLocation } from '../validator/Location' + +@InputType() @ArgsType() export class UpdateUserInfosArgs { @Field({ nullable: true }) @@ -50,7 +55,7 @@ export class UpdateUserInfosArgs { gmsPublishName: number @Field(() => Location, { nullable: true }) - @IsObject() + @isValidLocation() gmsLocation?: Location | null @Field(() => Int, { nullable: false }) diff --git a/backend/src/graphql/model/Location.ts b/backend/src/graphql/model/Location.ts index 281ee9ff4..2b4c720f4 100644 --- a/backend/src/graphql/model/Location.ts +++ b/backend/src/graphql/model/Location.ts @@ -1,31 +1,10 @@ -import { Point } from '@dbTools/typeorm' -import { ArgsType, Field, InputType, Int } from 'type-graphql' +import { ArgsType, Field, Int } from 'type-graphql' -@InputType() @ArgsType() -// @ObjectType() export class Location { - constructor(lon: number, lat: number) { - this.longitude = lon - this.latitude = lat - } - @Field(() => Int) longitude: number @Field(() => Int) latitude: number - - // point is no Field and not part of the graphql type - private point: Point - - public getPoint(): Point { - const pointStr = '{ "type": "Point", "coordinates": [' - .concat(this.longitude.toString()) - .concat(', ') - .concat(this.latitude.toString()) - .concat('] }') - this.point = JSON.parse(pointStr) as Point - return this.point - } } diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 7a81b7017..f58cb53b0 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -23,6 +23,7 @@ import { PasswordEncryptionType } from '@enum/PasswordEncryptionType' import { RoleNames } from '@enum/RoleNames' import { UserContactType } from '@enum/UserContactType' import { ContributionLink } from '@model/ContributionLink' +import { Location } from '@model/Location' import { testEnvironment, headerPushMock, resetToken, cleanDB } from '@test/helpers' import { logger, i18n as localization } from '@test/testSetup' @@ -1237,6 +1238,9 @@ describe('UserResolver', () => { firstName: 'Benjamin', lastName: 'Blümchen', language: 'en', + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }), ]) }) @@ -1275,6 +1279,93 @@ describe('UserResolver', () => { await expect(User.find()).resolves.toEqual([ expect.objectContaining({ alias: 'bibi_Bloxberg', + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, + }), + ]) + }) + }) + }) + + describe('gms attributes', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + describe('default settings', () => { + it('updates the user in DB', async () => { + await mutate({ + mutation: updateUserInfos, + variables: { + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, + }, + }) + await expect(User.find()).resolves.toEqual([ + expect.objectContaining({ + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, + }), + ]) + }) + }) + + describe('individual settings', () => { + it('updates the user in DB', async () => { + await mutate({ + mutation: updateUserInfos, + variables: { + gmsAllowed: false, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_FIRST_INITIAL, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_APPROXIMATE, + }, + }) + await expect(User.find()).resolves.toEqual([ + expect.objectContaining({ + gmsAllowed: false, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_FIRST_INITIAL, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_APPROXIMATE, + }), + ]) + }) + }) + + describe.only('with gms location', () => { + const loc = new Location() + loc.longitude = 9.573224 + loc.latitude = 49.679437 + console.log('with gms location:', loc) + it('updates the user in DB', async () => { + const usr = await User.find() + console.log('usr=', usr) + await mutate({ + mutation: updateUserInfos, + variables: { + /* + firstName: usr[0].firstName, + lastName: usr[0].lastName, + alias: usr[0].alias, + language: usr[0].language, + password: usr[0].password, + passwordNew: usr[0].password, + hideAmountGDD: usr[0].hideAmountGDD, + hideAmountGDT: usr[0].hideAmountGDT, + */ + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsLocation: loc, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, + }, + }) + await expect(User.find()).resolves.toEqual([ + expect.objectContaining({ + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + location: loc, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }), ]) }) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index fa00a4d57..757ddd5d3 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -24,6 +24,7 @@ import { Order } from '@enum/Order' import { PasswordEncryptionType } from '@enum/PasswordEncryptionType' import { UserContactType } from '@enum/UserContactType' import { SearchAdminUsersResult } from '@model/AdminUser' +import { Location } from '@model/Location' import { User } from '@model/User' import { UserAdmin, SearchUsersResult } from '@model/UserAdmin' @@ -73,6 +74,7 @@ import { getKlicktippState } from './util/getKlicktippState' import { setUserRole, deleteUserRole } from './util/modifyUserRole' import { sendUserToGms } from './util/sendUserToGms' import { validateAlias } from './util/validateAlias' +import { Location2Point } from './util/Location2Point' const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl'] const DEFAULT_LANGUAGE = 'de' @@ -554,75 +556,88 @@ export class UserResolver { }: UpdateUserInfosArgs, @Ctx() context: Context, ): Promise { + console.log( + `updateUserInfos(${firstName}, ${lastName}, ${alias}, ${language}, ${password}, ${passwordNew}, ${hideAmountGDD}, ${hideAmountGDT}, ${gmsAllowed}, ${gmsPublishName}, ${gmsLocation}, ${gmsPublishLocation})`, + ) logger.info( - `updateUserInfos(${firstName}, ${lastName}, ${language}, ***, ***, ${gmsAllowed}, ${gmsPublishName}, ${gmsLocation}, ${gmsPublishLocation})...`, + `updateUserInfos(${firstName}, ${lastName}, ${alias}, ${language}, ***, ***, ${hideAmountGDD}, ${hideAmountGDT}, ${gmsAllowed}, ${gmsPublishName}, ${gmsLocation}, ${gmsPublishLocation})...`, ) const user = getUser(context) - - if (firstName) { - user.firstName = firstName - } - - if (lastName) { - user.lastName = lastName - } - - if (alias && (await validateAlias(alias))) { - user.alias = alias - } - - if (language) { - if (!isLanguage(language)) { - throw new LogError('Given language is not a valid language', language) - } - user.language = language - i18n.setLocale(language) - } - - if (password && passwordNew) { - // Validate Password - if (!isValidPassword(passwordNew)) { - throw new LogError( - 'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!', - ) + console.log('getUser:', user) + try { + if (firstName) { + user.firstName = firstName } - if (!verifyPassword(user, password)) { - throw new LogError(`Old password is invalid`) + if (lastName) { + user.lastName = lastName } - // Save new password hash and newly encrypted private key - user.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID - user.password = encryptPassword(user, passwordNew) - } + if (alias && (await validateAlias(alias))) { + user.alias = alias + } - // Save hideAmountGDD value - if (hideAmountGDD !== undefined) { - user.hideAmountGDD = hideAmountGDD - } - // Save hideAmountGDT value - if (hideAmountGDT !== undefined) { - user.hideAmountGDT = hideAmountGDT - } + if (language) { + if (!isLanguage(language)) { + throw new LogError('Given language is not a valid language', language) + } + user.language = language + i18n.setLocale(language) + } - user.gmsAllowed = gmsAllowed - user.gmsPublishName = gmsPublishName - if (gmsLocation) { - user.location = gmsLocation.getPoint() - } - user.gmsPublishLocation = gmsPublishLocation + if (password && passwordNew) { + // Validate Password + if (!isValidPassword(passwordNew)) { + throw new LogError( + 'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!', + ) + } + if (!verifyPassword(user, password)) { + throw new LogError(`Old password is invalid`) + } + + // Save new password hash and newly encrypted private key + user.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID + user.password = encryptPassword(user, passwordNew) + } + + // Save hideAmountGDD value + if (hideAmountGDD !== undefined) { + user.hideAmountGDD = hideAmountGDD + } + // Save hideAmountGDT value + if (hideAmountGDT !== undefined) { + user.hideAmountGDT = hideAmountGDT + } + + console.log('gmsAllowed:', user.gmsAllowed, gmsAllowed) + user.gmsAllowed = gmsAllowed + console.log('gmsPublishName:', user.gmsPublishName, gmsPublishName) + user.gmsPublishName = gmsPublishName + if (gmsLocation) { + console.log('1. gmsLocation:', user.location, gmsLocation) + user.location = Location2Point(gmsLocation) + console.log('2. gmsLocation:', user.location) + } + console.log('gmsPublishLocation:', user.gmsPublishLocation, gmsPublishLocation) + user.gmsPublishLocation = gmsPublishLocation + console.log('vor commit user:', user) + } catch (err) { + console.log('error:', err) + } const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction('REPEATABLE READ') try { await queryRunner.manager.save(user).catch((error) => { + console.log('Error on saving user:', error) throw new LogError('Error saving user', error) }) await queryRunner.commitTransaction() - logger.debug('writing User data successful...') + logger.debug('writing User data successful...', user) } catch (e) { await queryRunner.rollbackTransaction() throw new LogError('Error on writing updated user data', e) diff --git a/backend/src/graphql/validator/Location.ts b/backend/src/graphql/validator/Location.ts new file mode 100644 index 000000000..1333a626e --- /dev/null +++ b/backend/src/graphql/validator/Location.ts @@ -0,0 +1,31 @@ +import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator' + +import { Location } from '@model/Location' + +import { Location2Point } from '@/graphql/resolver/util/Location2Point' + +export function isValidLocation(validationOptions?: ValidationOptions) { + // eslint-disable-next-line @typescript-eslint/ban-types + return function (object: Object, propertyName: string) { + registerDecorator({ + name: 'isValidLocation', + target: object.constructor, + propertyName, + options: validationOptions, + validator: { + validate(value: Location) { + // console.log('isValidLocation:', value, value.getPoint()) + if (!value || Location2Point(value).type === 'Point') { + console.log('isValidLocation: true') + return true + } + console.log('isValidLocation: false') + return false + }, + defaultMessage(args: ValidationArguments) { + return `${propertyName} must be a valid Location, ${args.property}` + }, + }, + }) + } +} From b2d2501f8976c8883420279090fd55f04b21ae42 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Tue, 23 Jan 2024 22:08:27 +0100 Subject: [PATCH 03/15] test for updateUserInfo with geolocation but with db-driver error --- .../graphql/resolver/util/Location2Point.ts | 32 +++++++++++++ backend/src/graphql/scalar/Geometry.ts | 33 ------------- backend/src/graphql/scalar/Location.ts | 46 +++++++++++++++++++ backend/src/graphql/scalar/Point.ts | 31 +++++++++++++ backend/src/graphql/schema.ts | 14 ++++-- 5 files changed, 119 insertions(+), 37 deletions(-) create mode 100644 backend/src/graphql/resolver/util/Location2Point.ts delete mode 100644 backend/src/graphql/scalar/Geometry.ts create mode 100644 backend/src/graphql/scalar/Location.ts create mode 100644 backend/src/graphql/scalar/Point.ts diff --git a/backend/src/graphql/resolver/util/Location2Point.ts b/backend/src/graphql/resolver/util/Location2Point.ts new file mode 100644 index 000000000..c3849821d --- /dev/null +++ b/backend/src/graphql/resolver/util/Location2Point.ts @@ -0,0 +1,32 @@ +import { Point } from '@dbTools/typeorm' + +import { Location } from '@model/Location' + +export function Location2Point(location: Location): Point { + console.log('in Location2Point:', location) + let pointStr: string + if (location.longitude && location.latitude) { + pointStr = '{ "type": "Point", "coordinates": [' + .concat(location.longitude?.toString()) + .concat(', ') + .concat(location.latitude?.toString()) + .concat('] }') + } else { + pointStr = '{ "type": "Point", "coordinates": [] }' + } + console.log('pointStr:', pointStr) + const point = JSON.parse(pointStr) as Point + console.log('point:', point) + return point +} + +export function Point2Location(point: Point): Location { + console.log('in Point2Location:', point) + const location = new Location() + if (point.type === 'Point' && point.coordinates.length === 2) { + location.longitude = point.coordinates[0] + location.latitude = point.coordinates[1] + } + console.log('location:', location) + return location +} diff --git a/backend/src/graphql/scalar/Geometry.ts b/backend/src/graphql/scalar/Geometry.ts deleted file mode 100644 index c2cd48fdb..000000000 --- a/backend/src/graphql/scalar/Geometry.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -import { Geometry as DbGeometry } from '@dbTools/typeorm' -import { GraphQLScalarType, Kind } from 'graphql' - -import { Location } from '@model/Location' - -export const GeometryScalar = new GraphQLScalarType({ - name: 'Geometry', - description: - 'The `Geometry` scalar type to represent longitude and latitude values of a geo location', - - serialize(value: DbGeometry): Location { - // Check type of value - if (value.type !== 'Point') { - throw new Error(`GeometryScalar can only serialize Geometry type 'Point' values`) - } - - return new Location(value.coordinates[0], value.coordinates[1]) - }, - - parseValue(value): DbGeometry { - const geometry: DbGeometry = JSON.parse(value) as DbGeometry - return geometry - }, - - parseLiteral(ast) { - if (ast.kind !== Kind.STRING) { - throw new TypeError(`${String(ast)} is not a valid Geometry value.`) - } - - return JSON.parse(ast.value) as DbGeometry - }, -}) diff --git a/backend/src/graphql/scalar/Location.ts b/backend/src/graphql/scalar/Location.ts new file mode 100644 index 000000000..6002ee25b --- /dev/null +++ b/backend/src/graphql/scalar/Location.ts @@ -0,0 +1,46 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { GraphQLScalarType, Kind } from 'graphql' + +import { Location } from '@model/Location' + +export const LocationScalar = new GraphQLScalarType({ + name: 'Location', + description: + 'The `Location` scalar type to represent longitude and latitude values of a geo location', + + serialize(value: Location) { + console.log('serialize LocationScalar:', value) + return value + }, + + parseValue(value): Location { + console.log('parseValue LocationScalar:', value) + try { + const loc = new Location() + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment + loc.longitude = value.longitude + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment + loc.latitude = value.latitude + console.log('parsed:', loc) + return loc + } catch (err) { + console.log('Error:', err) + } + return new Location() + }, + + parseLiteral(ast): Location { + console.log('parseLiteral LocationScalar:', ast) + if (ast.kind !== Kind.STRING) { + throw new TypeError(`${String(ast)} is not a valid Location value.`) + } + let loc = new Location() + try { + loc = JSON.parse(ast.value) as Location + console.log('parsed:', loc) + } catch (err) { + console.log('Error:', err) + } + return loc + }, +}) diff --git a/backend/src/graphql/scalar/Point.ts b/backend/src/graphql/scalar/Point.ts new file mode 100644 index 000000000..06af56bfc --- /dev/null +++ b/backend/src/graphql/scalar/Point.ts @@ -0,0 +1,31 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { Point as DbPoint } from '@dbTools/typeorm' +import { GraphQLScalarType, Kind } from 'graphql' + +export const PointScalar = new GraphQLScalarType({ + name: 'Point', + description: + 'The `Point` scalar type to represent longitude and latitude values of a geo location', + + serialize(value: DbPoint) { + // Check type of value + if (value.type !== 'Point') { + throw new Error(`PointScalar can only serialize Geometry type 'Point' values`) + } + return value + }, + + parseValue(value): DbPoint { + const point = JSON.parse(value) as DbPoint + return point + }, + + parseLiteral(ast) { + if (ast.kind !== Kind.STRING) { + throw new TypeError(`${String(ast)} is not a valid Geometry value.`) + } + + const point = JSON.parse(ast.value) as DbPoint + return point + }, +}) diff --git a/backend/src/graphql/schema.ts b/backend/src/graphql/schema.ts index 18214861f..856844c94 100644 --- a/backend/src/graphql/schema.ts +++ b/backend/src/graphql/schema.ts @@ -4,21 +4,27 @@ import { Decimal } from 'decimal.js-light' import { GraphQLSchema } from 'graphql' import { buildSchema } from 'type-graphql' +import { Location } from '@model/Location' + import { isAuthorized } from './directive/isAuthorized' import { DecimalScalar } from './scalar/Decimal' +import { LocationScalar } from './scalar/Location' export const schema = async (): Promise => { return buildSchema({ resolvers: [path.join(__dirname, 'resolver', `!(*.test).{js,ts}`)], authChecker: isAuthorized, - scalarsMap: [{ type: Decimal, scalar: DecimalScalar }], + scalarsMap: [ + { type: Decimal, scalar: DecimalScalar }, + { type: Location, scalar: LocationScalar }, + ], validate: { validationError: { target: false }, skipMissingProperties: true, skipNullProperties: true, - skipUndefinedProperties: false, - forbidUnknownValues: true, - stopAtFirstError: true, + skipUndefinedProperties: true, + forbidUnknownValues: false, + stopAtFirstError: false, }, }) } From 7c0bc5ae357b4072ab273b763900af70a82846d7 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 25 Jan 2024 02:11:22 +0100 Subject: [PATCH 04/15] add GeometryTransformer and configure db-connection --- backend/src/typeorm/connection.ts | 1 + backend/yarn.lock | 14 ++++++++ .../0081-introduce_gms_registration/User.ts | 9 ++++- database/package.json | 5 ++- database/src/typeorm/GeometryTransformer.ts | 34 +++++++++++++++++++ database/yarn.lock | 17 ++++++++++ 6 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 database/src/typeorm/GeometryTransformer.ts diff --git a/backend/src/typeorm/connection.ts b/backend/src/typeorm/connection.ts index 3c8307478..104f6449d 100644 --- a/backend/src/typeorm/connection.ts +++ b/backend/src/typeorm/connection.ts @@ -30,6 +30,7 @@ export class Connection { Connection.instance = await createConnection({ name: 'default', type: 'mysql', + legacySpatialSupport: false, host: CONFIG.DB_HOST, port: CONFIG.DB_PORT, username: CONFIG.DB_USER, diff --git a/backend/yarn.lock b/backend/yarn.lock index 234dc817a..91186187b 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -3483,6 +3483,11 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== +geojson@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/geojson/-/geojson-0.5.0.tgz#3cd6c96399be65b56ee55596116fe9191ce701c0" + integrity sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ== + get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -3698,11 +3703,13 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: crypto "^1.0.1" decimal.js-light "^2.5.1" dotenv "^10.0.0" + geojson "^0.5.0" mysql2 "^2.3.0" reflect-metadata "^0.1.13" ts-mysql-migrate "^1.0.2" typeorm "^0.3.16" uuid "^8.3.2" + wkx "^0.5.0" grapheme-splitter@^1.0.4: version "1.0.4" @@ -7301,6 +7308,13 @@ with@^7.0.0: assert-never "^1.2.1" babel-walk "3.0.0-canary-5" +wkx@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.5.0.tgz#c6c37019acf40e517cc6b94657a25a3d4aa33e8c" + integrity sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg== + dependencies: + "@types/node" "*" + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" diff --git a/database/entity/0081-introduce_gms_registration/User.ts b/database/entity/0081-introduce_gms_registration/User.ts index f91129f31..cdbd36441 100644 --- a/database/entity/0081-introduce_gms_registration/User.ts +++ b/database/entity/0081-introduce_gms_registration/User.ts @@ -13,6 +13,7 @@ import { Contribution } from '../Contribution' import { ContributionMessage } from '../ContributionMessage' import { UserContact } from '../UserContact' import { UserRole } from '../UserRole' +import { GeometryTransformer } from '../../src/typeorm/GeometryTransformer' @Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class User extends BaseEntity { @@ -125,7 +126,13 @@ export class User extends BaseEntity { @Column({ name: 'gms_allowed', type: 'bool', default: true }) gmsAllowed: boolean - @Column({ name: 'location', type: 'geometry', default: null, nullable: true }) + @Column({ + name: 'location', + type: 'geometry', + default: null, + nullable: true, + transformer: GeometryTransformer, + }) location: Geometry | null @Column({ diff --git a/database/package.json b/database/package.json index 8e1a99826..d1a9a2059 100644 --- a/database/package.json +++ b/database/package.json @@ -21,6 +21,7 @@ "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "^3.2.1", "@types/faker": "^5.5.9", + "@types/geojson": "^7946.0.13", "@types/node": "^16.10.3", "@typescript-eslint/eslint-plugin": "^5.57.1", "@typescript-eslint/parser": "^5.57.1", @@ -45,11 +46,13 @@ "crypto": "^1.0.1", "decimal.js-light": "^2.5.1", "dotenv": "^10.0.0", + "geojson": "^0.5.0", "mysql2": "^2.3.0", "reflect-metadata": "^0.1.13", "ts-mysql-migrate": "^1.0.2", "typeorm": "^0.3.16", - "uuid": "^8.3.2" + "uuid": "^8.3.2", + "wkx": "^0.5.0" }, "engines": { "node": ">=14" diff --git a/database/src/typeorm/GeometryTransformer.ts b/database/src/typeorm/GeometryTransformer.ts new file mode 100644 index 000000000..7e73d02a5 --- /dev/null +++ b/database/src/typeorm/GeometryTransformer.ts @@ -0,0 +1,34 @@ +/* eslint-disable camelcase */ +import { Geometry as wkx_Geometry } from 'wkx' +import { Geometry } from 'geojson' +import { ValueTransformer } from 'typeorm/decorator/options/ValueTransformer' + +/** + * TypeORM transformer to convert GeoJSON to MySQL WKT (Well Known Text) e.g. POINT(LAT, LON) and back + */ +export const GeometryTransformer: ValueTransformer = { + to: (geojson: Geometry): string | null => { + console.log('GeometryTransformer to: geojson=', geojson) + if (geojson) { + const wkxg = wkx_Geometry.parseGeoJSON(geojson) + console.log('GeometryTransformer to: wkxg=', wkxg) + const str = wkxg.toWkt() + console.log('GeometryTransformer to: str=', str) + return str + } + return null + }, + + from: (wkb: string): Record | null => { + // wkb ? wkx_Geometry.parse(wkb).toGeoJSON() : undefined + console.log('GeometryTransformer from: wbk=', wkb) + if (!wkb) { + return null + } + const record = wkx_Geometry.parse(wkb) + console.log('GeometryTransformer from: record=', record) + const str = record.toGeoJSON() + console.log('GeometryTransformer from: str=', str) + return str + }, +} diff --git a/database/yarn.lock b/database/yarn.lock index d8a0d6ffb..fd5598693 100644 --- a/database/yarn.lock +++ b/database/yarn.lock @@ -143,6 +143,11 @@ resolved "https://registry.yarnpkg.com/@types/faker/-/faker-5.5.9.tgz#588ede92186dc557bff8341d294335d50d255f0c" integrity sha512-uCx6mP3UY5SIO14XlspxsGjgaemrxpssJI0Ol+GfhxtcKpv9pgRZYsS4eeKeHVLje6Qtc8lGszuBI461+gVZBA== +"@types/geojson@^7946.0.13": + version "7946.0.13" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.13.tgz#e6e77ea9ecf36564980a861e24e62a095988775e" + integrity sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ== + "@types/json-schema@^7.0.9": version "7.0.12" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" @@ -1132,6 +1137,11 @@ generate-function@^2.3.1: dependencies: is-property "^1.0.2" +geojson@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/geojson/-/geojson-0.5.0.tgz#3cd6c96399be65b56ee55596116fe9191ce701c0" + integrity sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ== + get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -2502,6 +2512,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +wkx@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.5.0.tgz#c6c37019acf40e517cc6b94657a25a3d4aa33e8c" + integrity sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg== + dependencies: + "@types/node" "*" + word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" From 028337b29144828c1ff2418d0743759dc6661f26 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 7 Feb 2024 21:03:46 +0100 Subject: [PATCH 05/15] solve tests for updateUserInfo with gmsLocation --- .../resolver/TransactionResolver.test.ts | 5 + .../src/graphql/resolver/UserResolver.test.ts | 18 +-- backend/src/graphql/resolver/UserResolver.ts | 123 ++++++++---------- .../graphql/resolver/util/Location2Point.ts | 5 - backend/src/graphql/scalar/Location.ts | 12 +- backend/src/graphql/validator/Location.ts | 2 - database/src/typeorm/GeometryTransformer.ts | 6 - 7 files changed, 69 insertions(+), 102 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index d130a802e..6ac69f26e 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -15,6 +15,8 @@ import { ApolloServerTestClient } from 'apollo-server-testing' import { GraphQLError } from 'graphql' import { v4 as uuidv4 } from 'uuid' +import { GmsPublishLocationType } from '@enum/GmsPublishLocationType' +import { GmsPublishNameType } from '@enum/GmsPublishNameType' import { cleanDB, testEnvironment } from '@test/helpers' import { logger } from '@test/testSetup' @@ -523,6 +525,9 @@ describe('send coins', () => { mutation: updateUserInfos, variables: { alias: 'bob', + gmsAllowed: true, + gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }) await mutate({ diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index f58cb53b0..8011ba98e 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -71,6 +71,8 @@ import { stephenHawking } from '@/seeds/users/stephen-hawking' import { printTimeDuration } from '@/util/time' import { objectValuesToArray } from '@/util/utilities' +import { Location2Point } from './util/Location2Point' + jest.mock('@/emails/sendEmailVariants', () => { const originalModule = jest.requireActual('@/emails/sendEmailVariants') return { @@ -1333,27 +1335,15 @@ describe('UserResolver', () => { }) }) - describe.only('with gms location', () => { + describe('with gms location', () => { const loc = new Location() loc.longitude = 9.573224 loc.latitude = 49.679437 - console.log('with gms location:', loc) it('updates the user in DB', async () => { const usr = await User.find() - console.log('usr=', usr) await mutate({ mutation: updateUserInfos, variables: { - /* - firstName: usr[0].firstName, - lastName: usr[0].lastName, - alias: usr[0].alias, - language: usr[0].language, - password: usr[0].password, - passwordNew: usr[0].password, - hideAmountGDD: usr[0].hideAmountGDD, - hideAmountGDT: usr[0].hideAmountGDT, - */ gmsAllowed: true, gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, gmsLocation: loc, @@ -1364,7 +1354,7 @@ describe('UserResolver', () => { expect.objectContaining({ gmsAllowed: true, gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, - location: loc, + location: Location2Point(loc), gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }), ]) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 757ddd5d3..b8c106bf6 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -71,10 +71,10 @@ import { getUserCreations } from './util/creations' import { findUserByIdentifier } from './util/findUserByIdentifier' import { findUsers } from './util/findUsers' import { getKlicktippState } from './util/getKlicktippState' +import { Location2Point } from './util/Location2Point' import { setUserRole, deleteUserRole } from './util/modifyUserRole' import { sendUserToGms } from './util/sendUserToGms' import { validateAlias } from './util/validateAlias' -import { Location2Point } from './util/Location2Point' const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl'] const DEFAULT_LANGUAGE = 'de' @@ -556,83 +556,72 @@ export class UserResolver { }: UpdateUserInfosArgs, @Ctx() context: Context, ): Promise { - console.log( - `updateUserInfos(${firstName}, ${lastName}, ${alias}, ${language}, ${password}, ${passwordNew}, ${hideAmountGDD}, ${hideAmountGDT}, ${gmsAllowed}, ${gmsPublishName}, ${gmsLocation}, ${gmsPublishLocation})`, - ) logger.info( `updateUserInfos(${firstName}, ${lastName}, ${alias}, ${language}, ***, ***, ${hideAmountGDD}, ${hideAmountGDT}, ${gmsAllowed}, ${gmsPublishName}, ${gmsLocation}, ${gmsPublishLocation})...`, ) const user = getUser(context) - console.log('getUser:', user) - try { - if (firstName) { - user.firstName = firstName - } - - if (lastName) { - user.lastName = lastName - } - - if (alias && (await validateAlias(alias))) { - user.alias = alias - } - - if (language) { - if (!isLanguage(language)) { - throw new LogError('Given language is not a valid language', language) - } - user.language = language - i18n.setLocale(language) - } - - if (password && passwordNew) { - // Validate Password - if (!isValidPassword(passwordNew)) { - throw new LogError( - 'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!', - ) - } - - if (!verifyPassword(user, password)) { - throw new LogError(`Old password is invalid`) - } - - // Save new password hash and newly encrypted private key - user.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID - user.password = encryptPassword(user, passwordNew) - } - - // Save hideAmountGDD value - if (hideAmountGDD !== undefined) { - user.hideAmountGDD = hideAmountGDD - } - // Save hideAmountGDT value - if (hideAmountGDT !== undefined) { - user.hideAmountGDT = hideAmountGDT - } - - console.log('gmsAllowed:', user.gmsAllowed, gmsAllowed) - user.gmsAllowed = gmsAllowed - console.log('gmsPublishName:', user.gmsPublishName, gmsPublishName) - user.gmsPublishName = gmsPublishName - if (gmsLocation) { - console.log('1. gmsLocation:', user.location, gmsLocation) - user.location = Location2Point(gmsLocation) - console.log('2. gmsLocation:', user.location) - } - console.log('gmsPublishLocation:', user.gmsPublishLocation, gmsPublishLocation) - user.gmsPublishLocation = gmsPublishLocation - console.log('vor commit user:', user) - } catch (err) { - console.log('error:', err) + // try { + if (firstName) { + user.firstName = firstName } + + if (lastName) { + user.lastName = lastName + } + + if (alias && (await validateAlias(alias))) { + user.alias = alias + } + + if (language) { + if (!isLanguage(language)) { + throw new LogError('Given language is not a valid language', language) + } + user.language = language + i18n.setLocale(language) + } + + if (password && passwordNew) { + // Validate Password + if (!isValidPassword(passwordNew)) { + throw new LogError( + 'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!', + ) + } + + if (!verifyPassword(user, password)) { + throw new LogError(`Old password is invalid`) + } + + // Save new password hash and newly encrypted private key + user.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID + user.password = encryptPassword(user, passwordNew) + } + + // Save hideAmountGDD value + if (hideAmountGDD !== undefined) { + user.hideAmountGDD = hideAmountGDD + } + // Save hideAmountGDT value + if (hideAmountGDT !== undefined) { + user.hideAmountGDT = hideAmountGDT + } + + user.gmsAllowed = gmsAllowed + user.gmsPublishName = gmsPublishName + if (gmsLocation) { + user.location = Location2Point(gmsLocation) + } + user.gmsPublishLocation = gmsPublishLocation + // } catch (err) { + // console.log('error:', err) + // } const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction('REPEATABLE READ') try { await queryRunner.manager.save(user).catch((error) => { - console.log('Error on saving user:', error) throw new LogError('Error saving user', error) }) diff --git a/backend/src/graphql/resolver/util/Location2Point.ts b/backend/src/graphql/resolver/util/Location2Point.ts index c3849821d..df40c034a 100644 --- a/backend/src/graphql/resolver/util/Location2Point.ts +++ b/backend/src/graphql/resolver/util/Location2Point.ts @@ -3,7 +3,6 @@ import { Point } from '@dbTools/typeorm' import { Location } from '@model/Location' export function Location2Point(location: Location): Point { - console.log('in Location2Point:', location) let pointStr: string if (location.longitude && location.latitude) { pointStr = '{ "type": "Point", "coordinates": [' @@ -14,19 +13,15 @@ export function Location2Point(location: Location): Point { } else { pointStr = '{ "type": "Point", "coordinates": [] }' } - console.log('pointStr:', pointStr) const point = JSON.parse(pointStr) as Point - console.log('point:', point) return point } export function Point2Location(point: Point): Location { - console.log('in Point2Location:', point) const location = new Location() if (point.type === 'Point' && point.coordinates.length === 2) { location.longitude = point.coordinates[0] location.latitude = point.coordinates[1] } - console.log('location:', location) return location } diff --git a/backend/src/graphql/scalar/Location.ts b/backend/src/graphql/scalar/Location.ts index 6002ee25b..abac36742 100644 --- a/backend/src/graphql/scalar/Location.ts +++ b/backend/src/graphql/scalar/Location.ts @@ -2,6 +2,7 @@ import { GraphQLScalarType, Kind } from 'graphql' import { Location } from '@model/Location' +import { LogError } from '@/server/LogError' export const LocationScalar = new GraphQLScalarType({ name: 'Location', @@ -9,37 +10,32 @@ export const LocationScalar = new GraphQLScalarType({ 'The `Location` scalar type to represent longitude and latitude values of a geo location', serialize(value: Location) { - console.log('serialize LocationScalar:', value) return value }, parseValue(value): Location { - console.log('parseValue LocationScalar:', value) try { const loc = new Location() // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment loc.longitude = value.longitude // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment loc.latitude = value.latitude - console.log('parsed:', loc) return loc } catch (err) { - console.log('Error:', err) + throw new LogError('Error:', err) } - return new Location() + // return new Location() }, parseLiteral(ast): Location { - console.log('parseLiteral LocationScalar:', ast) if (ast.kind !== Kind.STRING) { throw new TypeError(`${String(ast)} is not a valid Location value.`) } let loc = new Location() try { loc = JSON.parse(ast.value) as Location - console.log('parsed:', loc) } catch (err) { - console.log('Error:', err) + throw new LogError('Error:', err) } return loc }, diff --git a/backend/src/graphql/validator/Location.ts b/backend/src/graphql/validator/Location.ts index 1333a626e..f1f23cd81 100644 --- a/backend/src/graphql/validator/Location.ts +++ b/backend/src/graphql/validator/Location.ts @@ -16,10 +16,8 @@ export function isValidLocation(validationOptions?: ValidationOptions) { validate(value: Location) { // console.log('isValidLocation:', value, value.getPoint()) if (!value || Location2Point(value).type === 'Point') { - console.log('isValidLocation: true') return true } - console.log('isValidLocation: false') return false }, defaultMessage(args: ValidationArguments) { diff --git a/database/src/typeorm/GeometryTransformer.ts b/database/src/typeorm/GeometryTransformer.ts index 7e73d02a5..3598c493f 100644 --- a/database/src/typeorm/GeometryTransformer.ts +++ b/database/src/typeorm/GeometryTransformer.ts @@ -8,12 +8,9 @@ import { ValueTransformer } from 'typeorm/decorator/options/ValueTransformer' */ export const GeometryTransformer: ValueTransformer = { to: (geojson: Geometry): string | null => { - console.log('GeometryTransformer to: geojson=', geojson) if (geojson) { const wkxg = wkx_Geometry.parseGeoJSON(geojson) - console.log('GeometryTransformer to: wkxg=', wkxg) const str = wkxg.toWkt() - console.log('GeometryTransformer to: str=', str) return str } return null @@ -21,14 +18,11 @@ export const GeometryTransformer: ValueTransformer = { from: (wkb: string): Record | null => { // wkb ? wkx_Geometry.parse(wkb).toGeoJSON() : undefined - console.log('GeometryTransformer from: wbk=', wkb) if (!wkb) { return null } const record = wkx_Geometry.parse(wkb) - console.log('GeometryTransformer from: record=', record) const str = record.toGeoJSON() - console.log('GeometryTransformer from: str=', str) return str }, } From cff2517156578b09fd5c5e0ad2681d4e20856633 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 7 Feb 2024 23:13:02 +0100 Subject: [PATCH 06/15] linting --- backend/src/graphql/arg/UpdateUserInfosArgs.ts | 5 +---- backend/src/graphql/resolver/UserResolver.test.ts | 1 - backend/src/graphql/resolver/UserResolver.ts | 2 +- backend/src/graphql/scalar/Location.ts | 1 + backend/tsconfig.json | 1 + 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/backend/src/graphql/arg/UpdateUserInfosArgs.ts b/backend/src/graphql/arg/UpdateUserInfosArgs.ts index 40e63f488..f2244c999 100644 --- a/backend/src/graphql/arg/UpdateUserInfosArgs.ts +++ b/backend/src/graphql/arg/UpdateUserInfosArgs.ts @@ -2,10 +2,7 @@ import { IsBoolean, IsInt, IsString } from 'class-validator' import { ArgsType, Field, InputType, Int } from 'type-graphql' import { Location } from '@model/Location' - -import { isValidLocation } from '../validator/Location' - -// import { isValidLocation } from '../validator/Location' +import { isValidLocation } from '@validator/Location' @InputType() @ArgsType() diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 8011ba98e..a3f69bc4d 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -1340,7 +1340,6 @@ describe('UserResolver', () => { loc.longitude = 9.573224 loc.latitude = 49.679437 it('updates the user in DB', async () => { - const usr = await User.find() await mutate({ mutation: updateUserInfos, variables: { diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index b8c106bf6..6f8a39aa4 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -24,7 +24,7 @@ import { Order } from '@enum/Order' import { PasswordEncryptionType } from '@enum/PasswordEncryptionType' import { UserContactType } from '@enum/UserContactType' import { SearchAdminUsersResult } from '@model/AdminUser' -import { Location } from '@model/Location' +// import { Location } from '@model/Location' import { User } from '@model/User' import { UserAdmin, SearchUsersResult } from '@model/UserAdmin' diff --git a/backend/src/graphql/scalar/Location.ts b/backend/src/graphql/scalar/Location.ts index abac36742..8b475d7f6 100644 --- a/backend/src/graphql/scalar/Location.ts +++ b/backend/src/graphql/scalar/Location.ts @@ -2,6 +2,7 @@ import { GraphQLScalarType, Kind } from 'graphql' import { Location } from '@model/Location' + import { LogError } from '@/server/LogError' export const LocationScalar = new GraphQLScalarType({ diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 28ddf1c38..d04b5aaab 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -53,6 +53,7 @@ "@enum/*": ["src/graphql/enum/*"], "@model/*": ["src/graphql/model/*"], "@union/*": ["src/graphql/union/*"], + "@validator/*": ["src/graphql/validator/*"], "@repository/*": ["src/typeorm/repository/*"], "@typeorm/*": ["src/typeorm/*"], "@test/*": ["test/*"], From 3f2387cfa1a8d7ba99e7fdb8f06aa57d03d7f358 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 7 Feb 2024 23:35:27 +0100 Subject: [PATCH 07/15] linting --- backend/src/graphql/arg/UpdateUserInfosArgs.ts | 4 +++- backend/tsconfig.json | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/arg/UpdateUserInfosArgs.ts b/backend/src/graphql/arg/UpdateUserInfosArgs.ts index f2244c999..991680e29 100644 --- a/backend/src/graphql/arg/UpdateUserInfosArgs.ts +++ b/backend/src/graphql/arg/UpdateUserInfosArgs.ts @@ -1,8 +1,10 @@ + import { IsBoolean, IsInt, IsString } from 'class-validator' import { ArgsType, Field, InputType, Int } from 'type-graphql' import { Location } from '@model/Location' -import { isValidLocation } from '@validator/Location' + +import { isValidLocation } from '@/graphql/validator/Location' @InputType() @ArgsType() diff --git a/backend/tsconfig.json b/backend/tsconfig.json index d04b5aaab..28ddf1c38 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -53,7 +53,6 @@ "@enum/*": ["src/graphql/enum/*"], "@model/*": ["src/graphql/model/*"], "@union/*": ["src/graphql/union/*"], - "@validator/*": ["src/graphql/validator/*"], "@repository/*": ["src/typeorm/repository/*"], "@typeorm/*": ["src/typeorm/*"], "@test/*": ["test/*"], From d0370614e8892cc0e3f015a63adc60164ca5d590 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 7 Feb 2024 23:42:14 +0100 Subject: [PATCH 08/15] linting --- backend/src/graphql/arg/UpdateUserInfosArgs.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/graphql/arg/UpdateUserInfosArgs.ts b/backend/src/graphql/arg/UpdateUserInfosArgs.ts index 991680e29..cc61e8c36 100644 --- a/backend/src/graphql/arg/UpdateUserInfosArgs.ts +++ b/backend/src/graphql/arg/UpdateUserInfosArgs.ts @@ -1,4 +1,3 @@ - import { IsBoolean, IsInt, IsString } from 'class-validator' import { ArgsType, Field, InputType, Int } from 'type-graphql' From 29a62dadd50464038ca0e1e76c8b8db79dfed5ca Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 7 Feb 2024 23:55:30 +0100 Subject: [PATCH 09/15] adaptions after merge master --- .../Community.ts | 7 +++++++ .../User.ts | 6 ++++++ .../UserContact.ts | 0 database/entity/UserContact.ts | 2 +- ..._registration.ts => 0082-introduce_gms_registration.ts} | 0 5 files changed, 14 insertions(+), 1 deletion(-) rename database/entity/{0081-introduce_gms_registration => 0082-introduce_gms_registration}/Community.ts (88%) rename database/entity/{0081-introduce_gms_registration => 0082-introduce_gms_registration}/User.ts (94%) rename database/entity/{0081-introduce_gms_registration => 0082-introduce_gms_registration}/UserContact.ts (100%) rename database/migrations/{0081-introduce_gms_registration.ts => 0082-introduce_gms_registration.ts} (100%) diff --git a/database/entity/0081-introduce_gms_registration/Community.ts b/database/entity/0082-introduce_gms_registration/Community.ts similarity index 88% rename from database/entity/0081-introduce_gms_registration/Community.ts rename to database/entity/0082-introduce_gms_registration/Community.ts index 89c31763f..cc5607e2e 100644 --- a/database/entity/0081-introduce_gms_registration/Community.ts +++ b/database/entity/0082-introduce_gms_registration/Community.ts @@ -5,7 +5,10 @@ import { Column, CreateDateColumn, UpdateDateColumn, + OneToMany, + JoinColumn, } from 'typeorm' +import { User } from '../User' @Entity('communities') export class Community extends BaseEntity { @@ -63,4 +66,8 @@ export class Community extends BaseEntity { nullable: true, }) updatedAt: Date | null + + @OneToMany(() => User, (user) => user.community) + @JoinColumn({ name: 'community_uuid', referencedColumnName: 'communityUuid' }) + users: User[] } diff --git a/database/entity/0081-introduce_gms_registration/User.ts b/database/entity/0082-introduce_gms_registration/User.ts similarity index 94% rename from database/entity/0081-introduce_gms_registration/User.ts rename to database/entity/0082-introduce_gms_registration/User.ts index cdbd36441..3dc0dccb6 100644 --- a/database/entity/0081-introduce_gms_registration/User.ts +++ b/database/entity/0082-introduce_gms_registration/User.ts @@ -8,12 +8,14 @@ import { JoinColumn, OneToOne, Geometry, + ManyToOne, } from 'typeorm' import { Contribution } from '../Contribution' import { ContributionMessage } from '../ContributionMessage' import { UserContact } from '../UserContact' import { UserRole } from '../UserRole' import { GeometryTransformer } from '../../src/typeorm/GeometryTransformer' +import { Community } from '../Community' @Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class User extends BaseEntity { @@ -40,6 +42,10 @@ export class User extends BaseEntity { }) communityUuid: string + @ManyToOne(() => Community, (community) => community.users) + @JoinColumn({ name: 'community_uuid', referencedColumnName: 'communityUuid' }) + community: Community | null + @Column({ name: 'alias', length: 20, diff --git a/database/entity/0081-introduce_gms_registration/UserContact.ts b/database/entity/0082-introduce_gms_registration/UserContact.ts similarity index 100% rename from database/entity/0081-introduce_gms_registration/UserContact.ts rename to database/entity/0082-introduce_gms_registration/UserContact.ts diff --git a/database/entity/UserContact.ts b/database/entity/UserContact.ts index c938f1312..e91e9a9d3 100644 --- a/database/entity/UserContact.ts +++ b/database/entity/UserContact.ts @@ -1 +1 @@ -export { UserContact } from './0081-introduce_gms_registration/UserContact' +export { UserContact } from './0082-introduce_gms_registration/UserContact' diff --git a/database/migrations/0081-introduce_gms_registration.ts b/database/migrations/0082-introduce_gms_registration.ts similarity index 100% rename from database/migrations/0081-introduce_gms_registration.ts rename to database/migrations/0082-introduce_gms_registration.ts From 491f4d2131259597beaeda8864db0126ac1d0894 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 8 Feb 2024 00:15:57 +0100 Subject: [PATCH 10/15] adaptions after merge master --- database/entity/Community.ts | 6 +----- database/entity/User.ts | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/database/entity/Community.ts b/database/entity/Community.ts index 13fbbcf00..3b48d5c29 100644 --- a/database/entity/Community.ts +++ b/database/entity/Community.ts @@ -1,5 +1 @@ -<<<<<<< HEAD -export { Community } from './0081-introduce_gms_registration/Community' -======= -export { Community } from './0081-user_join_community/Community' ->>>>>>> refs/remotes/origin/master +export { Community } from './0082-introduce_gms_registration/Community' diff --git a/database/entity/User.ts b/database/entity/User.ts index 0cde9b36f..e3f15113d 100644 --- a/database/entity/User.ts +++ b/database/entity/User.ts @@ -1,5 +1 @@ -<<<<<<< HEAD -export { User } from './0081-introduce_gms_registration/User' -======= -export { User } from './0081-user_join_community/User' ->>>>>>> refs/remotes/origin/master +export { User } from './0082-introduce_gms_registration/User' From f70ff4d74bccd7cdcb753e728a16d8e740cd5072 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 9 Feb 2024 23:32:42 +0100 Subject: [PATCH 11/15] solve merge conflicts --- database/entity/0082-introduce_gms_registration/User.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/database/entity/0082-introduce_gms_registration/User.ts b/database/entity/0082-introduce_gms_registration/User.ts index 46c7898a5..3dc0dccb6 100644 --- a/database/entity/0082-introduce_gms_registration/User.ts +++ b/database/entity/0082-introduce_gms_registration/User.ts @@ -14,7 +14,6 @@ import { Contribution } from '../Contribution' import { ContributionMessage } from '../ContributionMessage' import { UserContact } from '../UserContact' import { UserRole } from '../UserRole' -<<<<<<< HEAD import { GeometryTransformer } from '../../src/typeorm/GeometryTransformer' import { Community } from '../Community' From 62b38bfc75c8b7e1ba5afc56ee30dcdaf9198268 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 9 Feb 2024 23:41:46 +0100 Subject: [PATCH 12/15] rework review comment --- backend/src/graphql/schema.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/schema.ts b/backend/src/graphql/schema.ts index 856844c94..bcb8081a6 100644 --- a/backend/src/graphql/schema.ts +++ b/backend/src/graphql/schema.ts @@ -22,9 +22,9 @@ export const schema = async (): Promise => { validationError: { target: false }, skipMissingProperties: true, skipNullProperties: true, - skipUndefinedProperties: true, - forbidUnknownValues: false, - stopAtFirstError: false, + skipUndefinedProperties: false, + forbidUnknownValues: true, + stopAtFirstError: true, }, }) } From 0c1f6cde7081f840799003ab2d5825a3a2dc186a Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Mon, 12 Feb 2024 20:07:46 +0100 Subject: [PATCH 13/15] no mandatory parameters in updateUserInfos --- backend/src/graphql/arg/UpdateUserInfosArgs.ts | 12 ++++++------ backend/src/graphql/resolver/UserResolver.ts | 13 +++++++++++++ backend/src/seeds/graphql/mutations.ts | 6 +++--- frontend/src/graphql/mutations.js | 14 +++++++++++--- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/backend/src/graphql/arg/UpdateUserInfosArgs.ts b/backend/src/graphql/arg/UpdateUserInfosArgs.ts index cc61e8c36..0920fb3bc 100644 --- a/backend/src/graphql/arg/UpdateUserInfosArgs.ts +++ b/backend/src/graphql/arg/UpdateUserInfosArgs.ts @@ -44,19 +44,19 @@ export class UpdateUserInfosArgs { @IsBoolean() hideAmountGDT?: boolean - @Field({ nullable: false }) + @Field({ nullable: true, defaultValue: true }) @IsBoolean() - gmsAllowed: boolean + gmsAllowed?: boolean - @Field(() => Int, { nullable: false }) + @Field(() => Int, { nullable: true, defaultValue: 0 }) @IsInt() - gmsPublishName: number + gmsPublishName?: number | null @Field(() => Location, { nullable: true }) @isValidLocation() gmsLocation?: Location | null - @Field(() => Int, { nullable: false }) + @Field(() => Int, { nullable: true, defaultValue: 2 }) @IsInt() - gmsPublishLocation: number + gmsPublishLocation?: number | null } diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 8a4e8b7fc..3f70ce112 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -19,6 +19,8 @@ import { SearchUsersFilters } from '@arg/SearchUsersFilters' import { SetUserRoleArgs } from '@arg/SetUserRoleArgs' import { UnsecureLoginArgs } from '@arg/UnsecureLoginArgs' import { UpdateUserInfosArgs } from '@arg/UpdateUserInfosArgs' +import { GmsPublishLocationType } from '@enum/GmsPublishLocationType' +import { GmsPublishNameType } from '@enum/GmsPublishNameType' import { OptInType } from '@enum/OptInType' import { Order } from '@enum/Order' import { PasswordEncryptionType } from '@enum/PasswordEncryptionType' @@ -559,6 +561,17 @@ export class UserResolver { logger.info( `updateUserInfos(${firstName}, ${lastName}, ${alias}, ${language}, ***, ***, ${hideAmountGDD}, ${hideAmountGDT}, ${gmsAllowed}, ${gmsPublishName}, ${gmsLocation}, ${gmsPublishLocation})...`, ) + // check default arg settings + if (gmsAllowed === null || gmsAllowed === undefined) { + gmsAllowed = true + } + if (!gmsPublishName) { + gmsPublishName = GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS + } + if (!gmsPublishLocation) { + gmsPublishLocation = GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM + } + const user = getUser(context) // try { if (firstName) { diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index a658c84d7..b10bb4b4e 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -34,10 +34,10 @@ export const updateUserInfos = gql` $locale: String $hideAmountGDD: Boolean $hideAmountGDT: Boolean - $gmsAllowed: Boolean! - $gmsPublishName: Int! + $gmsAllowed: Boolean + $gmsPublishName: Int $gmsLocation: Location - $gmsPublishLocation: Int! + $gmsPublishLocation: Int ) { updateUserInfos( firstName: $firstName diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index b4f96179f..14e9a62bc 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -25,25 +25,33 @@ export const forgotPassword = gql` ` export const updateUserInfos = gql` - mutation( - $alias: String + mutation ( $firstName: String $lastName: String + $alias: String $password: String $passwordNew: String $locale: String $hideAmountGDD: Boolean $hideAmountGDT: Boolean + $gmsAllowed: Boolean + $gmsPublishName: Int + $gmsLocation: Location + $gmsPublishLocation: Int ) { updateUserInfos( - alias: $alias firstName: $firstName lastName: $lastName + alias: $alias password: $password passwordNew: $passwordNew language: $locale hideAmountGDD: $hideAmountGDD hideAmountGDT: $hideAmountGDT + gmsAllowed: $gmsAllowed + gmsPublishName: $gmsPublishName + gmsLocation: $gmsLocation + gmsPublishLocation: $gmsPublishLocation ) } ` From d6d8093850e3221b16ca8b0bc551e543912107b9 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Mon, 12 Feb 2024 20:42:24 +0100 Subject: [PATCH 14/15] remove unnecessary parameter settings --- .../src/graphql/resolver/UserResolver.test.ts | 36 ++----------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 9ef4155d9..c570dbd9f 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -1173,11 +1173,7 @@ describe('UserResolver', () => { await expect( mutate({ mutation: updateUserInfos, - variables: { - gmsAllowed: true, - gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, - gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, - }, + variables: {}, }), ).resolves.toEqual( expect.objectContaining({ @@ -1207,11 +1203,7 @@ describe('UserResolver', () => { await expect( mutate({ mutation: updateUserInfos, - variables: { - gmsAllowed: true, - gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, - gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, - }, + variables: {}, }), ).resolves.toEqual( expect.objectContaining({ @@ -1230,9 +1222,6 @@ describe('UserResolver', () => { firstName: 'Benjamin', lastName: 'Blümchen', locale: 'en', - gmsAllowed: true, - gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, - gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }) await expect(User.find()).resolves.toEqual([ @@ -1273,9 +1262,6 @@ describe('UserResolver', () => { mutation: updateUserInfos, variables: { alias: 'bibi_Bloxberg', - gmsAllowed: true, - gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, - gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }) await expect(User.find()).resolves.toEqual([ @@ -1299,11 +1285,7 @@ describe('UserResolver', () => { it('updates the user in DB', async () => { await mutate({ mutation: updateUserInfos, - variables: { - gmsAllowed: true, - gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, - gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, - }, + variables: {}, }) await expect(User.find()).resolves.toEqual([ expect.objectContaining({ @@ -1369,9 +1351,6 @@ describe('UserResolver', () => { mutation: updateUserInfos, variables: { locale: 'not-valid', - gmsAllowed: true, - gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, - gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }), ).resolves.toEqual( @@ -1396,9 +1375,6 @@ describe('UserResolver', () => { variables: { password: 'wrong password', passwordNew: 'Aa12345_', - gmsAllowed: true, - gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, - gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }), ).resolves.toEqual( @@ -1422,9 +1398,6 @@ describe('UserResolver', () => { variables: { password: 'Aa12345_', passwordNew: 'Aa12345', - gmsAllowed: true, - gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, - gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }), ).resolves.toEqual( @@ -1453,9 +1426,6 @@ describe('UserResolver', () => { variables: { password: 'Aa12345_', passwordNew: 'Bb12345_', - gmsAllowed: true, - gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS, - gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }, }), ).resolves.toEqual( From f0d5741964464328b116373e87c9d3e4c4dbccb9 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Mon, 12 Feb 2024 20:44:36 +0100 Subject: [PATCH 15/15] linting --- frontend/src/graphql/mutations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index 14e9a62bc..cade098da 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -25,7 +25,7 @@ export const forgotPassword = gql` ` export const updateUserInfos = gql` - mutation ( + mutation( $firstName: String $lastName: String $alias: String