diff --git a/backend/src/graphql/arg/UpdateUserInfosArgs.ts b/backend/src/graphql/arg/UpdateUserInfosArgs.ts index 6b2ab1032..0920fb3bc 100644 --- a/backend/src/graphql/arg/UpdateUserInfosArgs.ts +++ b/backend/src/graphql/arg/UpdateUserInfosArgs.ts @@ -1,6 +1,11 @@ import { IsBoolean, IsInt, IsString } from 'class-validator' -import { ArgsType, Field, Int } from 'type-graphql' +import { ArgsType, Field, InputType, Int } from 'type-graphql' +import { Location } from '@model/Location' + +import { isValidLocation } from '@/graphql/validator/Location' + +@InputType() @ArgsType() export class UpdateUserInfosArgs { @Field({ nullable: true }) @@ -38,4 +43,20 @@ export class UpdateUserInfosArgs { @Field({ nullable: true }) @IsBoolean() hideAmountGDT?: boolean + + @Field({ nullable: true, defaultValue: true }) + @IsBoolean() + gmsAllowed?: boolean + + @Field(() => Int, { nullable: true, defaultValue: 0 }) + @IsInt() + gmsPublishName?: number | null + + @Field(() => Location, { nullable: true }) + @isValidLocation() + gmsLocation?: Location | null + + @Field(() => Int, { nullable: true, defaultValue: 2 }) + @IsInt() + gmsPublishLocation?: number | null } diff --git a/backend/src/graphql/model/Location.ts b/backend/src/graphql/model/Location.ts new file mode 100644 index 000000000..2b4c720f4 --- /dev/null +++ b/backend/src/graphql/model/Location.ts @@ -0,0 +1,10 @@ +import { ArgsType, Field, Int } from 'type-graphql' + +@ArgsType() +export class Location { + @Field(() => Int) + longitude: number + + @Field(() => Int) + latitude: number +} diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 97e210dfa..1bb4a86f7 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' @@ -532,6 +534,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 9bb15c66e..c570dbd9f 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -16,11 +16,14 @@ 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' 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' @@ -68,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 { @@ -1165,7 +1170,12 @@ describe('UserResolver', () => { it('throws an error', async () => { jest.clearAllMocks() resetToken() - await expect(mutate({ mutation: updateUserInfos })).resolves.toEqual( + await expect( + mutate({ + mutation: updateUserInfos, + variables: {}, + }), + ).resolves.toEqual( expect.objectContaining({ errors: [new GraphQLError('401 Unauthorized')], }), @@ -1190,7 +1200,12 @@ describe('UserResolver', () => { }) it('returns true', async () => { - await expect(mutate({ mutation: updateUserInfos })).resolves.toEqual( + await expect( + mutate({ + mutation: updateUserInfos, + variables: {}, + }), + ).resolves.toEqual( expect.objectContaining({ data: { updateUserInfos: true, @@ -1214,6 +1229,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, }), ]) }) @@ -1249,6 +1267,76 @@ 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: {}, + }) + 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('with gms location', () => { + const loc = new Location() + loc.longitude = 9.573224 + loc.latitude = 49.679437 + it('updates the user in DB', async () => { + await mutate({ + mutation: updateUserInfos, + variables: { + 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: Location2Point(loc), + gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM, }), ]) }) @@ -2586,6 +2674,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 7be858d7a..3f70ce112 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -19,11 +19,14 @@ 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' 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' @@ -70,6 +73,7 @@ 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' @@ -547,12 +551,29 @@ export class UserResolver { passwordNew, hideAmountGDD, hideAmountGDT, + gmsAllowed, + gmsPublishName, + gmsLocation, + gmsPublishLocation, }: UpdateUserInfosArgs, @Ctx() context: Context, ): Promise { - logger.info(`updateUserInfos(${firstName}, ${lastName}, ${language}, ***, ***)...`) - const user = getUser(context) + 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) { user.firstName = firstName } @@ -599,6 +620,15 @@ export class UserResolver { 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') @@ -609,7 +639,7 @@ export class UserResolver { }) 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/resolver/util/Location2Point.ts b/backend/src/graphql/resolver/util/Location2Point.ts new file mode 100644 index 000000000..df40c034a --- /dev/null +++ b/backend/src/graphql/resolver/util/Location2Point.ts @@ -0,0 +1,27 @@ +import { Point } from '@dbTools/typeorm' + +import { Location } from '@model/Location' + +export function Location2Point(location: Location): Point { + 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": [] }' + } + const point = JSON.parse(pointStr) as Point + return point +} + +export function Point2Location(point: Point): Location { + const location = new Location() + if (point.type === 'Point' && point.coordinates.length === 2) { + location.longitude = point.coordinates[0] + location.latitude = point.coordinates[1] + } + return location +} diff --git a/backend/src/graphql/scalar/Location.ts b/backend/src/graphql/scalar/Location.ts new file mode 100644 index 000000000..8b475d7f6 --- /dev/null +++ b/backend/src/graphql/scalar/Location.ts @@ -0,0 +1,43 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { GraphQLScalarType, Kind } from 'graphql' + +import { Location } from '@model/Location' + +import { LogError } from '@/server/LogError' + +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) { + return value + }, + + parseValue(value): Location { + 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 + return loc + } catch (err) { + throw new LogError('Error:', err) + } + // return new Location() + }, + + parseLiteral(ast): Location { + 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 + } catch (err) { + throw new LogError('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..bcb8081a6 100644 --- a/backend/src/graphql/schema.ts +++ b/backend/src/graphql/schema.ts @@ -4,14 +4,20 @@ 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, diff --git a/backend/src/graphql/validator/Location.ts b/backend/src/graphql/validator/Location.ts new file mode 100644 index 000000000..f1f23cd81 --- /dev/null +++ b/backend/src/graphql/validator/Location.ts @@ -0,0 +1,29 @@ +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') { + return true + } + return false + }, + defaultMessage(args: ValidationArguments) { + return `${propertyName} must be a valid Location, ${args.property}` + }, + }, + }) + } +} diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 554cc4def..b10bb4b4e 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 ) } ` 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/0082-introduce_gms_registration/User.ts b/database/entity/0082-introduce_gms_registration/User.ts index e11842960..3dc0dccb6 100644 --- a/database/entity/0082-introduce_gms_registration/User.ts +++ b/database/entity/0082-introduce_gms_registration/User.ts @@ -14,6 +14,7 @@ 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' }) @@ -131,7 +132,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 2b31e033b..caeb917c4 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..3598c493f --- /dev/null +++ b/database/src/typeorm/GeometryTransformer.ts @@ -0,0 +1,28 @@ +/* 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 => { + if (geojson) { + const wkxg = wkx_Geometry.parseGeoJSON(geojson) + const str = wkxg.toWkt() + return str + } + return null + }, + + from: (wkb: string): Record | null => { + // wkb ? wkx_Geometry.parse(wkb).toGeoJSON() : undefined + if (!wkb) { + return null + } + const record = wkx_Geometry.parse(wkb) + const str = record.toGeoJSON() + 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" diff --git a/deployment/bare_metal/.env.dist b/deployment/bare_metal/.env.dist index 19d4651ec..9e6e911eb 100644 --- a/deployment/bare_metal/.env.dist +++ b/deployment/bare_metal/.env.dist @@ -113,13 +113,12 @@ NGINX_UPDATE_PAGE_ROOT=/home/gradido/gradido/deployment/bare_metal/nginx/update- #NGINX_SSL_CERTIFICATE_KEY=/etc/letsencrypt/live/gddhost.tld/privkey.pem NGINX_SSL_DHPARAM=/etc/letsencrypt/ssl-dhparams.pem NGINX_SSL_INCLUDE=/etc/letsencrypt/options-ssl-nginx.conf +NGINX_REWRITE_LEGACY_URLS=false # LEGACY -NGINX_REWRITE_LEGACY_URLS=false DEFAULT_PUBLISHER_ID=2896 WEBHOOK_ELOPAGE_SECRET=secret - # GMS #GMS_ACTIVE=true # Coordinates of Illuminz test instance diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index b4f96179f..cade098da 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -26,24 +26,32 @@ export const forgotPassword = gql` export const updateUserInfos = gql` mutation( - $alias: String $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 ) } `