mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
move findUserByIdentifier and validateAlias into database inclusive refactored tests for this
This commit is contained in:
parent
bd6e804a56
commit
aab6dcd98b
@ -137,19 +137,11 @@ describe('send coins', () => {
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('No user with this credentials')],
|
||||
errors: [new GraphQLError('The recipient user was not found')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'No user with this credentials',
|
||||
'wrong@email.com',
|
||||
homeCom.communityUuid,
|
||||
)
|
||||
})
|
||||
|
||||
describe('deleted recipient', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
@ -170,18 +162,10 @@ describe('send coins', () => {
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('No user with this credentials')],
|
||||
errors: [new GraphQLError('The recipient user was not found')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'No user with this credentials',
|
||||
'stephen@hawking.uk',
|
||||
homeCom.communityUuid,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('recipient account not activated', () => {
|
||||
@ -204,18 +188,10 @@ describe('send coins', () => {
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('No user with this credentials')],
|
||||
errors: [new GraphQLError('The recipient user was not found')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'No user with this credentials',
|
||||
'garrick@ollivander.com',
|
||||
homeCom.communityUuid,
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
Transaction as dbTransaction,
|
||||
TransactionLink as dbTransactionLink,
|
||||
User as dbUser,
|
||||
findUserByIdentifier
|
||||
} from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Args, Authorized, Ctx, Mutation, Query, Resolver } from 'type-graphql'
|
||||
@ -40,7 +41,6 @@ import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.'
|
||||
import { BalanceResolver } from './BalanceResolver'
|
||||
import { GdtResolver } from './GdtResolver'
|
||||
import { getCommunityByIdentifier, getCommunityName, isHomeCommunity } from './util/communities'
|
||||
import { findUserByIdentifier } from './util/findUserByIdentifier'
|
||||
import { getLastTransaction } from './util/getLastTransaction'
|
||||
import { getTransactionList } from './util/getTransactionList'
|
||||
import {
|
||||
|
||||
@ -68,7 +68,7 @@ import { printTimeDuration } from '@/util/time'
|
||||
import { objectValuesToArray } from '@/util/utilities'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.'
|
||||
import { Location2Point } from './util/Location2Point'
|
||||
|
||||
@ -698,9 +698,6 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
describe('no users in database', () => {
|
||||
beforeAll(() => {
|
||||
clearLogs()
|
||||
})
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
const result = await mutate({ mutation: login, variables })
|
||||
@ -712,7 +709,6 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
printLogs()
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
`findUserByEmail failed, user with email=${variables.email} not found`,
|
||||
)
|
||||
@ -2698,166 +2694,6 @@ describe('UserResolver', () => {
|
||||
expect(logErrorLogger.error).toBeCalledWith('401 Unauthorized')
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
const uuid = uuidv4()
|
||||
|
||||
beforeAll(async () => {
|
||||
user = await userFactory(testEnv, bibiBloxberg)
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
// first set alias to null, because updating alias isn't currently allowed
|
||||
await User.update({ alias: 'BBB' }, { alias: () => 'NULL' })
|
||||
await mutate({
|
||||
mutation: updateUserInfos,
|
||||
variables: {
|
||||
alias: 'bibi',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('identifier is no gradido ID, no email and no alias', () => {
|
||||
it('throws and logs "Unknown identifier type" error', async () => {
|
||||
await expect(
|
||||
query({
|
||||
query: userQuery,
|
||||
variables: {
|
||||
identifier: 'identifier_is_no_valid_alias!',
|
||||
communityIdentifier: homeCom1.communityUuid,
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Unknown identifier type')],
|
||||
}),
|
||||
)
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Unknown identifier type',
|
||||
'identifier_is_no_valid_alias!',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('identifier is not found', () => {
|
||||
it('throws and logs "No user found to given identifier" error', async () => {
|
||||
await expect(
|
||||
query({
|
||||
query: userQuery,
|
||||
variables: {
|
||||
identifier: uuid,
|
||||
communityIdentifier: homeCom1.communityUuid,
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('No user found to given identifier(s)')],
|
||||
}),
|
||||
)
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'No user found to given identifier(s)',
|
||||
uuid,
|
||||
homeCom1.communityUuid,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('identifier is found via email, but not matching community', () => {
|
||||
it('returns user', async () => {
|
||||
await expect(
|
||||
query({
|
||||
query: userQuery,
|
||||
variables: {
|
||||
identifier: 'bibi@bloxberg.de',
|
||||
communityIdentifier: foreignCom1.communityUuid,
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('No user with this credentials')],
|
||||
}),
|
||||
)
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'No user with this credentials',
|
||||
'bibi@bloxberg.de',
|
||||
foreignCom1.communityUuid,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('identifier is found via email', () => {
|
||||
it('returns user', async () => {
|
||||
await expect(
|
||||
query({
|
||||
query: userQuery,
|
||||
variables: {
|
||||
identifier: 'bibi@bloxberg.de',
|
||||
communityIdentifier: homeCom1.communityUuid,
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
}),
|
||||
},
|
||||
errors: undefined,
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('identifier is found via gradidoID', () => {
|
||||
it('returns user', async () => {
|
||||
await expect(
|
||||
query({
|
||||
query: userQuery,
|
||||
variables: {
|
||||
identifier: user.gradidoID,
|
||||
communityIdentifier: homeCom1.communityUuid,
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
}),
|
||||
},
|
||||
errors: undefined,
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('identifier is found via alias', () => {
|
||||
it('returns user', async () => {
|
||||
await expect(
|
||||
query({
|
||||
query: userQuery,
|
||||
variables: {
|
||||
identifier: 'bibi',
|
||||
communityIdentifier: homeCom1.communityUuid,
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
}),
|
||||
},
|
||||
errors: undefined,
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('check username', () => {
|
||||
|
||||
@ -6,6 +6,8 @@ import {
|
||||
UserContact as DbUserContact,
|
||||
ProjectBranding,
|
||||
UserLoggingView,
|
||||
getHomeCommunity,
|
||||
findUserByIdentifier
|
||||
} from 'database'
|
||||
import { GraphQLResolveInfo } from 'graphql'
|
||||
import i18n from 'i18n'
|
||||
@ -93,11 +95,9 @@ import { Logger, getLogger } from 'log4js'
|
||||
import { FULL_CREATION_AVAILABLE } from './const/const'
|
||||
import { Location2Point, Point2Location } from './util/Location2Point'
|
||||
import { authenticateGmsUserPlayground } from './util/authenticateGmsUserPlayground'
|
||||
import { getHomeCommunity } from 'database'
|
||||
import { compareGmsRelevantUserSettings } from './util/compareGmsRelevantUserSettings'
|
||||
import { getUserCreations } from './util/creations'
|
||||
import { extractGraphQLFieldsForSelect } from './util/extractGraphQLFields'
|
||||
import { findUserByIdentifier } from './util/findUserByIdentifier'
|
||||
import { findUsers } from './util/findUsers'
|
||||
import { getKlicktippState } from './util/getKlicktippState'
|
||||
import { deleteUserRole, setUserRole } from './util/modifyUserRole'
|
||||
@ -1153,8 +1153,11 @@ export class UserResolver {
|
||||
{ identifier, communityIdentifier }: UserArgs,
|
||||
): Promise<User> {
|
||||
const foundDbUser = await findUserByIdentifier(identifier, communityIdentifier)
|
||||
const modelUser = new User(foundDbUser)
|
||||
return modelUser
|
||||
if (!foundDbUser) {
|
||||
createLogger().debug('User not found', identifier, communityIdentifier)
|
||||
throw new Error('User not found')
|
||||
}
|
||||
return new User(foundDbUser)
|
||||
}
|
||||
|
||||
// FIELD RESOLVERS
|
||||
|
||||
@ -1,65 +0,0 @@
|
||||
import { isURL } from 'class-validator'
|
||||
import { Community, User as DbUser, UserContact as DbUserContact } from 'database'
|
||||
import { FindOptionsWhere } from 'typeorm'
|
||||
import { validate, version } from 'uuid'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { isEMail, isUUID4 } from '@/util/validate'
|
||||
|
||||
import { aliasSchema } from 'shared'
|
||||
|
||||
/**
|
||||
*
|
||||
* @param identifier could be gradidoID, alias or email of user
|
||||
* @param communityIdentifier could be uuid or name of community
|
||||
* @returns
|
||||
*/
|
||||
export const findUserByIdentifier = async (
|
||||
identifier: string,
|
||||
communityIdentifier: string,
|
||||
): Promise<DbUser> => {
|
||||
let user: DbUser | null
|
||||
const communityWhere: FindOptionsWhere<Community> = isURL(communityIdentifier)
|
||||
? { url: communityIdentifier }
|
||||
: isUUID4(communityIdentifier)
|
||||
? { communityUuid: communityIdentifier }
|
||||
: { name: communityIdentifier }
|
||||
|
||||
if (validate(identifier) && version(identifier) === 4) {
|
||||
user = await DbUser.findOne({
|
||||
where: { gradidoID: identifier, community: communityWhere },
|
||||
relations: ['emailContact', 'community'],
|
||||
})
|
||||
if (!user) {
|
||||
throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier)
|
||||
}
|
||||
} else if (isEMail(identifier)) {
|
||||
const userContact = await DbUserContact.findOne({
|
||||
where: {
|
||||
email: identifier,
|
||||
emailChecked: true,
|
||||
user: {
|
||||
community: communityWhere,
|
||||
},
|
||||
},
|
||||
relations: { user: { community: true } },
|
||||
})
|
||||
if (!userContact) {
|
||||
throw new LogError('No user with this credentials', identifier, communityIdentifier)
|
||||
}
|
||||
user = userContact.user
|
||||
user.emailContact = userContact
|
||||
} else if (aliasSchema.safeParse(identifier).success) {
|
||||
user = await DbUser.findOne({
|
||||
where: { alias: identifier, community: communityWhere },
|
||||
relations: ['emailContact', 'community'],
|
||||
})
|
||||
if (!user) {
|
||||
throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier)
|
||||
}
|
||||
} else {
|
||||
throw new LogError('Unknown identifier type', identifier)
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
@ -1,92 +0,0 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Community as DbCommunity, User as DbUser } from 'database'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
|
||||
import { writeHomeCommunityEntry } from '@/seeds/community'
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { bobBaumeister } from '@/seeds/users/bob-baumeister'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
|
||||
import { findUserByIdentifier } from './findUserByIdentifier'
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment()
|
||||
con = testEnv.con
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('graphql/resolver/util/findUserByIdentifier', () => {
|
||||
let homeCom: DbCommunity
|
||||
let communityUuid: string
|
||||
let communityName: string
|
||||
let userBibi: DbUser
|
||||
|
||||
beforeAll(async () => {
|
||||
homeCom = await writeHomeCommunityEntry()
|
||||
|
||||
communityUuid = homeCom.communityUuid!
|
||||
|
||||
communityName = homeCom.communityUuid!
|
||||
|
||||
userBibi = await userFactory(testEnv, bibiBloxberg)
|
||||
await userFactory(testEnv, peterLustig)
|
||||
await userFactory(testEnv, bobBaumeister)
|
||||
})
|
||||
|
||||
describe('communityIdentifier is community uuid', () => {
|
||||
it('userIdentifier is gradido id', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.gradidoID, communityUuid)
|
||||
user.userRoles = []
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
|
||||
it('userIdentifier is alias', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.alias, communityUuid)
|
||||
user.userRoles = []
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
|
||||
it('userIdentifier is email', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.emailContact.email, communityUuid)
|
||||
user.userRoles = []
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
})
|
||||
|
||||
describe('communityIdentifier is community name', () => {
|
||||
it('userIdentifier is gradido id', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.gradidoID, communityName)
|
||||
user.userRoles = []
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
|
||||
it('userIdentifier is alias', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.alias, communityName)
|
||||
user.userRoles = []
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
|
||||
it('userIdentifier is email', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.emailContact.email, communityName)
|
||||
user.userRoles = []
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
})
|
||||
})
|
||||
14
bun.lock
14
bun.lock
@ -181,7 +181,7 @@
|
||||
"name": "core",
|
||||
"version": "2.6.0",
|
||||
"dependencies": {
|
||||
"database": "workspace:*",
|
||||
"database": "*",
|
||||
"esbuild": "^0.25.2",
|
||||
"log4js": "^6.9.1",
|
||||
"zod": "^3.25.61",
|
||||
@ -224,6 +224,7 @@
|
||||
"@types/geojson": "^7946.0.13",
|
||||
"@types/jest": "27.0.2",
|
||||
"@types/node": "^18.7.14",
|
||||
"crypto-random-bigint": "^2.1.1",
|
||||
"jest": "27.2.4",
|
||||
"ts-jest": "27.0.5",
|
||||
"ts-node": "^10.9.2",
|
||||
@ -251,8 +252,8 @@
|
||||
"@types/joi": "^17.2.3",
|
||||
"@types/node": "^17.0.45",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"config-schema": "workspace:*",
|
||||
"database": "workspace:*",
|
||||
"config-schema": "*",
|
||||
"database": "*",
|
||||
"dotenv": "10.0.0",
|
||||
"esbuild": "^0.25.3",
|
||||
"jest": "27.5.1",
|
||||
@ -427,6 +428,7 @@
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@types/node": "^17.0.21",
|
||||
"typescript": "^4.9.5",
|
||||
"uuid": "^8.3.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -1605,6 +1607,8 @@
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"crypto-random-bigint": ["crypto-random-bigint@2.1.1", "", { "dependencies": { "uint-rng": "^1.2.1" } }, "sha512-96+FDrenXybkpnLML/60S8NcG32KgJ5Y8yvNNCYPW02r+ssoXFR5XKenuIQcHLWumnGj8UPqUUHBzXNrDGkDmQ=="],
|
||||
|
||||
"css-functions-list": ["css-functions-list@3.2.3", "", {}, "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA=="],
|
||||
|
||||
"css-select": ["css-select@4.3.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" } }, "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ=="],
|
||||
@ -3099,6 +3103,8 @@
|
||||
|
||||
"tiny-case": ["tiny-case@1.0.3", "", {}, "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="],
|
||||
|
||||
"tiny-webcrypto": ["tiny-webcrypto@1.0.3", "", {}, "sha512-LQQdNMAgz9BXNT2SKbYh3eCb+fLV0p7JB7MwUjzY6IOlQLGIadfnFqRpshERsS5Dl2OM/hs0+4I/XmSrF+RBbw=="],
|
||||
|
||||
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
|
||||
|
||||
"tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
|
||||
@ -3209,6 +3215,8 @@
|
||||
|
||||
"uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="],
|
||||
|
||||
"uint-rng": ["uint-rng@1.2.1", "", { "dependencies": { "tiny-webcrypto": "^1.0.2" } }, "sha512-swhDg5H+3DX2sIvnYA7VMBMXV/t8mPxvh49CjCDkwFmj/3OZIDOQwJANBgM1MPSUBrUHNIlXmU7/GcL7m4907g=="],
|
||||
|
||||
"uint8array-extras": ["uint8array-extras@1.4.0", "", {}, "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ=="],
|
||||
|
||||
"unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
|
||||
|
||||
106
config-schema/test/testSetup.vitest.ts
Normal file
106
config-schema/test/testSetup.vitest.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import { vi } from 'vitest'
|
||||
/*
|
||||
* This file is used to mock the log4js logger in the tests.
|
||||
* It is used to collect all log entries in the logs array.
|
||||
* If you want to debug your test, you can use `printLogs()` to print all log entries collected through the tests.
|
||||
* To have only the relevant logs, call `clearLogs()` before your calling the methods you like to test and `printLogs()` after.
|
||||
*/
|
||||
|
||||
|
||||
type LogEntry = {
|
||||
level: string;
|
||||
message: string;
|
||||
logger: string;
|
||||
context: string;
|
||||
additional: any[];
|
||||
}
|
||||
|
||||
const loggers: { [key: string]: any } = {}
|
||||
const logs: LogEntry[] = []
|
||||
|
||||
function addLog(level: string, message: string, logger: string, context: Map<string, string>, additional: any[]) {
|
||||
logs.push({
|
||||
level,
|
||||
context: [...context.entries()].map(([key, value]) => `${key}=${value}`).join(' ').trimEnd(),
|
||||
message,
|
||||
logger,
|
||||
additional
|
||||
})
|
||||
}
|
||||
|
||||
export function printLogs() {
|
||||
for (const log of logs) {
|
||||
const messages = []
|
||||
messages.push(log.message)
|
||||
messages.push(log.additional.map((d) => {
|
||||
if (typeof d === 'object' && d.toString() === '[object Object]') {
|
||||
return JSON.stringify(d)
|
||||
}
|
||||
if (d) {
|
||||
return d.toString()
|
||||
}
|
||||
}).filter((d) => d))
|
||||
process.stdout.write(`${log.logger} [${log.level}] ${log.context} ${messages.join(' ')}\n`)
|
||||
}
|
||||
}
|
||||
|
||||
export function clearLogs(): void {
|
||||
logs.length = 0
|
||||
}
|
||||
|
||||
const getLoggerMocked = vi.fn().mockImplementation((param: any) => {
|
||||
if (loggers[param]) {
|
||||
// TODO: check if it is working when tests run in parallel
|
||||
loggers[param].clearContext()
|
||||
return loggers[param]
|
||||
}
|
||||
// console.log('getLogger called with: ', param)
|
||||
const fakeLogger = {
|
||||
context: new Map<string, string>(),
|
||||
addContext: vi.fn((key: string, value: string) => {
|
||||
fakeLogger.context.set(key, value)
|
||||
}),
|
||||
trace: vi.fn((message: string, ...args: any[]) => {
|
||||
addLog('trace', message, param, fakeLogger.context, args)
|
||||
}),
|
||||
debug: vi.fn((message: string, ...args: any[]) => {
|
||||
addLog('debug', message, param, fakeLogger.context, args)
|
||||
}),
|
||||
warn: vi.fn((message: string, ...args: any[]) => {
|
||||
addLog('warn', message, param, fakeLogger.context, args)
|
||||
}),
|
||||
info: vi.fn((message: string, ...args: any[]) => {
|
||||
addLog('info', message, param, fakeLogger.context, args)
|
||||
}),
|
||||
error: vi.fn((message: string, ...args: any[]) => {
|
||||
addLog('error', message, param, fakeLogger.context, args)
|
||||
}),
|
||||
fatal: vi.fn((message: string, ...args: any[]) => {
|
||||
addLog('fatal', message, param, fakeLogger.context, args)
|
||||
}),
|
||||
removeContext: vi.fn((key: string) => {
|
||||
fakeLogger.context.delete(key)
|
||||
}),
|
||||
clearContext: vi.fn(() => {
|
||||
fakeLogger.context.clear()
|
||||
})
|
||||
}
|
||||
loggers[param] = fakeLogger
|
||||
return fakeLogger
|
||||
})
|
||||
|
||||
vi.mock('log4js', () => {
|
||||
const originalModule = vi.importActual('log4js')
|
||||
return {
|
||||
__esModule: true,
|
||||
...originalModule,
|
||||
getLogger: getLoggerMocked
|
||||
}
|
||||
})
|
||||
|
||||
export function getLogger(name: string) {
|
||||
if (!loggers[name]) {
|
||||
return getLoggerMocked(name)
|
||||
}
|
||||
return loggers[name]
|
||||
}
|
||||
@ -1 +1 @@
|
||||
export * from './validation'
|
||||
export * from './validation/user'
|
||||
@ -1,4 +1,3 @@
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const'
|
||||
|
||||
export const LOG_CATEGORY_SCHEMA_ALIAS = `${LOG4JS_BASE_CATEGORY_NAME}.schema`
|
||||
export * from './user'
|
||||
export const LOG4JS_CATEGORY_SCHEMA_ALIAS = `${LOG4JS_BASE_CATEGORY_NAME}.schema`
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { validateAlias } from './user.schema'
|
||||
import { getLogger } from '../../../config-schema/test/testSetup.bun'
|
||||
import { LOG_CATEGORY_SCHEMA_ALIAS } from '.'
|
||||
import { LOG4JS_CATEGORY_SCHEMA_ALIAS } from '.'
|
||||
import { validateAlias } from './user'
|
||||
import { getLogger, printLogs } from '../../../config-schema/test/testSetup.bun'
|
||||
import { describe, it, expect, beforeEach, mock, jest } from 'bun:test'
|
||||
import { aliasExists } from 'database'
|
||||
|
||||
const logger = getLogger(`${LOG_CATEGORY_SCHEMA_ALIAS}.alias`)
|
||||
const logger = getLogger(`${LOG4JS_CATEGORY_SCHEMA_ALIAS}.alias`)
|
||||
|
||||
mock.module('database', () => ({
|
||||
aliasExists: jest.fn(),
|
||||
@ -33,7 +33,7 @@ describe('validate alias', () => {
|
||||
minimum: 3,
|
||||
origin: 'string',
|
||||
message: 'Given alias is too short',
|
||||
}),*/
|
||||
}), */
|
||||
expect.objectContaining({
|
||||
code: 'too_small',
|
||||
exact: false,
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { ZodError } from 'zod'
|
||||
import { getLogger } from 'log4js'
|
||||
import { LOG_CATEGORY_SCHEMA_ALIAS } from '.'
|
||||
import { LOG4JS_CATEGORY_SCHEMA_ALIAS } from '.'
|
||||
import { aliasExists } from 'database'
|
||||
import { aliasSchema } from 'shared'
|
||||
|
||||
const logger = getLogger(`${LOG_CATEGORY_SCHEMA_ALIAS}.alias`)
|
||||
const logger = getLogger(`${LOG4JS_CATEGORY_SCHEMA_ALIAS}.alias`)
|
||||
|
||||
export async function validateAlias(alias: string): Promise<true> {
|
||||
try {
|
||||
@ -12,9 +12,10 @@ export async function validateAlias(alias: string): Promise<true> {
|
||||
} catch (err) {
|
||||
if (err instanceof ZodError || (err as Error).name === 'ZodError') {
|
||||
// throw only first error, but log all errors
|
||||
logger.warn('invalid alias', alias, (err as ZodError).issues)
|
||||
throw new Error((err as ZodError).issues[0].message)
|
||||
logger.warn('invalid alias', alias, (err as ZodError).errors)
|
||||
throw new Error((err as ZodError).errors[0].message)
|
||||
}
|
||||
console.log(err)
|
||||
throw err
|
||||
}
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
"lint": "biome check --error-on-warnings .",
|
||||
"lint:fix": "biome check --error-on-warnings . --write",
|
||||
"clear": "cross-env TZ=UTC tsx migration/index.ts clear",
|
||||
"test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test vitest run",
|
||||
"test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test vitest --reporter verbose --no-file-parallelism run",
|
||||
"test:debug": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test node --inspect-brk node_modules/.bin/jest --bail --runInBand --forceExit --detectOpenHandles",
|
||||
"up": "cross-env TZ=UTC tsx migration/index.ts up",
|
||||
"down": "cross-env TZ=UTC tsx migration/index.ts down",
|
||||
@ -40,6 +40,7 @@
|
||||
"@types/geojson": "^7946.0.13",
|
||||
"@types/jest": "27.0.2",
|
||||
"@types/node": "^18.7.14",
|
||||
"crypto-random-bigint": "^2.1.1",
|
||||
"jest": "27.2.4",
|
||||
"ts-jest": "27.0.5",
|
||||
"ts-node": "^10.9.2",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Community } from '..'
|
||||
import { Community as DbCommunity } from '..'
|
||||
import { AppDatabase } from '../AppDatabase'
|
||||
import { getHomeCommunity } from './communities'
|
||||
import { describe, expect, it, beforeAll, afterAll } from 'vitest'
|
||||
@ -15,7 +15,7 @@ afterAll(async () => {
|
||||
|
||||
describe('community.queries', () => {
|
||||
beforeAll(async () => {
|
||||
await Community.clear()
|
||||
await DbCommunity.clear()
|
||||
})
|
||||
describe('getHomeCommunity', () => {
|
||||
it('should return null if no home community exists', async () => {
|
||||
|
||||
@ -1,2 +1,6 @@
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const'
|
||||
|
||||
export * from './user'
|
||||
export * from './communities'
|
||||
|
||||
export const LOG4JS_QUERIES_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.queries`
|
||||
|
||||
@ -1,11 +1,18 @@
|
||||
import { User, UserContact } from '..'
|
||||
import { User as DbUser, UserContact as DbUserContact, Community as DbCommunity } from '..'
|
||||
import { AppDatabase } from '../AppDatabase'
|
||||
import { aliasExists } from './user'
|
||||
import { aliasExists, findUserByIdentifier } from './user'
|
||||
import { userFactory } from '../seeds/factory/user'
|
||||
import { bibiBloxberg } from '../seeds/users/bibi-bloxberg'
|
||||
import { describe, expect, it, beforeAll, afterAll } from 'vitest'
|
||||
import { describe, expect, it, beforeAll, afterAll, vi } from 'vitest'
|
||||
import { createCommunity } from '../seeds/homeCommunity'
|
||||
import { peterLustig } from '../seeds/users/peter-lustig'
|
||||
import { bobBaumeister } from '../seeds/users/bob-baumeister'
|
||||
import { getLogger, printLogs, clearLogs } from '../../../config-schema/test/testSetup.vitest.ts'
|
||||
import { LOG4JS_QUERIES_CATEGORY_NAME } from '.'
|
||||
import { beforeEach } from 'node:test'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
const userIdentifierLoggerName = `${LOG4JS_QUERIES_CATEGORY_NAME}.user.findUserByIdentifier`
|
||||
|
||||
beforeAll(async () => {
|
||||
await db.init()
|
||||
@ -14,29 +21,121 @@ afterAll(async () => {
|
||||
await db.destroy()
|
||||
})
|
||||
|
||||
describe('integration test mysql queries', () => {
|
||||
describe('user.queries', () => {
|
||||
describe('aliasExists', () => {
|
||||
beforeAll(async () => {
|
||||
await User.clear()
|
||||
await UserContact.clear()
|
||||
describe('user.queries', () => {
|
||||
describe('aliasExists', () => {
|
||||
beforeAll(async () => {
|
||||
await DbUser.clear()
|
||||
await DbUserContact.clear()
|
||||
|
||||
const bibi = bibiBloxberg
|
||||
bibi.alias = 'b-b'
|
||||
await userFactory(bibi)
|
||||
const bibi = bibiBloxberg
|
||||
bibi.alias = 'b-b'
|
||||
await userFactory(bibi)
|
||||
})
|
||||
|
||||
it('should return true if alias exists', async () => {
|
||||
expect(await aliasExists('b-b')).toBe(true)
|
||||
})
|
||||
|
||||
it('should return true if alias exists even with deviating casing', async () => {
|
||||
expect(await aliasExists('b-B')).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false if alias does not exist', async () => {
|
||||
expect(await aliasExists('bibi')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('findUserByIdentifier', () => {
|
||||
let homeCom: DbCommunity
|
||||
let communityUuid: string
|
||||
let communityName: string
|
||||
let userBibi: DbUser
|
||||
|
||||
beforeAll(async () => {
|
||||
await DbUser.clear()
|
||||
await DbUserContact.clear()
|
||||
await DbCommunity.clear()
|
||||
|
||||
homeCom = await createCommunity(false)
|
||||
communityUuid = homeCom.communityUuid!
|
||||
communityName = homeCom.name!
|
||||
userBibi = await userFactory(bibiBloxberg)
|
||||
await userFactory(peterLustig)
|
||||
await userFactory(bobBaumeister)
|
||||
})
|
||||
beforeEach(() => {
|
||||
clearLogs()
|
||||
})
|
||||
describe('communityIdentifier is community uuid', () => {
|
||||
it('userIdentifier is gradido id', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.gradidoID, communityUuid)
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
|
||||
it('should return true if alias exists', async () => {
|
||||
expect(await aliasExists('b-b')).toBe(true)
|
||||
|
||||
it('userIdentifier is alias', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.alias, communityUuid)
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
|
||||
it('should return true if alias exists even with deviating casing', async () => {
|
||||
expect(await aliasExists('b-B')).toBe(true)
|
||||
|
||||
it('userIdentifier is email', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.emailContact.email, communityUuid)
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
|
||||
it('should return false if alias does not exist', async () => {
|
||||
expect(await aliasExists('bibi')).toBe(false)
|
||||
it('userIdentifier is unknown', async () => {
|
||||
const user = await findUserByIdentifier('unknown', communityUuid)
|
||||
expect(user).toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('communityIdentifier is community name', () => {
|
||||
it('userIdentifier is gradido id', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.gradidoID, communityName)
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
|
||||
it('userIdentifier is alias', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.alias, communityName)
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
|
||||
it('userIdentifier is email', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.emailContact.email, communityName)
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
})
|
||||
describe('communityIdentifier is unknown', () => {
|
||||
it('userIdentifier is gradido id', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.gradidoID, 'unknown')
|
||||
expect(user).toBeNull()
|
||||
})
|
||||
it('userIdentifier is unknown', async () => {
|
||||
const user = await findUserByIdentifier('unknown', communityUuid)
|
||||
expect(user).toBeNull()
|
||||
})
|
||||
})
|
||||
describe('communityIdentifier is empty', () => {
|
||||
it('userIdentifier is gradido id', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.gradidoID)
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
|
||||
it('userIdentifier is alias', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.alias)
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
|
||||
it('userIdentifier is email', async () => {
|
||||
const user = await findUserByIdentifier(userBibi.emailContact.email)
|
||||
expect(user).toMatchObject(userBibi)
|
||||
})
|
||||
it('userIdentifier is unknown type', async () => {
|
||||
const user = await findUserByIdentifier('sa')
|
||||
printLogs()
|
||||
expect(getLogger(userIdentifierLoggerName).warn).toHaveBeenCalledWith('Unknown identifier type', 'sa')
|
||||
expect(user).toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
@ -1,9 +1,62 @@
|
||||
import { Raw } from 'typeorm'
|
||||
import { User as DbUser } from '../entity'
|
||||
import { Community, User as DbUser, UserContact as DbUserContact } from '../entity'
|
||||
import { FindOptionsWhere } from 'typeorm'
|
||||
import { aliasSchema, emailSchema, uuidv4Schema, urlSchema } from 'shared'
|
||||
import { getLogger } from 'log4js'
|
||||
import { LOG4JS_QUERIES_CATEGORY_NAME } from './index'
|
||||
|
||||
export async function aliasExists(alias: string): Promise<boolean> {
|
||||
const user = await DbUser.findOne({
|
||||
where: { alias: Raw((a) => `LOWER(${a}) = LOWER(:alias)`, { alias }) },
|
||||
})
|
||||
return user !== null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param identifier could be gradidoID, alias or email of user
|
||||
* @param communityIdentifier could be uuid or name of community
|
||||
* @returns
|
||||
*/
|
||||
export const findUserByIdentifier = async (
|
||||
identifier: string,
|
||||
communityIdentifier?: string,
|
||||
): Promise<DbUser | null> => {
|
||||
const communityWhere: FindOptionsWhere<Community> = urlSchema.safeParse(communityIdentifier).success
|
||||
? { url: communityIdentifier }
|
||||
: uuidv4Schema.safeParse(communityIdentifier).success
|
||||
? { communityUuid: communityIdentifier }
|
||||
: { name: communityIdentifier }
|
||||
|
||||
if (uuidv4Schema.safeParse(identifier).success) {
|
||||
return DbUser.findOne({
|
||||
where: { gradidoID: identifier, community: communityWhere },
|
||||
relations: ['emailContact', 'community'],
|
||||
})
|
||||
} else if (emailSchema.safeParse(identifier).success) {
|
||||
const userContact = await DbUserContact.findOne({
|
||||
where: {
|
||||
email: identifier,
|
||||
emailChecked: true,
|
||||
user: {
|
||||
community: communityWhere,
|
||||
},
|
||||
},
|
||||
relations: { user: { community: true } },
|
||||
})
|
||||
if (userContact) {
|
||||
// TODO: remove circular reference
|
||||
const user = userContact.user
|
||||
user.emailContact = userContact
|
||||
return user
|
||||
}
|
||||
} else if (aliasSchema.safeParse(identifier).success) {
|
||||
return await DbUser.findOne({
|
||||
where: { alias: identifier, community: communityWhere },
|
||||
relations: ['emailContact', 'community'],
|
||||
})
|
||||
}
|
||||
// should don't happen often, so we create only in the rare case a logger for it
|
||||
getLogger(`${LOG4JS_QUERIES_CATEGORY_NAME}.user.findUserByIdentifier`).warn('Unknown identifier type', identifier)
|
||||
return null
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { UserInterface } from '../users/UserInterface'
|
||||
import { User, UserContact } from '../../entity'
|
||||
import { generateRandomNumber, generateRandomNumericString } from '../utils'
|
||||
import { v4 } from 'uuid'
|
||||
import { UserContactType, OptInType, PasswordEncryptionType } from 'shared'
|
||||
import { getHomeCommunity } from '../../queries/communities'
|
||||
import random from 'crypto-random-bigint'
|
||||
|
||||
export const userFactory = async (user: UserInterface): Promise<User> => {
|
||||
let dbUserContact = new UserContact()
|
||||
@ -21,12 +22,17 @@ export const userFactory = async (user: UserInterface): Promise<User> => {
|
||||
dbUser.gradidoID = v4()
|
||||
|
||||
if (user.emailChecked) {
|
||||
dbUserContact.emailVerificationCode = generateRandomNumericString(64)
|
||||
dbUserContact.emailVerificationCode = random(64).toString()
|
||||
dbUserContact.emailOptInTypeId = OptInType.EMAIL_OPT_IN_REGISTER
|
||||
dbUserContact.emailChecked = true
|
||||
dbUser.password = generateRandomNumber()
|
||||
dbUser.password = random(64)
|
||||
dbUser.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID
|
||||
}
|
||||
const homeCommunity = await getHomeCommunity()
|
||||
if (homeCommunity) {
|
||||
dbUser.community = homeCommunity
|
||||
dbUser.communityUuid = homeCommunity.communityUuid!
|
||||
}
|
||||
// TODO: improve with cascade
|
||||
dbUser = await dbUser.save()
|
||||
dbUserContact.userId = dbUser.id
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
import { randomBytes } from 'node:crypto'
|
||||
|
||||
export function generateRandomNumber(): BigInt {
|
||||
return BigInt(randomBytes(8).readBigUInt64LE())
|
||||
}
|
||||
export function generateRandomNumericString(length: number = 64): string {
|
||||
const digits = '0123456789'
|
||||
const bytes = randomBytes(length / 8)
|
||||
return Array.from(bytes, (b) => digits[b % 10]).join('').slice(0, length)
|
||||
}
|
||||
7
database/vitest.config.js
Normal file
7
database/vitest.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
setupFiles: '../config-schema/test/testSetup.vitest.ts',
|
||||
},
|
||||
})
|
||||
@ -3,7 +3,7 @@ module.exports = {
|
||||
verbose: false,
|
||||
preset: 'ts-jest',
|
||||
collectCoverage: false,
|
||||
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
|
||||
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!build/**'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
lines: 82,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { contributionDateFormatter } from '@test/helpers'
|
||||
import { contributionDateFormatter } from './helpers'
|
||||
|
||||
describe('contributionDateFormatter', () => {
|
||||
it('formats the date correctly', () => {
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { findUserByIdentifier } from '@/graphql/util/findUserByIdentifier'
|
||||
import { fullName } from '@/graphql/util/fullName'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import {
|
||||
Community as DbCommunity,
|
||||
PendingTransaction as DbPendingTransaction,
|
||||
PendingTransactionLoggingView,
|
||||
findUserByIdentifier
|
||||
} from 'database'
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { getLogger } from 'log4js'
|
||||
@ -43,14 +43,14 @@ export class SendCoinsResolver {
|
||||
)
|
||||
}
|
||||
let receiverUser
|
||||
try {
|
||||
// second check if receiver user exists in this community
|
||||
receiverUser = await findUserByIdentifier(
|
||||
args.recipientUserIdentifier,
|
||||
args.recipientCommunityUuid,
|
||||
)
|
||||
} catch (err) {
|
||||
logger.error('Error in findUserByIdentifier:', err)
|
||||
|
||||
// second check if receiver user exists in this community
|
||||
receiverUser = await findUserByIdentifier(
|
||||
args.recipientUserIdentifier,
|
||||
args.recipientCommunityUuid,
|
||||
)
|
||||
if (!receiverUser) {
|
||||
logger.error('Error in findUserByIdentifier:')
|
||||
throw new LogError(
|
||||
`voteForSendCoins with unknown recipientUserIdentifier in the community=`,
|
||||
homeCom.name,
|
||||
@ -126,11 +126,11 @@ export class SendCoinsResolver {
|
||||
)
|
||||
}
|
||||
let receiverUser
|
||||
try {
|
||||
// second check if receiver user exists in this community
|
||||
receiverUser = await findUserByIdentifier(args.recipientUserIdentifier)
|
||||
} catch (err) {
|
||||
logger.error('Error in findUserByIdentifier:', err)
|
||||
|
||||
// second check if receiver user exists in this community
|
||||
receiverUser = await findUserByIdentifier(args.recipientUserIdentifier)
|
||||
if (!receiverUser) {
|
||||
logger.error('Error in findUserByIdentifier')
|
||||
throw new LogError(
|
||||
`revertSendCoins with unknown recipientUserIdentifier in the community=`,
|
||||
homeCom.name,
|
||||
@ -193,12 +193,11 @@ export class SendCoinsResolver {
|
||||
args.recipientCommunityUuid,
|
||||
)
|
||||
}
|
||||
let receiverUser
|
||||
try {
|
||||
// second check if receiver user exists in this community
|
||||
receiverUser = await findUserByIdentifier(args.recipientUserIdentifier)
|
||||
} catch (err) {
|
||||
logger.error('Error in findUserByIdentifier:', err)
|
||||
|
||||
// second check if receiver user exists in this community
|
||||
const receiverUser = await findUserByIdentifier(args.recipientUserIdentifier)
|
||||
if (!receiverUser) {
|
||||
logger.error('Error in findUserByIdentifier')
|
||||
throw new LogError(
|
||||
`settleSendCoins with unknown recipientUserIdentifier in the community=`,
|
||||
homeCom.name,
|
||||
@ -265,13 +264,12 @@ export class SendCoinsResolver {
|
||||
`revertSettledSendCoins with wrong recipientCommunityUuid`,
|
||||
args.recipientCommunityUuid,
|
||||
)
|
||||
}
|
||||
let receiverUser
|
||||
try {
|
||||
// second check if receiver user exists in this community
|
||||
receiverUser = await findUserByIdentifier(args.recipientUserIdentifier)
|
||||
} catch (err) {
|
||||
logger.error('Error in findUserByIdentifier:', err)
|
||||
}
|
||||
|
||||
// second check if receiver user exists in this community
|
||||
const receiverUser = await findUserByIdentifier(args.recipientUserIdentifier)
|
||||
if (!receiverUser) {
|
||||
logger.error('Error in findUserByIdentifier')
|
||||
throw new LogError(
|
||||
`revertSettledSendCoins with unknown recipientUserIdentifier in the community=`,
|
||||
homeCom.name,
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
import { User as DbUser, UserContact as DbUserContact } from 'database'
|
||||
import { validate, version } from 'uuid'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
import { VALID_ALIAS_REGEX } from './validateAlias'
|
||||
|
||||
export const findUserByIdentifier = async (
|
||||
identifier: string,
|
||||
communityIdentifier?: string,
|
||||
): Promise<DbUser> => {
|
||||
let user: DbUser | null
|
||||
if (validate(identifier) && version(identifier) === 4) {
|
||||
user = await DbUser.findOne({
|
||||
where: { gradidoID: identifier, communityUuid: communityIdentifier },
|
||||
relations: ['emailContact'],
|
||||
})
|
||||
if (!user) {
|
||||
throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier)
|
||||
}
|
||||
} else if (/^.{2,}@.{2,}\..{2,}$/.exec(identifier)) {
|
||||
const userContact = await DbUserContact.findOne({
|
||||
where: {
|
||||
email: identifier,
|
||||
emailChecked: true,
|
||||
},
|
||||
relations: ['user'],
|
||||
})
|
||||
if (!userContact) {
|
||||
throw new LogError('No user with this credentials', identifier)
|
||||
}
|
||||
if (!userContact.user) {
|
||||
throw new LogError('No user to given contact', identifier)
|
||||
}
|
||||
if (userContact.user.communityUuid !== communityIdentifier) {
|
||||
throw new LogError(
|
||||
'Found user to given contact, but belongs to other community',
|
||||
identifier,
|
||||
communityIdentifier,
|
||||
)
|
||||
}
|
||||
user = userContact.user
|
||||
user.emailContact = userContact
|
||||
} else if (VALID_ALIAS_REGEX.exec(identifier)) {
|
||||
user = await DbUser.findOne({
|
||||
where: { alias: identifier, communityUuid: communityIdentifier },
|
||||
relations: ['emailContact'],
|
||||
})
|
||||
if (!user) {
|
||||
throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier)
|
||||
}
|
||||
} else {
|
||||
throw new LogError('Unknown identifier type', identifier)
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
import { User as DbUser } from 'database'
|
||||
import { Raw } from 'typeorm'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
export const VALID_ALIAS_REGEX = /^(?=.{3,20}$)[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/
|
||||
|
||||
const RESERVED_ALIAS = [
|
||||
'admin',
|
||||
'email',
|
||||
'gast',
|
||||
'gdd',
|
||||
'gradido',
|
||||
'guest',
|
||||
'home',
|
||||
'root',
|
||||
'support',
|
||||
'temp',
|
||||
'tmp',
|
||||
'tmp',
|
||||
'user',
|
||||
'usr',
|
||||
'var',
|
||||
]
|
||||
|
||||
export const validateAlias = async (alias: string): Promise<boolean> => {
|
||||
if (alias.length < 3) {
|
||||
throw new LogError('Given alias is too short', alias)
|
||||
}
|
||||
if (alias.length > 20) {
|
||||
throw new LogError('Given alias is too long', alias)
|
||||
}
|
||||
if (!alias.match(VALID_ALIAS_REGEX)) {
|
||||
throw new LogError('Invalid characters in alias', alias)
|
||||
}
|
||||
if (RESERVED_ALIAS.includes(alias.toLowerCase())) {
|
||||
throw new LogError('Alias is not allowed', alias)
|
||||
}
|
||||
const aliasInUse = await DbUser.find({
|
||||
where: { alias: Raw((a) => `LOWER(${a}) = "${alias.toLowerCase()}"`) },
|
||||
})
|
||||
if (aliasInUse.length !== 0) {
|
||||
throw new LogError('Alias already in use', alias)
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -7,7 +7,7 @@
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./build/index.js",
|
||||
"require": "./build/index.js"
|
||||
"require": "./build/index.js"
|
||||
}
|
||||
},
|
||||
"repository": "https://github.com/gradido/gradido/shared",
|
||||
@ -26,7 +26,8 @@
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@types/node": "^17.0.21",
|
||||
"typescript": "^4.9.5"
|
||||
"typescript": "^4.9.5",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.2",
|
||||
|
||||
12
shared/src/schema/base.schema.test.ts
Normal file
12
shared/src/schema/base.schema.test.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { describe, expect, it } from 'bun:test'
|
||||
import { uuidv4Schema } from './base.schema'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
describe('uuidv4 schema', () => {
|
||||
it('should validate uuidv4 (40x)', () => {
|
||||
for (let i = 0; i < 40; i++) {
|
||||
const uuid = uuidv4()
|
||||
expect(uuidv4Schema.safeParse(uuid).success).toBeTruthy()
|
||||
}
|
||||
})
|
||||
})
|
||||
6
shared/src/schema/base.schema.ts
Normal file
6
shared/src/schema/base.schema.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { string } from 'zod'
|
||||
import { validate, version } from 'uuid'
|
||||
|
||||
export const uuidv4Schema = string().refine((val: string) => validate(val) && version(val) === 4, 'Invalid uuid')
|
||||
export const emailSchema = string().email()
|
||||
export const urlSchema = string().url()
|
||||
@ -1 +1,2 @@
|
||||
export * from './user.schema'
|
||||
export * from './base.schema'
|
||||
@ -1,13 +0,0 @@
|
||||
{
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"test": {
|
||||
"dependsOn": ["config-schema#build"]
|
||||
},
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["build/**"],
|
||||
"cache": true
|
||||
}
|
||||
}
|
||||
}
|
||||
19
yarn.lock
19
yarn.lock
@ -5180,6 +5180,13 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
crypto-random-bigint@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/crypto-random-bigint/-/crypto-random-bigint-2.1.1.tgz#f80239ca9d69b53a4920fc5908949689d1b9db95"
|
||||
integrity sha512-96+FDrenXybkpnLML/60S8NcG32KgJ5Y8yvNNCYPW02r+ssoXFR5XKenuIQcHLWumnGj8UPqUUHBzXNrDGkDmQ==
|
||||
dependencies:
|
||||
uint-rng "^1.2.1"
|
||||
|
||||
css-functions-list@^3.2.3:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.2.3.tgz#95652b0c24f0f59b291a9fc386041a19d4f40dbe"
|
||||
@ -11735,6 +11742,11 @@ tiny-case@^1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/tiny-case/-/tiny-case-1.0.3.tgz#d980d66bc72b5d5a9ca86fb7c9ffdb9c898ddd03"
|
||||
integrity sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==
|
||||
|
||||
tiny-webcrypto@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tiny-webcrypto/-/tiny-webcrypto-1.0.3.tgz#a78e1c5707c546a7d086569368b13a0de56dc9f6"
|
||||
integrity sha512-LQQdNMAgz9BXNT2SKbYh3eCb+fLV0p7JB7MwUjzY6IOlQLGIadfnFqRpshERsS5Dl2OM/hs0+4I/XmSrF+RBbw==
|
||||
|
||||
tinybench@^2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"
|
||||
@ -12269,6 +12281,13 @@ uglify-js@^3.1.4:
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f"
|
||||
integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==
|
||||
|
||||
uint-rng@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/uint-rng/-/uint-rng-1.2.1.tgz#4d1d22f75f52bc4baab739a0363fd054474be9c8"
|
||||
integrity sha512-swhDg5H+3DX2sIvnYA7VMBMXV/t8mPxvh49CjCDkwFmj/3OZIDOQwJANBgM1MPSUBrUHNIlXmU7/GcL7m4907g==
|
||||
dependencies:
|
||||
tiny-webcrypto "^1.0.2"
|
||||
|
||||
uint8array-extras@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/uint8array-extras/-/uint8array-extras-1.4.0.tgz#e42a678a6dd335ec2d21661333ed42f44ae7cc74"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user