mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
Merge branch 'frontend_show_gradido_id' into dlt_connector_direct_usage
This commit is contained in:
commit
48744c02bf
@ -8,7 +8,7 @@ export const updateHomeCommunity = gql`
|
||||
location: $location
|
||||
hieroTopicId: $hieroTopicId
|
||||
) {
|
||||
id
|
||||
uuid
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -5,8 +5,6 @@ export const ADMIN_RIGHTS = [
|
||||
RIGHTS.DELETE_USER,
|
||||
RIGHTS.UNDELETE_USER,
|
||||
RIGHTS.COMMUNITY_UPDATE,
|
||||
RIGHTS.COMMUNITY_BY_UUID,
|
||||
RIGHTS.COMMUNITY_BY_IDENTIFIER,
|
||||
RIGHTS.HOME_COMMUNITY,
|
||||
RIGHTS.COMMUNITY_WITH_API_KEYS,
|
||||
RIGHTS.PROJECT_BRANDING_MUTATE,
|
||||
]
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
import { RIGHTS } from './RIGHTS'
|
||||
|
||||
export const DLT_CONNECTOR_RIGHTS = [RIGHTS.COMMUNITY_BY_IDENTIFIER, RIGHTS.HOME_COMMUNITY, RIGHTS.COMMUNITY_UPDATE]
|
||||
export const DLT_CONNECTOR_RIGHTS = [RIGHTS.COMMUNITIES, RIGHTS.COMMUNITY_UPDATE]
|
||||
|
||||
@ -35,6 +35,7 @@ export enum RIGHTS {
|
||||
UPDATE_CONTRIBUTION = 'UPDATE_CONTRIBUTION',
|
||||
LIST_CONTRIBUTION_LINKS = 'LIST_CONTRIBUTION_LINKS',
|
||||
COMMUNITY_STATISTICS = 'COMMUNITY_STATISTICS',
|
||||
COMMUNITY_STATUS = 'COMMUNITY_STATUS',
|
||||
SEARCH_ADMIN_USERS = 'SEARCH_ADMIN_USERS',
|
||||
CREATE_CONTRIBUTION_MESSAGE = 'CREATE_CONTRIBUTION_MESSAGE',
|
||||
LIST_ALL_CONTRIBUTION_MESSAGES = 'LIST_ALL_CONTRIBUTION_MESSAGES',
|
||||
@ -69,9 +70,7 @@ export enum RIGHTS {
|
||||
SET_USER_ROLE = 'SET_USER_ROLE',
|
||||
DELETE_USER = 'DELETE_USER',
|
||||
UNDELETE_USER = 'UNDELETE_USER',
|
||||
COMMUNITY_BY_UUID = 'COMMUNITY_BY_UUID',
|
||||
COMMUNITY_BY_IDENTIFIER = 'COMMUNITY_BY_IDENTIFIER',
|
||||
HOME_COMMUNITY = 'HOME_COMMUNITY',
|
||||
COMMUNITY_UPDATE = 'COMMUNITY_UPDATE',
|
||||
COMMUNITY_WITH_API_KEYS = 'COMMUNITY_WITH_API_KEYS',
|
||||
PROJECT_BRANDING_MUTATE = 'PROJECT_BRANDING_MUTATE',
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ export const USER_RIGHTS = [
|
||||
RIGHTS.SEARCH_ADMIN_USERS,
|
||||
RIGHTS.LIST_CONTRIBUTION_LINKS,
|
||||
RIGHTS.COMMUNITY_STATISTICS,
|
||||
RIGHTS.COMMUNITY_STATUS,
|
||||
RIGHTS.CREATE_CONTRIBUTION_MESSAGE,
|
||||
RIGHTS.LIST_ALL_CONTRIBUTION_MESSAGES,
|
||||
RIGHTS.OPEN_CREATIONS,
|
||||
|
||||
@ -4,7 +4,7 @@ import { FederationClient as V1_0_FederationClient } from '@/federation/client/1
|
||||
import { FederationClient as V1_1_FederationClient } from '@/federation/client/1_1/FederationClient'
|
||||
import { ApiVersionType, ensureUrlEndsWithSlash } from 'core'
|
||||
|
||||
type FederationClient = V1_0_FederationClient | V1_1_FederationClient
|
||||
export type FederationClient = V1_0_FederationClient | V1_1_FederationClient
|
||||
|
||||
interface FederationClientInstance {
|
||||
id: number
|
||||
|
||||
@ -7,7 +7,7 @@ export class UserArgs {
|
||||
@IsString()
|
||||
identifier: string
|
||||
|
||||
@Field()
|
||||
@Field({ nullable: true })
|
||||
@IsString()
|
||||
communityIdentifier: string
|
||||
communityIdentifier?: string
|
||||
}
|
||||
|
||||
@ -38,7 +38,6 @@ export class AdminCommunityView {
|
||||
this.updatedAt = dbCom.updatedAt
|
||||
this.uuid = dbCom.communityUuid
|
||||
this.authenticatedAt = dbCom.authenticatedAt
|
||||
this.gmsApiKey = dbCom.gmsApiKey
|
||||
this.hieroTopicId = dbCom.hieroTopicId
|
||||
if (dbCom.location) {
|
||||
this.location = Point2Location(dbCom.location as Point)
|
||||
|
||||
@ -12,7 +12,7 @@ export class Community {
|
||||
this.creationDate = dbCom.creationDate
|
||||
this.uuid = dbCom.communityUuid
|
||||
this.authenticatedAt = dbCom.authenticatedAt
|
||||
this.gmsApiKey = dbCom.gmsApiKey
|
||||
// this.gmsApiKey = dbCom.gmsApiKey //
|
||||
this.hieroTopicId = dbCom.hieroTopicId
|
||||
}
|
||||
|
||||
@ -40,8 +40,9 @@ export class Community {
|
||||
@Field(() => Date, { nullable: true })
|
||||
authenticatedAt: Date | null
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
gmsApiKey: string | null
|
||||
// gms api key should only seen by admins, they can use AdminCommunityView
|
||||
// @Field(() => String, { nullable: true })
|
||||
// gmsApiKey: string | null
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
hieroTopicId: string | null
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity, getHomeCommunity } from 'database'
|
||||
import { GraphQLError } from 'graphql/error/GraphQLError'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
@ -10,19 +10,20 @@ import { i18n as localization } from '@test/testSetup'
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
import { login, updateHomeCommunityQuery } from '@/seeds/graphql/mutations'
|
||||
import {
|
||||
allCommunities,
|
||||
communitiesQuery,
|
||||
getCommunities,
|
||||
allCommunities,
|
||||
getCommunityByIdentifierQuery,
|
||||
getHomeCommunityQuery,
|
||||
reachableCommunities,
|
||||
} from '@/seeds/graphql/queries'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { createCommunity, createVerifiedFederatedCommunity } from 'database/src/seeds/community'
|
||||
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { getCommunityByUuid } from './util/communities'
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
CONFIG.FEDERATION_VALIDATE_COMMUNITY_TIMER = 1000
|
||||
|
||||
// to do: We need a setup for the tests that closes the connection
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
@ -46,11 +47,10 @@ beforeAll(async () => {
|
||||
mutate = testEnv.mutate
|
||||
query = testEnv.query
|
||||
con = testEnv.con
|
||||
await DbFederatedCommunity.clear()
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
@ -109,31 +109,38 @@ const ed25519KeyPairStaticHex = [
|
||||
]
|
||||
|
||||
describe('CommunityResolver', () => {
|
||||
describe('getCommunities', () => {
|
||||
describe('allCommunities for admin', () => {
|
||||
let homeCom1: DbFederatedCommunity
|
||||
let homeCom2: DbFederatedCommunity
|
||||
let homeCom3: DbFederatedCommunity
|
||||
let foreignCom1: DbFederatedCommunity
|
||||
let foreignCom2: DbFederatedCommunity
|
||||
let foreignCom3: DbFederatedCommunity
|
||||
let foreignCom3: DbFederatedCommunity
|
||||
|
||||
beforeAll(async () => {
|
||||
// create admin and login as admin
|
||||
await userFactory(testEnv, peterLustig)
|
||||
await mutate({ mutation: login, variables: peterLoginData })
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
describe('with empty list', () => {
|
||||
it('returns no community entry', async () => {
|
||||
// const result: Community[] = await query({ query: getCommunities })
|
||||
// expect(result.length).toEqual(0)
|
||||
await expect(query({ query: getCommunities })).resolves.toMatchObject({
|
||||
await expect(query({ query: allCommunities })).resolves.toMatchObject({
|
||||
data: {
|
||||
getCommunities: [],
|
||||
allCommunities: [],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('only home-communities entries', () => {
|
||||
describe('only home-community entries (different apis)', () => {
|
||||
beforeEach(async () => {
|
||||
await cleanDB()
|
||||
jest.clearAllMocks()
|
||||
|
||||
homeCom1 = DbFederatedCommunity.create()
|
||||
homeCom1.foreign = false
|
||||
homeCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[0].public, 'hex')
|
||||
@ -144,7 +151,7 @@ describe('CommunityResolver', () => {
|
||||
|
||||
homeCom2 = DbFederatedCommunity.create()
|
||||
homeCom2.foreign = false
|
||||
homeCom2.publicKey = Buffer.from(ed25519KeyPairStaticHex[1].public, 'hex')
|
||||
homeCom2.publicKey = Buffer.from(ed25519KeyPairStaticHex[0].public, 'hex')
|
||||
homeCom2.apiVersion = '1_1'
|
||||
homeCom2.endPoint = 'http://localhost/api'
|
||||
homeCom2.createdAt = new Date()
|
||||
@ -152,170 +159,67 @@ describe('CommunityResolver', () => {
|
||||
|
||||
homeCom3 = DbFederatedCommunity.create()
|
||||
homeCom3.foreign = false
|
||||
homeCom3.publicKey = Buffer.from(ed25519KeyPairStaticHex[2].public, 'hex')
|
||||
homeCom3.publicKey = Buffer.from(ed25519KeyPairStaticHex[0].public, 'hex')
|
||||
homeCom3.apiVersion = '2_0'
|
||||
homeCom3.endPoint = 'http://localhost/api'
|
||||
homeCom3.createdAt = new Date()
|
||||
await DbFederatedCommunity.insert(homeCom3)
|
||||
})
|
||||
|
||||
it('returns 3 home-community entries', async () => {
|
||||
await expect(query({ query: getCommunities })).resolves.toMatchObject({
|
||||
it('returns only home-community entries', async () => {
|
||||
await expect(query({ query: allCommunities })).resolves.toMatchObject({
|
||||
data: {
|
||||
getCommunities: [
|
||||
allCommunities: [
|
||||
{
|
||||
id: 3,
|
||||
foreign: homeCom3.foreign,
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[2].public),
|
||||
endPoint: expect.stringMatching('http://localhost/api/'),
|
||||
apiVersion: '2_0',
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
createdAt: homeCom3.createdAt.toISOString(),
|
||||
updatedAt: null,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
foreign: homeCom2.foreign,
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[1].public),
|
||||
endPoint: expect.stringMatching('http://localhost/api/'),
|
||||
apiVersion: '1_1',
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
createdAt: homeCom2.createdAt.toISOString(),
|
||||
updatedAt: null,
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
foreign: homeCom1.foreign,
|
||||
foreign: false,
|
||||
url: 'http://localhost',
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[0].public),
|
||||
endPoint: expect.stringMatching('http://localhost/api/'),
|
||||
apiVersion: '1_0',
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
createdAt: homeCom1.createdAt.toISOString(),
|
||||
authenticatedAt: null,
|
||||
createdAt: null,
|
||||
creationDate: null,
|
||||
description: null,
|
||||
gmsApiKey: null,
|
||||
name: null,
|
||||
updatedAt: null,
|
||||
uuid: null,
|
||||
federatedCommunities: [
|
||||
{
|
||||
id: 3,
|
||||
apiVersion: '2_0',
|
||||
endPoint: 'http://localhost/api/',
|
||||
createdAt: homeCom3.createdAt.toISOString(),
|
||||
lastAnnouncedAt: null,
|
||||
lastErrorAt: null,
|
||||
updatedAt: null,
|
||||
verifiedAt: null,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
apiVersion: '1_1',
|
||||
endPoint: 'http://localhost/api/',
|
||||
createdAt: homeCom2.createdAt.toISOString(),
|
||||
lastAnnouncedAt: null,
|
||||
lastErrorAt: null,
|
||||
updatedAt: null,
|
||||
verifiedAt: null,
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
apiVersion: '1_0',
|
||||
endPoint: 'http://localhost/api/',
|
||||
createdAt: homeCom1.createdAt.toISOString(),
|
||||
lastAnnouncedAt: null,
|
||||
lastErrorAt: null,
|
||||
updatedAt: null,
|
||||
verifiedAt: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('plus foreign-communities entries', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
|
||||
foreignCom1 = DbFederatedCommunity.create()
|
||||
foreignCom1.foreign = true
|
||||
foreignCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[3].public, 'hex')
|
||||
foreignCom1.apiVersion = '1_0'
|
||||
foreignCom1.endPoint = 'http://remotehost/api'
|
||||
foreignCom1.createdAt = new Date()
|
||||
await DbFederatedCommunity.insert(foreignCom1)
|
||||
|
||||
foreignCom2 = DbFederatedCommunity.create()
|
||||
foreignCom2.foreign = true
|
||||
foreignCom2.publicKey = Buffer.from(ed25519KeyPairStaticHex[4].public, 'hex')
|
||||
foreignCom2.apiVersion = '1_1'
|
||||
foreignCom2.endPoint = 'http://remotehost/api'
|
||||
foreignCom2.createdAt = new Date()
|
||||
await DbFederatedCommunity.insert(foreignCom2)
|
||||
|
||||
foreignCom3 = DbFederatedCommunity.create()
|
||||
foreignCom3.foreign = true
|
||||
foreignCom3.publicKey = Buffer.from(ed25519KeyPairStaticHex[5].public, 'hex')
|
||||
foreignCom3.apiVersion = '2_0'
|
||||
foreignCom3.endPoint = 'http://remotehost/api'
|
||||
foreignCom3.createdAt = new Date()
|
||||
await DbFederatedCommunity.insert(foreignCom3)
|
||||
})
|
||||
|
||||
it('returns 3 home community and 3 foreign community entries', async () => {
|
||||
await expect(query({ query: getCommunities })).resolves.toMatchObject({
|
||||
data: {
|
||||
getCommunities: [
|
||||
{
|
||||
id: 3,
|
||||
foreign: homeCom3.foreign,
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[2].public),
|
||||
endPoint: expect.stringMatching('http://localhost/api/'),
|
||||
apiVersion: '2_0',
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
createdAt: homeCom3.createdAt.toISOString(),
|
||||
updatedAt: null,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
foreign: homeCom2.foreign,
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[1].public),
|
||||
endPoint: expect.stringMatching('http://localhost/api/'),
|
||||
apiVersion: '1_1',
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
createdAt: homeCom2.createdAt.toISOString(),
|
||||
updatedAt: null,
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
foreign: homeCom1.foreign,
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[0].public),
|
||||
endPoint: expect.stringMatching('http://localhost/api/'),
|
||||
apiVersion: '1_0',
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
createdAt: homeCom1.createdAt.toISOString(),
|
||||
updatedAt: null,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
foreign: foreignCom3.foreign,
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[5].public),
|
||||
endPoint: expect.stringMatching('http://remotehost/api/'),
|
||||
apiVersion: '2_0',
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
createdAt: foreignCom3.createdAt.toISOString(),
|
||||
updatedAt: null,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
foreign: foreignCom2.foreign,
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[4].public),
|
||||
endPoint: expect.stringMatching('http://remotehost/api/'),
|
||||
apiVersion: '1_1',
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
createdAt: foreignCom2.createdAt.toISOString(),
|
||||
updatedAt: null,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
foreign: foreignCom1.foreign,
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[3].public),
|
||||
endPoint: expect.stringMatching('http://remotehost/api/'),
|
||||
apiVersion: '1_0',
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
createdAt: foreignCom1.createdAt.toISOString(),
|
||||
updatedAt: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with 6 federated community entries', () => {
|
||||
let comHomeCom1: DbCommunity
|
||||
let comForeignCom1: DbCommunity
|
||||
@ -323,7 +227,6 @@ describe('CommunityResolver', () => {
|
||||
let foreignCom4: DbFederatedCommunity
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
comHomeCom1 = DbCommunity.create()
|
||||
comHomeCom1.foreign = false
|
||||
comHomeCom1.url = 'http://localhost'
|
||||
@ -360,6 +263,30 @@ describe('CommunityResolver', () => {
|
||||
comForeignCom2.creationDate = new Date()
|
||||
await DbCommunity.insert(comForeignCom2)
|
||||
|
||||
foreignCom1 = DbFederatedCommunity.create()
|
||||
foreignCom1.foreign = true
|
||||
foreignCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[3].public, 'hex')
|
||||
foreignCom1.apiVersion = '1_0'
|
||||
foreignCom1.endPoint = 'http://remotehost/api'
|
||||
foreignCom1.createdAt = new Date()
|
||||
await DbFederatedCommunity.insert(foreignCom1)
|
||||
|
||||
foreignCom2 = DbFederatedCommunity.create()
|
||||
foreignCom2.foreign = true
|
||||
foreignCom2.publicKey = Buffer.from(ed25519KeyPairStaticHex[4].public, 'hex')
|
||||
foreignCom2.apiVersion = '1_1'
|
||||
foreignCom2.endPoint = 'http://remotehost/api'
|
||||
foreignCom2.createdAt = new Date()
|
||||
await DbFederatedCommunity.insert(foreignCom2)
|
||||
|
||||
foreignCom3 = DbFederatedCommunity.create()
|
||||
foreignCom3.foreign = true
|
||||
foreignCom3.publicKey = Buffer.from(ed25519KeyPairStaticHex[5].public, 'hex')
|
||||
foreignCom3.apiVersion = '2_0'
|
||||
foreignCom3.endPoint = 'http://remotehost/api'
|
||||
foreignCom3.createdAt = new Date()
|
||||
await DbFederatedCommunity.insert(foreignCom3)
|
||||
|
||||
foreignCom4 = DbFederatedCommunity.create()
|
||||
foreignCom4.foreign = true
|
||||
foreignCom4.publicKey = Buffer.from(ed25519KeyPairStaticHex[5].public, 'hex')
|
||||
@ -376,15 +303,15 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
foreign: false,
|
||||
url: 'http://localhost',
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[2].public),
|
||||
authenticatedAt: null,
|
||||
createdAt: null,
|
||||
creationDate: null,
|
||||
description: null,
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[0].public),
|
||||
authenticatedAt: comHomeCom1.authenticatedAt?.toISOString(),
|
||||
createdAt: comHomeCom1.createdAt.toISOString(),
|
||||
creationDate: comHomeCom1.creationDate?.toISOString(),
|
||||
description: comHomeCom1.description,
|
||||
gmsApiKey: null,
|
||||
name: null,
|
||||
name: comHomeCom1.name,
|
||||
updatedAt: null,
|
||||
uuid: null,
|
||||
uuid: comHomeCom1.communityUuid,
|
||||
federatedCommunities: [
|
||||
{
|
||||
id: 3,
|
||||
@ -396,21 +323,6 @@ describe('CommunityResolver', () => {
|
||||
updatedAt: null,
|
||||
verifiedAt: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
foreign: false,
|
||||
url: 'http://localhost',
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[1].public),
|
||||
authenticatedAt: null,
|
||||
createdAt: null,
|
||||
creationDate: null,
|
||||
description: null,
|
||||
gmsApiKey: null,
|
||||
name: null,
|
||||
updatedAt: null,
|
||||
uuid: null,
|
||||
federatedCommunities: [
|
||||
{
|
||||
id: 2,
|
||||
apiVersion: '1_1',
|
||||
@ -421,21 +333,6 @@ describe('CommunityResolver', () => {
|
||||
updatedAt: null,
|
||||
verifiedAt: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
foreign: false,
|
||||
url: 'http://localhost',
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[0].public),
|
||||
authenticatedAt: comHomeCom1.authenticatedAt?.toISOString(),
|
||||
createdAt: comHomeCom1.createdAt.toISOString(),
|
||||
creationDate: comHomeCom1.creationDate?.toISOString(),
|
||||
description: comHomeCom1.description,
|
||||
gmsApiKey: null,
|
||||
name: comHomeCom1.name,
|
||||
updatedAt: null,
|
||||
uuid: comHomeCom1.communityUuid,
|
||||
federatedCommunities: [
|
||||
{
|
||||
id: 1,
|
||||
apiVersion: '1_0',
|
||||
@ -540,23 +437,21 @@ describe('CommunityResolver', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('communities', () => {
|
||||
describe('reachableCommunities', () => {
|
||||
let homeCom1: DbCommunity
|
||||
let foreignCom1: DbCommunity
|
||||
let foreignCom2: DbCommunity
|
||||
|
||||
describe('with empty list', () => {
|
||||
beforeEach(async () => {
|
||||
await cleanDB()
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
afterAll(async () => {
|
||||
await DbCommunity.clear()
|
||||
await DbFederatedCommunity.clear()
|
||||
})
|
||||
|
||||
describe('with empty list', () => {
|
||||
it('returns no community entry', async () => {
|
||||
// const result: Community[] = await query({ query: getCommunities })
|
||||
// expect(result.length).toEqual(0)
|
||||
await expect(query({ query: communitiesQuery })).resolves.toMatchObject({
|
||||
await expect(query({ query: reachableCommunities })).resolves.toMatchObject({
|
||||
data: {
|
||||
communities: [],
|
||||
reachableCommunities: [],
|
||||
},
|
||||
})
|
||||
})
|
||||
@ -564,35 +459,20 @@ describe('CommunityResolver', () => {
|
||||
|
||||
describe('with one home-community entry', () => {
|
||||
beforeEach(async () => {
|
||||
await cleanDB()
|
||||
jest.clearAllMocks()
|
||||
|
||||
homeCom1 = DbCommunity.create()
|
||||
homeCom1.foreign = false
|
||||
homeCom1.url = 'http://localhost/api'
|
||||
homeCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[0].public, 'hex')
|
||||
homeCom1.privateKey = Buffer.from(ed25519KeyPairStaticHex[0].private, 'hex')
|
||||
homeCom1.communityUuid = 'HomeCom-UUID'
|
||||
homeCom1.authenticatedAt = new Date()
|
||||
homeCom1.name = 'HomeCommunity-name'
|
||||
homeCom1.description = 'HomeCommunity-description'
|
||||
homeCom1.creationDate = new Date()
|
||||
homeCom1 = await createCommunity(false, false)
|
||||
await DbCommunity.insert(homeCom1)
|
||||
})
|
||||
|
||||
it('returns 1 home-community entry', async () => {
|
||||
await expect(query({ query: communitiesQuery })).resolves.toMatchObject({
|
||||
await expect(query({ query: reachableCommunities })).resolves.toMatchObject({
|
||||
data: {
|
||||
communities: [
|
||||
reachableCommunities: [
|
||||
{
|
||||
id: expect.any(Number),
|
||||
foreign: homeCom1.foreign,
|
||||
name: homeCom1.name,
|
||||
description: homeCom1.description,
|
||||
url: homeCom1.url,
|
||||
creationDate: homeCom1.creationDate?.toISOString(),
|
||||
uuid: homeCom1.communityUuid,
|
||||
authenticatedAt: homeCom1.authenticatedAt?.toISOString(),
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -602,135 +482,55 @@ describe('CommunityResolver', () => {
|
||||
|
||||
describe('returns 2 filtered communities even with 3 existing entries', () => {
|
||||
beforeEach(async () => {
|
||||
await cleanDB()
|
||||
jest.clearAllMocks()
|
||||
|
||||
homeCom1 = DbCommunity.create()
|
||||
homeCom1.foreign = false
|
||||
homeCom1.url = 'http://localhost/api'
|
||||
homeCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[0].public, 'hex')
|
||||
homeCom1.privateKey = Buffer.from(ed25519KeyPairStaticHex[0].private, 'hex')
|
||||
homeCom1.communityUuid = 'HomeCom-UUID'
|
||||
homeCom1.authenticatedAt = new Date()
|
||||
homeCom1.name = 'HomeCommunity-name'
|
||||
homeCom1.description = 'HomeCommunity-description'
|
||||
homeCom1.creationDate = new Date()
|
||||
await DbCommunity.insert(homeCom1)
|
||||
|
||||
foreignCom1 = DbCommunity.create()
|
||||
foreignCom1.foreign = true
|
||||
foreignCom1.url = 'http://stage-2.gradido.net/api'
|
||||
foreignCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[3].public, 'hex')
|
||||
foreignCom1.privateKey = Buffer.from(ed25519KeyPairStaticHex[3].private, 'hex')
|
||||
// foreignCom1.communityUuid = 'Stage2-Com-UUID'
|
||||
// foreignCom1.authenticatedAt = new Date()
|
||||
foreignCom1.name = 'Stage-2_Community-name'
|
||||
foreignCom1.description = 'Stage-2_Community-description'
|
||||
foreignCom1.creationDate = new Date()
|
||||
await DbCommunity.insert(foreignCom1)
|
||||
|
||||
foreignCom2 = DbCommunity.create()
|
||||
foreignCom2.foreign = true
|
||||
foreignCom2.url = 'http://stage-3.gradido.net/api'
|
||||
foreignCom2.publicKey = Buffer.from(ed25519KeyPairStaticHex[4].public, 'hex')
|
||||
foreignCom2.privateKey = Buffer.from(ed25519KeyPairStaticHex[4].private, 'hex')
|
||||
foreignCom2.communityUuid = 'Stage3-Com-UUID'
|
||||
foreignCom2.authenticatedAt = new Date()
|
||||
foreignCom2.name = 'Stage-3_Community-name'
|
||||
foreignCom2.description = 'Stage-3_Community-description'
|
||||
foreignCom2.creationDate = new Date()
|
||||
await DbCommunity.insert(foreignCom2)
|
||||
foreignCom1 = await createCommunity(true, false)
|
||||
foreignCom2 = await createCommunity(true, false)
|
||||
const com1FedCom = await createVerifiedFederatedCommunity('1_0', 100, foreignCom1, false)
|
||||
const com1FedCom2 = await createVerifiedFederatedCommunity('1_1', 100, foreignCom1, false)
|
||||
const com2FedCom = await createVerifiedFederatedCommunity('1_0', 10000, foreignCom2, false)
|
||||
await Promise.all([
|
||||
DbCommunity.insert(foreignCom1),
|
||||
DbCommunity.insert(foreignCom2),
|
||||
DbFederatedCommunity.insert(com1FedCom),
|
||||
DbFederatedCommunity.insert(com1FedCom2),
|
||||
DbFederatedCommunity.insert(com2FedCom)
|
||||
])
|
||||
})
|
||||
|
||||
it('returns 2 community entries', async () => {
|
||||
await expect(query({ query: communitiesQuery })).resolves.toMatchObject({
|
||||
data: {
|
||||
communities: [
|
||||
{
|
||||
id: expect.any(Number),
|
||||
foreign: homeCom1.foreign,
|
||||
name: homeCom1.name,
|
||||
description: homeCom1.description,
|
||||
url: homeCom1.url,
|
||||
creationDate: homeCom1.creationDate?.toISOString(),
|
||||
uuid: homeCom1.communityUuid,
|
||||
authenticatedAt: homeCom1.authenticatedAt?.toISOString(),
|
||||
},
|
||||
/*
|
||||
{
|
||||
id: expect.any(Number),
|
||||
foreign: foreignCom1.foreign,
|
||||
name: foreignCom1.name,
|
||||
description: foreignCom1.description,
|
||||
url: foreignCom1.url,
|
||||
creationDate: foreignCom1.creationDate?.toISOString(),
|
||||
uuid: foreignCom1.communityUuid,
|
||||
authenticatedAt: foreignCom1.authenticatedAt?.toISOString(),
|
||||
},
|
||||
*/
|
||||
{
|
||||
id: expect.any(Number),
|
||||
foreign: foreignCom2.foreign,
|
||||
name: foreignCom2.name,
|
||||
description: foreignCom2.description,
|
||||
url: foreignCom2.url,
|
||||
creationDate: foreignCom2.creationDate?.toISOString(),
|
||||
uuid: foreignCom2.communityUuid,
|
||||
authenticatedAt: foreignCom2.authenticatedAt?.toISOString(),
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
const result = await query({ query: reachableCommunities })
|
||||
expect(result.data.reachableCommunities.length).toBe(2)
|
||||
expect(result.data.reachableCommunities).toMatchObject([
|
||||
{
|
||||
foreign: homeCom1.foreign,
|
||||
name: homeCom1.name,
|
||||
description: homeCom1.description,
|
||||
url: homeCom1.url,
|
||||
uuid: homeCom1.communityUuid,
|
||||
}, {
|
||||
foreign: foreignCom1.foreign,
|
||||
name: foreignCom1.name,
|
||||
description: foreignCom1.description,
|
||||
url: foreignCom1.url,
|
||||
uuid: foreignCom1.communityUuid,
|
||||
}
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('search community by uuid', () => {
|
||||
let homeCom: DbCommunity | null
|
||||
beforeEach(async () => {
|
||||
await cleanDB()
|
||||
jest.clearAllMocks()
|
||||
const admin = await userFactory(testEnv, peterLustig)
|
||||
// login as admin
|
||||
await mutate({ mutation: login, variables: peterLoginData })
|
||||
beforeAll(async () => {
|
||||
await DbCommunity.clear()
|
||||
|
||||
// HomeCommunity is still created in userFactory
|
||||
homeCom = await getCommunityByUuid(admin.communityUuid)
|
||||
homeCom = await createCommunity(false, false)
|
||||
foreignCom1 = await createCommunity(true, false)
|
||||
foreignCom2 = await createCommunity(true, false)
|
||||
|
||||
foreignCom1 = DbCommunity.create()
|
||||
foreignCom1.foreign = true
|
||||
foreignCom1.url = 'http://stage-2.gradido.net/api'
|
||||
foreignCom1.publicKey = Buffer.from(
|
||||
'8a1f9374b99c30d827b85dcd23f7e50328430d64ef65ef35bf375ea8eb9a2e1d',
|
||||
'hex',
|
||||
)
|
||||
foreignCom1.privateKey = Buffer.from(
|
||||
'f6c2a9d78e20a3c910f35b8ffcf824aa7b37f0d3d81bfc4c0e65e17a194b3a4a',
|
||||
'hex',
|
||||
)
|
||||
// foreignCom1.communityUuid = 'Stage2-Com-UUID'
|
||||
// foreignCom1.authenticatedAt = new Date()
|
||||
foreignCom1.name = 'Stage-2_Community-name'
|
||||
foreignCom1.description = 'Stage-2_Community-description'
|
||||
foreignCom1.creationDate = new Date()
|
||||
await DbCommunity.insert(foreignCom1)
|
||||
|
||||
foreignCom2 = DbCommunity.create()
|
||||
foreignCom2.foreign = true
|
||||
foreignCom2.url = 'http://stage-3.gradido.net/api'
|
||||
foreignCom2.publicKey = Buffer.from(
|
||||
'e047365a54082e8a7e9273da61b55c8134a2a0c836799ba12b78b9b0c52bc85f',
|
||||
'hex',
|
||||
)
|
||||
foreignCom2.privateKey = Buffer.from(
|
||||
'e047365a54082e8a7e9273da61b55c8134a2a0c836799ba12b78b9b0c52bc85f',
|
||||
'hex',
|
||||
)
|
||||
foreignCom2.communityUuid = uuidv4()
|
||||
foreignCom2.authenticatedAt = new Date()
|
||||
foreignCom2.name = 'Stage-3_Community-name'
|
||||
foreignCom2.description = 'Stage-3_Community-description'
|
||||
foreignCom2.creationDate = new Date()
|
||||
await DbCommunity.insert(foreignCom2)
|
||||
await Promise.all([
|
||||
DbCommunity.insert(homeCom),
|
||||
DbCommunity.insert(foreignCom1),
|
||||
DbCommunity.insert(foreignCom2),
|
||||
])
|
||||
})
|
||||
|
||||
it('finds the home-community by uuid', async () => {
|
||||
@ -749,7 +549,6 @@ describe('CommunityResolver', () => {
|
||||
url: homeCom?.url,
|
||||
creationDate: homeCom?.creationDate?.toISOString(),
|
||||
uuid: homeCom?.communityUuid,
|
||||
authenticatedAt: homeCom?.authenticatedAt,
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -769,76 +568,100 @@ describe('CommunityResolver', () => {
|
||||
description: homeCom?.description,
|
||||
url: homeCom?.url,
|
||||
creationDate: homeCom?.creationDate?.toISOString(),
|
||||
uuid: homeCom?.communityUuid,
|
||||
authenticatedAt: homeCom?.authenticatedAt,
|
||||
uuid: homeCom?.communityUuid
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('updates the home-community gmsApiKey', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: updateHomeCommunityQuery,
|
||||
variables: { uuid: homeCom?.communityUuid, gmsApiKey: 'gmsApiKey' },
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
updateHomeCommunity: {
|
||||
id: expect.any(Number),
|
||||
foreign: homeCom?.foreign,
|
||||
name: homeCom?.name,
|
||||
description: homeCom?.description,
|
||||
url: homeCom?.url,
|
||||
creationDate: homeCom?.creationDate?.toISOString(),
|
||||
uuid: homeCom?.communityUuid,
|
||||
authenticatedAt: homeCom?.authenticatedAt,
|
||||
gmsApiKey: 'gmsApiKey',
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('throws error on updating a foreign-community', async () => {
|
||||
expect(
|
||||
await mutate({
|
||||
mutation: updateHomeCommunityQuery,
|
||||
variables: { uuid: foreignCom2.communityUuid, gmsApiKey: 'gmsApiKey' },
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Error: Only the HomeCommunity could be modified!')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('throws error on updating a community without uuid', async () => {
|
||||
expect(
|
||||
await mutate({
|
||||
mutation: updateHomeCommunityQuery,
|
||||
variables: { uuid: null, gmsApiKey: 'gmsApiKey' },
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [
|
||||
new GraphQLError(`Variable "$uuid" of non-null type "String!" must not be null.`),
|
||||
],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('throws error on updating a community with not existing uuid', async () => {
|
||||
expect(
|
||||
await mutate({
|
||||
mutation: updateHomeCommunityQuery,
|
||||
variables: { uuid: uuidv4(), gmsApiKey: 'gmsApiKey' },
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('HomeCommunity with uuid not found: ')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('update community', () => {
|
||||
let homeCom: DbCommunity
|
||||
let foreignCom1: DbCommunity
|
||||
let foreignCom2: DbCommunity
|
||||
|
||||
beforeAll(async () => {
|
||||
await DbCommunity.clear()
|
||||
|
||||
// create admin and login as admin
|
||||
await userFactory(testEnv, peterLustig)
|
||||
homeCom = (await getHomeCommunity())!
|
||||
foreignCom1 = await createCommunity(true, false)
|
||||
foreignCom2 = await createCommunity(true, false)
|
||||
|
||||
await Promise.all([
|
||||
DbCommunity.insert(foreignCom1),
|
||||
DbCommunity.insert(foreignCom2),
|
||||
mutate({ mutation: login, variables: peterLoginData })
|
||||
])
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
it('updates the home-community gmsApiKey', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: updateHomeCommunityQuery,
|
||||
variables: { uuid: homeCom?.communityUuid, gmsApiKey: 'gmsApiKey' },
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
updateHomeCommunity: {
|
||||
foreign: homeCom?.foreign,
|
||||
name: homeCom?.name,
|
||||
description: homeCom?.description,
|
||||
url: homeCom?.url,
|
||||
creationDate: homeCom?.creationDate?.toISOString(),
|
||||
uuid: homeCom?.communityUuid,
|
||||
authenticatedAt: homeCom?.authenticatedAt,
|
||||
gmsApiKey: 'gmsApiKey',
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('throws error on updating a foreign-community', async () => {
|
||||
expect(
|
||||
await mutate({
|
||||
mutation: updateHomeCommunityQuery,
|
||||
variables: { uuid: foreignCom2.communityUuid, gmsApiKey: 'gmsApiKey' },
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Error: Only the HomeCommunity could be modified!')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('throws error on updating a community without uuid', async () => {
|
||||
expect(
|
||||
await mutate({
|
||||
mutation: updateHomeCommunityQuery,
|
||||
variables: { uuid: null, gmsApiKey: 'gmsApiKey' },
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [
|
||||
new GraphQLError(`Variable "$uuid" of non-null type "String!" must not be null.`),
|
||||
],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('throws error on updating a community with not existing uuid', async () => {
|
||||
expect(
|
||||
await mutate({
|
||||
mutation: updateHomeCommunityQuery,
|
||||
variables: { uuid: uuidv4(), gmsApiKey: 'gmsApiKey' },
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('HomeCommunity with uuid not found: ')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity, getHomeCommunity } from 'database'
|
||||
import {
|
||||
Community as DbCommunity,
|
||||
getReachableCommunities,
|
||||
getHomeCommunity
|
||||
} from 'database'
|
||||
import { Arg, Args, Authorized, Mutation, Query, Resolver } from 'type-graphql'
|
||||
import { IsNull, Not } from 'typeorm'
|
||||
|
||||
import { Paginated } from '@arg/Paginated'
|
||||
import { EditCommunityInput } from '@input/EditCommunityInput'
|
||||
import { AdminCommunityView } from '@model/AdminCommunityView'
|
||||
import { Community } from '@model/Community'
|
||||
import { FederatedCommunity } from '@model/FederatedCommunity'
|
||||
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import { LogError } from '@/server/LogError'
|
||||
@ -18,24 +20,11 @@ import {
|
||||
getCommunityByUuid,
|
||||
} from './util/communities'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
@Resolver()
|
||||
export class CommunityResolver {
|
||||
@Authorized([RIGHTS.COMMUNITIES])
|
||||
@Query(() => [FederatedCommunity])
|
||||
async getCommunities(): Promise<FederatedCommunity[]> {
|
||||
const dbFederatedCommunities: DbFederatedCommunity[] = await DbFederatedCommunity.find({
|
||||
order: {
|
||||
foreign: 'ASC',
|
||||
createdAt: 'DESC',
|
||||
lastAnnouncedAt: 'DESC',
|
||||
},
|
||||
})
|
||||
return dbFederatedCommunities.map(
|
||||
(dbCom: DbFederatedCommunity) => new FederatedCommunity(dbCom),
|
||||
)
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.COMMUNITIES])
|
||||
@Authorized([RIGHTS.COMMUNITY_WITH_API_KEYS])
|
||||
@Query(() => [AdminCommunityView])
|
||||
async allCommunities(@Args() paginated: Paginated): Promise<AdminCommunityView[]> {
|
||||
// communityUUID could be oneTimePassCode (uint32 number)
|
||||
@ -44,17 +33,17 @@ export class CommunityResolver {
|
||||
|
||||
@Authorized([RIGHTS.COMMUNITIES])
|
||||
@Query(() => [Community])
|
||||
async communities(): Promise<Community[]> {
|
||||
const dbCommunities: DbCommunity[] = await DbCommunity.find({
|
||||
where: { communityUuid: Not(IsNull()) }, //, authenticatedAt: Not(IsNull()) },
|
||||
order: {
|
||||
name: 'ASC',
|
||||
},
|
||||
async reachableCommunities(): Promise<Community[]> {
|
||||
const dbCommunities: DbCommunity[] = await getReachableCommunities(
|
||||
CONFIG.FEDERATION_VALIDATE_COMMUNITY_TIMER * 2, {
|
||||
// order by
|
||||
foreign: 'ASC', // home community first
|
||||
name: 'ASC', // sort foreign communities by name
|
||||
})
|
||||
return dbCommunities.map((dbCom: DbCommunity) => new Community(dbCom))
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.COMMUNITY_BY_IDENTIFIER])
|
||||
@Authorized([RIGHTS.COMMUNITIES])
|
||||
@Query(() => Community)
|
||||
async communityByIdentifier(
|
||||
@Arg('communityIdentifier') communityIdentifier: string,
|
||||
@ -67,7 +56,7 @@ export class CommunityResolver {
|
||||
return new Community(community)
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.HOME_COMMUNITY])
|
||||
@Authorized([RIGHTS.COMMUNITIES])
|
||||
@Query(() => Community)
|
||||
async homeCommunity(): Promise<Community> {
|
||||
const community = await getHomeCommunity()
|
||||
@ -78,10 +67,10 @@ export class CommunityResolver {
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.COMMUNITY_UPDATE])
|
||||
@Mutation(() => Community)
|
||||
@Mutation(() => AdminCommunityView)
|
||||
async updateHomeCommunity(
|
||||
@Args() { uuid, gmsApiKey, location, hieroTopicId }: EditCommunityInput,
|
||||
): Promise<Community> {
|
||||
): Promise<AdminCommunityView> {
|
||||
const homeCom = await getCommunityByUuid(uuid)
|
||||
if (!homeCom) {
|
||||
throw new LogError('HomeCommunity with uuid not found: ', uuid)
|
||||
@ -89,18 +78,24 @@ export class CommunityResolver {
|
||||
if (homeCom.foreign) {
|
||||
throw new LogError('Error: Only the HomeCommunity could be modified!')
|
||||
}
|
||||
|
||||
if (
|
||||
homeCom.gmsApiKey !== gmsApiKey ||
|
||||
homeCom.location !== location ||
|
||||
homeCom.hieroTopicId !== hieroTopicId
|
||||
) {
|
||||
// TODO: think about this, it is really expected to delete gmsApiKey if no new one is given?
|
||||
homeCom.gmsApiKey = gmsApiKey ?? null
|
||||
if (location) {
|
||||
homeCom.location = Location2Point(location)
|
||||
}
|
||||
homeCom.hieroTopicId = hieroTopicId ?? null
|
||||
// update only with new value, don't overwrite existing value with null or undefined!
|
||||
if (hieroTopicId) {
|
||||
homeCom.hieroTopicId = hieroTopicId
|
||||
}
|
||||
await DbCommunity.save(homeCom)
|
||||
}
|
||||
return new Community(homeCom)
|
||||
|
||||
return new AdminCommunityView(homeCom)
|
||||
}
|
||||
}
|
||||
|
||||
@ -456,9 +456,7 @@ export class TransactionResolver {
|
||||
logger.debug(
|
||||
`sendCoins(recipientCommunityIdentifier=${recipientCommunityIdentifier}, recipientIdentifier=${recipientIdentifier}, amount=${amount}, memo=${memo})`,
|
||||
)
|
||||
const homeCom = await DbCommunity.findOneOrFail({ where: { foreign: false } })
|
||||
const senderUser = getUser(context)
|
||||
|
||||
if (!recipientCommunityIdentifier || (await isHomeCommunity(recipientCommunityIdentifier))) {
|
||||
// processing sendCoins within sender and recipient are both in home community
|
||||
const recipientUser = await findUserByIdentifier(
|
||||
@ -466,7 +464,7 @@ export class TransactionResolver {
|
||||
recipientCommunityIdentifier,
|
||||
)
|
||||
if (!recipientUser) {
|
||||
throw new LogError('The recipient user was not found', recipientUser)
|
||||
throw new LogError('The recipient user was not found', { recipientIdentifier, recipientCommunityIdentifier })
|
||||
}
|
||||
logger.addContext('to', recipientUser?.id)
|
||||
if (recipientUser.foreign) {
|
||||
|
||||
@ -1163,6 +1163,12 @@ export class UserResolver {
|
||||
@Args()
|
||||
{ identifier, communityIdentifier }: UserArgs,
|
||||
): Promise<User> {
|
||||
// check if identifier contain community and user identifier
|
||||
if (identifier.includes('/')) {
|
||||
const parts = identifier.split('/')
|
||||
communityIdentifier = parts[0]
|
||||
identifier = parts[1]
|
||||
}
|
||||
const foundDbUser = await findUserByIdentifier(identifier, communityIdentifier)
|
||||
if (!foundDbUser) {
|
||||
createLogger().debug('User not found', identifier, communityIdentifier)
|
||||
|
||||
@ -375,7 +375,6 @@ export const logout = gql`
|
||||
export const updateHomeCommunityQuery = gql`
|
||||
mutation ($uuid: String!, $gmsApiKey: String!) {
|
||||
updateHomeCommunity(uuid: $uuid, gmsApiKey: $gmsApiKey) {
|
||||
id
|
||||
foreign
|
||||
name
|
||||
description
|
||||
|
||||
@ -135,18 +135,14 @@ export const listGDTEntriesQuery = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const communitiesQuery = gql`
|
||||
query {
|
||||
communities {
|
||||
id
|
||||
export const reachableCommunities = gql`
|
||||
query {
|
||||
reachableCommunities {
|
||||
foreign
|
||||
uuid
|
||||
name
|
||||
description
|
||||
url
|
||||
creationDate
|
||||
uuid
|
||||
authenticatedAt
|
||||
gmsApiKey
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -162,7 +158,6 @@ export const getCommunityByIdentifierQuery = gql`
|
||||
creationDate
|
||||
uuid
|
||||
authenticatedAt
|
||||
gmsApiKey
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -178,24 +173,6 @@ export const getHomeCommunityQuery = gql`
|
||||
creationDate
|
||||
uuid
|
||||
authenticatedAt
|
||||
gmsApiKey
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const getCommunities = gql`
|
||||
query {
|
||||
getCommunities {
|
||||
id
|
||||
foreign
|
||||
publicKey
|
||||
endPoint
|
||||
apiVersion
|
||||
lastAnnouncedAt
|
||||
verifiedAt
|
||||
lastErrorAt
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -268,7 +245,7 @@ export const listContributions = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const listAllContributions = `
|
||||
export const listAllContributions = gql`
|
||||
query ($pagination: Paginated!) {
|
||||
listAllContributions(pagination: $pagination) {
|
||||
contributionCount
|
||||
|
||||
@ -39,11 +39,13 @@ export const testEnvironment = async (testLogger = getLogger('apollo'), testI18n
|
||||
}
|
||||
|
||||
export const resetEntity = async (entity: any) => {
|
||||
const items = await entity.find({ withDeleted: true })
|
||||
// delete data and reset autoincrement!
|
||||
await entity.clear()
|
||||
/*const items = await entity.find({ withDeleted: true })
|
||||
if (items.length > 0) {
|
||||
const ids = items.map((e: any) => e.id)
|
||||
await entity.delete(ids)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
export const resetToken = () => {
|
||||
|
||||
@ -33,7 +33,7 @@ function composeDataString(data: (string | Object)[]): string {
|
||||
return data
|
||||
.map((d) => {
|
||||
// if it is a object and his toString function return only garbage
|
||||
if (typeof d === 'object' && d.toString() === '[object Object]') {
|
||||
if (d && typeof d === 'object' && d.toString() === '[object Object]') {
|
||||
return inspect(d, )
|
||||
}
|
||||
if (d) {
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
Community as DbCommunity,
|
||||
FederatedCommunity as DbFederatedCommunity,
|
||||
PendingTransaction as DbPendingTransaction,
|
||||
TransactionLink as DbTransactionLink,
|
||||
User as dbUser,
|
||||
findTransactionLinkByCode,
|
||||
findUserByIdentifier,
|
||||
@ -16,21 +17,22 @@ import { Decimal } from 'decimal.js-light'
|
||||
import { CONFIG as CONFIG_CORE } from '../../config'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '../../config/const'
|
||||
|
||||
import { encryptAndSign, PendingTransactionState, SendCoinsJwtPayloadType, SendCoinsResponseJwtPayloadType, verifyAndDecrypt } from 'shared'
|
||||
import { SendCoinsClient as V1_0_SendCoinsClient } from '../../federation/client/1_0/SendCoinsClient'
|
||||
import { SendCoinsResult } from '../../federation/client/1_0/model/SendCoinsResult'
|
||||
import { SendCoinsClientFactory } from '../../federation/client/SendCoinsClientFactory'
|
||||
import { TransactionTypeId } from '../../graphql/enum/TransactionTypeId'
|
||||
import { encryptAndSign, PendingTransactionState, SendCoinsJwtPayloadType, SendCoinsResponseJwtPayloadType, verifyAndDecrypt } from 'shared'
|
||||
// import { LogError } from '@server/LogError'
|
||||
import { getLogger } from 'log4js'
|
||||
import { calculateSenderBalance } from '../../util/calculateSenderBalance'
|
||||
import { fullName } from '../../util/utilities'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { randombytes_random } from 'sodium-native'
|
||||
import { SendCoinsResultLoggingView } from '../../federation/client/1_0/logging/SendCoinsResultLogging.view'
|
||||
import { EncryptedTransferArgs } from '../../graphql/model/EncryptedTransferArgs'
|
||||
import { randombytes_random } from 'sodium-native'
|
||||
import { settlePendingSenderTransaction } from './settlePendingSenderTransaction'
|
||||
import { storeForeignUser } from './storeForeignUser'
|
||||
import { storeLinkAsRedeemed } from './storeLinkAsRedeemed'
|
||||
|
||||
const createLogger = (method: string) => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.processXComSendCoins.${method}`)
|
||||
|
||||
@ -79,9 +81,10 @@ export async function processXComCompleteTransaction(
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
let dbTransactionLink: DbTransactionLink | null = null
|
||||
if(code !== undefined) {
|
||||
try {
|
||||
const dbTransactionLink = await findTransactionLinkByCode(code)
|
||||
dbTransactionLink = await findTransactionLinkByCode(code)
|
||||
if (dbTransactionLink && dbTransactionLink.validUntil < new Date()) {
|
||||
const errmsg = `TransactionLink ${code} is expired!`
|
||||
methodLogger.error(errmsg)
|
||||
@ -108,6 +111,7 @@ export async function processXComCompleteTransaction(
|
||||
memo,
|
||||
senderUser,
|
||||
recipientGradidoId,
|
||||
dbTransactionLink?.id,
|
||||
)
|
||||
methodLogger.debug('processXComPendingSendCoins result: ', pendingResult)
|
||||
if (pendingResult && pendingResult.vote && pendingResult.recipGradidoID) {
|
||||
@ -120,6 +124,7 @@ export async function processXComCompleteTransaction(
|
||||
memo,
|
||||
senderUser,
|
||||
pendingResult,
|
||||
dbTransactionLink?.id,
|
||||
)
|
||||
methodLogger.debug('processXComCommittingSendCoins result: ', committingResult)
|
||||
if (!committingResult.vote) {
|
||||
@ -134,14 +139,31 @@ export async function processXComCompleteTransaction(
|
||||
}
|
||||
// after successful x-com-tx store the recipient as foreign user
|
||||
methodLogger.debug('store recipient as foreign user...')
|
||||
if (await storeForeignUser(recipientCom, committingResult)) {
|
||||
const foreignUser = await storeForeignUser(recipientCom, committingResult)
|
||||
if (foreignUser) {
|
||||
methodLogger.info(
|
||||
'X-Com: new foreign user inserted successfully...',
|
||||
recipientCom.communityUuid,
|
||||
committingResult.recipGradidoID,
|
||||
)
|
||||
} else {
|
||||
const errmsg = `X-Com: Error storing foreign user for ${recipientCom.communityUuid} ${committingResult.recipGradidoID}`
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
}
|
||||
if(dbTransactionLink !== null) {
|
||||
// after successful x-com-tx per link store the link as redeemed
|
||||
methodLogger.debug('store link as redeemed...')
|
||||
if (await storeLinkAsRedeemed(dbTransactionLink, foreignUser!, creationDate)) {
|
||||
methodLogger.info(
|
||||
'X-Com: store link as redeemed successfully...',
|
||||
dbTransactionLink.code,
|
||||
foreignUser!.id,
|
||||
creationDate,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
const errmsg = `ERROR: on processXComCommittingSendCoins with ` +
|
||||
recipientCommunityUuid +
|
||||
@ -163,6 +185,7 @@ export async function processXComPendingSendCoins(
|
||||
memo: string,
|
||||
sender: dbUser,
|
||||
recipientIdentifier: string,
|
||||
transactionLinkId?: number,
|
||||
): Promise<SendCoinsResponseJwtPayloadType | null> {
|
||||
let voteResult: SendCoinsResponseJwtPayloadType
|
||||
const methodLogger = createLogger(`processXComPendingSendCoins`)
|
||||
@ -176,7 +199,8 @@ export async function processXComPendingSendCoins(
|
||||
amount: amount.toString(),
|
||||
memo: memo.substring(0, 5),
|
||||
sender: new UserLoggingView(sender),
|
||||
recipientIdentifier
|
||||
recipientIdentifier,
|
||||
transactionLinkId
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -216,7 +240,8 @@ export async function processXComPendingSendCoins(
|
||||
senderCom.communityUuid!,
|
||||
sender.gradidoID,
|
||||
fullName(sender.firstName, sender.lastName),
|
||||
sender.alias
|
||||
sender.alias,
|
||||
transactionLinkId
|
||||
)
|
||||
if(methodLogger.isDebugEnabled()) {
|
||||
methodLogger.debug(`ready for voteForSendCoins with payload=${payload}`)
|
||||
@ -277,6 +302,7 @@ export async function processXComPendingSendCoins(
|
||||
pendingTx.userId = sender.id
|
||||
pendingTx.userGradidoID = sender.gradidoID
|
||||
pendingTx.userName = fullName(sender.firstName, sender.lastName)
|
||||
pendingTx.transactionLinkId = transactionLinkId
|
||||
if(methodLogger.isDebugEnabled()) {
|
||||
methodLogger.debug(`initialized sender pendingTX=${new PendingTransactionLoggingView(pendingTx)}`)
|
||||
}
|
||||
@ -326,6 +352,7 @@ export async function processXComCommittingSendCoins(
|
||||
memo: string,
|
||||
sender: dbUser,
|
||||
recipient: SendCoinsResult,
|
||||
transactionLinkId?: number,
|
||||
): Promise<SendCoinsResult> {
|
||||
const methodLogger = createLogger(`processXComCommittingSendCoins`)
|
||||
const handshakeID = randombytes_random().toString()
|
||||
@ -342,6 +369,7 @@ export async function processXComCommittingSendCoins(
|
||||
memo: memo.substring(0, 5),
|
||||
sender: new UserLoggingView(sender),
|
||||
recipient: new SendCoinsResultLoggingView(recipient),
|
||||
transactionLinkId,
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -384,6 +412,7 @@ export async function processXComCommittingSendCoins(
|
||||
pendingTx.userGradidoID!,
|
||||
pendingTx.userName!,
|
||||
sender.alias,
|
||||
pendingTx.transactionLinkId,
|
||||
)
|
||||
payload.recipientCommunityUuid = pendingTx.linkedUserCommunityUuid
|
||||
? pendingTx.linkedUserCommunityUuid
|
||||
|
||||
@ -92,7 +92,7 @@ export async function settlePendingSenderTransaction(
|
||||
transactionSend.decay = sendBalance.decay.decay // pendingTx.decay
|
||||
transactionSend.decayStart = sendBalance.decay.start // pendingTx.decayStart
|
||||
transactionSend.previous = pendingTx.previous
|
||||
transactionSend.linkedTransactionId = pendingTx.linkedTransactionId
|
||||
transactionSend.transactionLinkId = pendingTx.transactionLinkId
|
||||
await queryRunner.manager.insert(dbTransaction, transactionSend)
|
||||
logger.debug(`send Transaction inserted: ${transactionSend}`)
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.logic.storeForeig
|
||||
export async function storeForeignUser(
|
||||
recipCom: DbCommunity,
|
||||
committingResult: SendCoinsResult,
|
||||
): Promise<boolean> {
|
||||
): Promise<DbUser | null> {
|
||||
if (recipCom.communityUuid !== null && committingResult.recipGradidoID !== null) {
|
||||
try {
|
||||
const user = await findForeignUserByUuids(recipCom.communityUuid, committingResult.recipGradidoID)
|
||||
@ -35,7 +35,7 @@ export async function storeForeignUser(
|
||||
foreignUser = await DbUser.save(foreignUser)
|
||||
logger.debug('new foreignUser inserted:', foreignUser)
|
||||
|
||||
return true
|
||||
return foreignUser
|
||||
} else if (
|
||||
user.firstName !== committingResult.recipFirstName ||
|
||||
user.lastName !== committingResult.recipLastName ||
|
||||
@ -57,15 +57,15 @@ export async function storeForeignUser(
|
||||
}
|
||||
await DbUser.save(user)
|
||||
logger.debug('update recipient successful.', user)
|
||||
return true
|
||||
return user
|
||||
} else {
|
||||
logger.debug('foreignUser still exists...:', user)
|
||||
return true
|
||||
return user
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('error in storeForeignUser;', err)
|
||||
return false
|
||||
return null
|
||||
}
|
||||
}
|
||||
return false
|
||||
return null
|
||||
}
|
||||
|
||||
17
core/src/graphql/logic/storeLinkAsRedeemed.ts
Normal file
17
core/src/graphql/logic/storeLinkAsRedeemed.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { TransactionLink as DbTransactionLink, User as DbUser } from "database";
|
||||
|
||||
export async function storeLinkAsRedeemed(
|
||||
dbTransactionLink: DbTransactionLink,
|
||||
foreignUser: DbUser,
|
||||
creationDate: Date,
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
dbTransactionLink.redeemedBy = foreignUser.id
|
||||
dbTransactionLink.redeemedAt = creationDate
|
||||
await DbTransactionLink.save(dbTransactionLink)
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('error in storeLinkAsRedeemed;', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
import { Community as DbCommunity } from '..'
|
||||
import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity } from '..'
|
||||
import { AppDatabase } from '../AppDatabase'
|
||||
import { getHomeCommunity } from './communities'
|
||||
import { describe, expect, it, beforeAll, afterAll } from 'vitest'
|
||||
import { createCommunity } from '../seeds/homeCommunity'
|
||||
import { getHomeCommunity, getReachableCommunities } from './communities'
|
||||
import { describe, expect, it, beforeEach, beforeAll, afterAll } from 'vitest'
|
||||
import { createCommunity, createVerifiedFederatedCommunity } from '../seeds/community'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
|
||||
@ -14,8 +14,10 @@ afterAll(async () => {
|
||||
})
|
||||
|
||||
describe('community.queries', () => {
|
||||
beforeAll(async () => {
|
||||
// clean db for every test case
|
||||
beforeEach(async () => {
|
||||
await DbCommunity.clear()
|
||||
await DbFederatedCommunity.clear()
|
||||
})
|
||||
describe('getHomeCommunity', () => {
|
||||
it('should return null if no home community exists', async () => {
|
||||
@ -37,4 +39,51 @@ describe('community.queries', () => {
|
||||
expect(community?.privateKey).toStrictEqual(homeCom.privateKey)
|
||||
})
|
||||
})
|
||||
describe('getReachableCommunities', () => {
|
||||
it('home community counts also to reachable communities', async () => {
|
||||
await createCommunity(false)
|
||||
expect(await getReachableCommunities(1000)).toHaveLength(1)
|
||||
})
|
||||
it('foreign communities authenticated within chosen range', async () => {
|
||||
const com1 = await createCommunity(true)
|
||||
const com2 = await createCommunity(true)
|
||||
const com3 = await createCommunity(true)
|
||||
await createVerifiedFederatedCommunity('1_0', 100, com1)
|
||||
await createVerifiedFederatedCommunity('1_0', 500, com2)
|
||||
// outside of range
|
||||
await createVerifiedFederatedCommunity('1_0', 1200, com3)
|
||||
|
||||
const communities = await getReachableCommunities(1000)
|
||||
expect(communities).toHaveLength(2)
|
||||
expect(communities[0].communityUuid).toBe(com1.communityUuid)
|
||||
expect(communities[1].communityUuid).toBe(com2.communityUuid)
|
||||
})
|
||||
it('multiple federated community api version, result in one community', async () => {
|
||||
const com1 = await createCommunity(true)
|
||||
await createVerifiedFederatedCommunity('1_0', 100, com1)
|
||||
await createVerifiedFederatedCommunity('1_1', 100, com1)
|
||||
expect(await getReachableCommunities(1000)).toHaveLength(1)
|
||||
})
|
||||
it('multiple federated community api version one outside of range, result in one community', async () => {
|
||||
const com1 = await createCommunity(true)
|
||||
await createVerifiedFederatedCommunity('1_0', 100, com1)
|
||||
// outside of range
|
||||
await createVerifiedFederatedCommunity('1_1', 1200, com1)
|
||||
expect(await getReachableCommunities(1000)).toHaveLength(1)
|
||||
})
|
||||
it('foreign and home community', async () => {
|
||||
// home community
|
||||
await createCommunity(false)
|
||||
const com1 = await createCommunity(true)
|
||||
const com2 = await createCommunity(true)
|
||||
await createVerifiedFederatedCommunity('1_0', 400, com1)
|
||||
await createVerifiedFederatedCommunity('1_0', 1200, com2)
|
||||
expect(await getReachableCommunities(1000)).toHaveLength(2)
|
||||
})
|
||||
it('not verified inside time frame federated community', async () => {
|
||||
const com1 = await createCommunity(true)
|
||||
await createVerifiedFederatedCommunity('1_0', 1200, com1)
|
||||
expect(await getReachableCommunities(1000)).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,10 +1,14 @@
|
||||
import { FindOptionsOrder, FindOptionsWhere, IsNull, MoreThanOrEqual, Not } from 'typeorm'
|
||||
import { Community as DbCommunity } from '../entity'
|
||||
import { urlSchema, uuidv4Schema } from 'shared'
|
||||
|
||||
/**
|
||||
* Retrieves the home community, i.e., a community that is not foreign.
|
||||
* @returns A promise that resolves to the home community, or null if no home community was found
|
||||
*/
|
||||
export async function getHomeCommunity(): Promise<DbCommunity | null> {
|
||||
// TODO: Put in Cache, it is needed nearly always
|
||||
// TODO: return only DbCommunity or throw to reduce unnecessary checks, because there should be always a home community
|
||||
return await DbCommunity.findOne({
|
||||
where: { foreign: false },
|
||||
})
|
||||
@ -15,3 +19,45 @@ export async function getCommunityByUuid(communityUuid: string): Promise<DbCommu
|
||||
where: [{ communityUuid }],
|
||||
})
|
||||
}
|
||||
|
||||
export function findWithCommunityIdentifier(communityIdentifier: string): FindOptionsWhere<DbCommunity> {
|
||||
const where: FindOptionsWhere<DbCommunity> = {}
|
||||
// pre filter identifier type to reduce db query complexity
|
||||
if (urlSchema.safeParse(communityIdentifier).success) {
|
||||
where.url = communityIdentifier
|
||||
} else if (uuidv4Schema.safeParse(communityIdentifier).success) {
|
||||
where.communityUuid = communityIdentifier
|
||||
} else {
|
||||
where.name = communityIdentifier
|
||||
}
|
||||
return where
|
||||
}
|
||||
|
||||
export async function getCommunityWithFederatedCommunityByIdentifier(
|
||||
communityIdentifier: string,
|
||||
): Promise<DbCommunity | null> {
|
||||
return await DbCommunity.findOne({
|
||||
where: { ...findWithCommunityIdentifier(communityIdentifier) },
|
||||
relations: ['federatedCommunities'],
|
||||
})
|
||||
}
|
||||
|
||||
// returns all reachable communities
|
||||
// home community and all federated communities which have been verified within the last authenticationTimeoutMs
|
||||
export async function getReachableCommunities(
|
||||
authenticationTimeoutMs: number,
|
||||
order?: FindOptionsOrder<DbCommunity>
|
||||
): Promise<DbCommunity[]> {
|
||||
return await DbCommunity.find({
|
||||
where: [
|
||||
{
|
||||
authenticatedAt: Not(IsNull()),
|
||||
federatedCommunities: {
|
||||
verifiedAt: MoreThanOrEqual(new Date(Date.now() - authenticationTimeoutMs))
|
||||
}
|
||||
},
|
||||
{ foreign: false },
|
||||
],
|
||||
order,
|
||||
})
|
||||
}
|
||||
@ -14,7 +14,7 @@ import { peterLustig } from '../seeds/users/peter-lustig'
|
||||
import { bobBaumeister } from '../seeds/users/bob-baumeister'
|
||||
import { garrickOllivander } from '../seeds/users/garrick-ollivander'
|
||||
import { describe, expect, it, beforeAll, afterAll } from 'vitest'
|
||||
import { createCommunity } from '../seeds/homeCommunity'
|
||||
import { createCommunity } from '../seeds/community'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import Decimal from 'decimal.js-light'
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import { aliasExists, findUserByIdentifier } from './user'
|
||||
import { userFactory } from '../seeds/factory/user'
|
||||
import { bibiBloxberg } from '../seeds/users/bibi-bloxberg'
|
||||
import { describe, expect, it, beforeAll, afterAll, beforeEach, } from 'vitest'
|
||||
import { createCommunity } from '../seeds/homeCommunity'
|
||||
import { createCommunity } from '../seeds/community'
|
||||
import { peterLustig } from '../seeds/users/peter-lustig'
|
||||
import { bobBaumeister } from '../seeds/users/bob-baumeister'
|
||||
import { getLogger, printLogs, clearLogs } from '../../../config-schema/test/testSetup.vitest'
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { Raw } from 'typeorm'
|
||||
import { Community, User as DbUser, UserContact as DbUserContact } from '../entity'
|
||||
import { FindOptionsWhere } from 'typeorm'
|
||||
import { aliasSchema, emailSchema, uuidv4Schema, urlSchema } from 'shared'
|
||||
import { User as DbUser, UserContact as DbUserContact } from '../entity'
|
||||
import { aliasSchema, emailSchema, uuidv4Schema } from 'shared'
|
||||
import { getLogger } from 'log4js'
|
||||
import { LOG4JS_QUERIES_CATEGORY_NAME } from './index'
|
||||
import { findWithCommunityIdentifier, LOG4JS_QUERIES_CATEGORY_NAME } from './index'
|
||||
|
||||
export async function aliasExists(alias: string): Promise<boolean> {
|
||||
const user = await DbUser.findOne({
|
||||
@ -29,11 +28,9 @@ 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 }
|
||||
const communityWhere = communityIdentifier
|
||||
? findWithCommunityIdentifier(communityIdentifier)
|
||||
: undefined
|
||||
|
||||
if (uuidv4Schema.safeParse(identifier).success) {
|
||||
return DbUser.findOne({
|
||||
|
||||
42
database/src/seeds/community.ts
Normal file
42
database/src/seeds/community.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { Community, FederatedCommunity } from '../entity'
|
||||
import { randomBytes } from 'node:crypto'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export async function createCommunity(foreign: boolean, save: boolean = true): Promise<Community> {
|
||||
const community = new Community()
|
||||
community.publicKey = randomBytes(32)
|
||||
community.communityUuid = uuidv4()
|
||||
community.name = 'HomeCommunity-name'
|
||||
community.creationDate = new Date()
|
||||
|
||||
if(foreign) {
|
||||
community.foreign = true
|
||||
community.name = 'ForeignCommunity-name'
|
||||
community.description = 'ForeignCommunity-description'
|
||||
community.url = `http://foreign-${Math.random()}/api`
|
||||
community.authenticatedAt = new Date()
|
||||
} else {
|
||||
community.foreign = false
|
||||
// todo: generate valid public/private key pair (ed25519)
|
||||
community.privateKey = randomBytes(64)
|
||||
community.name = 'HomeCommunity-name'
|
||||
community.description = 'HomeCommunity-description'
|
||||
community.url = 'http://localhost/api'
|
||||
}
|
||||
return save ? await community.save() : community
|
||||
}
|
||||
|
||||
export async function createVerifiedFederatedCommunity(
|
||||
apiVersion: string,
|
||||
verifiedBeforeMs: number,
|
||||
community: Community,
|
||||
save: boolean = true
|
||||
): Promise<FederatedCommunity> {
|
||||
const federatedCommunity = new FederatedCommunity()
|
||||
federatedCommunity.apiVersion = apiVersion
|
||||
federatedCommunity.endPoint = community.url
|
||||
federatedCommunity.publicKey = community.publicKey
|
||||
federatedCommunity.community = community
|
||||
federatedCommunity.verifiedAt = new Date(Date.now() - verifiedBeforeMs)
|
||||
return save ? await federatedCommunity.save() : federatedCommunity
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
import { Community } from '../entity'
|
||||
import { randomBytes } from 'node:crypto'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export async function createCommunity(foreign: boolean): Promise<Community> {
|
||||
const homeCom = new Community()
|
||||
homeCom.publicKey = randomBytes(32)
|
||||
homeCom.communityUuid = uuidv4()
|
||||
homeCom.authenticatedAt = new Date()
|
||||
homeCom.name = 'HomeCommunity-name'
|
||||
homeCom.creationDate = new Date()
|
||||
|
||||
if(foreign) {
|
||||
homeCom.foreign = true
|
||||
homeCom.name = 'ForeignCommunity-name'
|
||||
homeCom.description = 'ForeignCommunity-description'
|
||||
homeCom.url = 'http://foreign/api'
|
||||
} else {
|
||||
homeCom.foreign = false
|
||||
homeCom.privateKey = randomBytes(64)
|
||||
homeCom.name = 'HomeCommunity-name'
|
||||
homeCom.description = 'HomeCommunity-description'
|
||||
homeCom.url = 'http://localhost/api'
|
||||
}
|
||||
return await homeCom.save()
|
||||
}
|
||||
@ -109,7 +109,7 @@ export class AuthenticationResolver {
|
||||
}
|
||||
}
|
||||
|
||||
@Mutation(() => String)
|
||||
@Mutation(() => String, { nullable: true })
|
||||
async authenticate(
|
||||
@Arg('data')
|
||||
args: EncryptedTransferArgs,
|
||||
|
||||
@ -104,6 +104,7 @@ export class SendCoinsResolver {
|
||||
pendingTx.userCommunityUuid = authArgs.recipientCommunityUuid
|
||||
pendingTx.userGradidoID = receiverUser.gradidoID
|
||||
pendingTx.userName = fullName(receiverUser.firstName, receiverUser.lastName)
|
||||
pendingTx.transactionLinkId = authArgs.transactionLinkId
|
||||
|
||||
await DbPendingTransaction.insert(pendingTx)
|
||||
const responseArgs = new SendCoinsResponseJwtPayloadType(
|
||||
|
||||
@ -92,6 +92,7 @@ export async function settlePendingReceiveTransaction(
|
||||
transactionReceive.decayStart = receiveBalance ? receiveBalance.decay.start : null
|
||||
transactionReceive.previous = receiveBalance ? receiveBalance.lastTransactionId : null
|
||||
transactionReceive.linkedTransactionId = pendingTx.linkedTransactionId
|
||||
transactionReceive.transactionLinkId = pendingTx.transactionLinkId
|
||||
await queryRunner.manager.insert(dbTransaction, transactionReceive)
|
||||
logger.debug(`receive Transaction inserted: ${new TransactionLoggingView(transactionReceive)}`)
|
||||
|
||||
|
||||
@ -22,10 +22,10 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { selectCommunities } from '@/graphql/queries'
|
||||
import { reachableCommunities } from '@/graphql/communities.graphql'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
const props = defineProps({
|
||||
@ -33,9 +33,13 @@ const props = defineProps({
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
communityIdentifier: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits(['update:modelValue', 'communitiesLoaded'])
|
||||
|
||||
const route = useRoute()
|
||||
const { toastError } = useAppToast()
|
||||
@ -43,20 +47,31 @@ const { toastError } = useAppToast()
|
||||
const communities = ref([])
|
||||
const validCommunityIdentifier = ref(false)
|
||||
|
||||
const { onResult } = useQuery(selectCommunities)
|
||||
const { onResult } = useQuery(reachableCommunities)
|
||||
|
||||
onResult(({ data }) => {
|
||||
// console.log('CommunitySwitch.onResult...data=', data)
|
||||
if (data) {
|
||||
communities.value = data.communities
|
||||
communities.value = data.reachableCommunities
|
||||
setDefaultCommunity()
|
||||
if (data.communities.length === 1) {
|
||||
if (data.reachableCommunities.length === 1) {
|
||||
validCommunityIdentifier.value = true
|
||||
}
|
||||
emit('communitiesLoaded', data.reachableCommunities)
|
||||
}
|
||||
})
|
||||
|
||||
const communityIdentifier = computed(() => route.params.communityIdentifier)
|
||||
const communityIdentifier = computed(
|
||||
() => route.params.communityIdentifier || props.communityIdentifier,
|
||||
)
|
||||
|
||||
watch(
|
||||
() => communityIdentifier.value,
|
||||
() => {
|
||||
// console.log('CommunitySwitch.communityIdentifier.value', value)
|
||||
setDefaultCommunity()
|
||||
},
|
||||
)
|
||||
|
||||
function updateCommunity(community) {
|
||||
// console.log('CommunitySwitch.updateCommunity...community=', community)
|
||||
|
||||
@ -163,16 +163,37 @@ const validationSchema = computed(() => {
|
||||
.min(5, ({ min }) => ({ key: 'form.validation.contributionMemo.min', values: { min } }))
|
||||
.max(512, ({ max }) => ({ key: 'form.validation.contributionMemo.max', values: { max } }))
|
||||
.required('form.validation.contributionMemo.required'),
|
||||
hours: number()
|
||||
hours: string()
|
||||
.typeError({ key: 'form.validation.hours.typeError', values: { min: 0.01, max: maxHours } })
|
||||
.required()
|
||||
// .transform((value, originalValue) => (originalValue === '' ? undefined : value))
|
||||
.transform((currentValue) =>
|
||||
!currentValue || typeof currentValue !== 'string'
|
||||
? currentValue
|
||||
: currentValue.replace(',', '.'),
|
||||
)
|
||||
// min and max are needed for html min max which validatedInput will take from this scheme
|
||||
.min(0.01, ({ min }) => ({ key: 'form.validation.hours.min', values: { min } }))
|
||||
.max(maxHours, ({ max }) => ({ key: 'form.validation.hours.max', values: { max } }))
|
||||
.test('decimal-places', 'form.validation.hours.decimal-places', (value) => {
|
||||
if (value === undefined || value === null) return true
|
||||
return /^\d+(\.\d{0,2})?$/.test(value.toString())
|
||||
}),
|
||||
})
|
||||
// min and max are not working with string, so we need to do it manually
|
||||
.test('min-hours', { key: 'form.validation.hours.min', values: { min: 0.01 } }, (value) => {
|
||||
if (value === undefined || value === null || Number.isNaN(parseFloat(value))) {
|
||||
return false
|
||||
}
|
||||
return parseFloat(value) >= 0.01
|
||||
})
|
||||
.test(
|
||||
'max-hours',
|
||||
{ key: 'form.validation.hours.max', values: { max: maxHours } },
|
||||
(value) => {
|
||||
if (value === undefined || value === null || Number.isNaN(parseFloat(value))) {
|
||||
return false
|
||||
}
|
||||
return parseFloat(value) <= maxHours
|
||||
},
|
||||
),
|
||||
amount: number().min(0.01).max(maxAmounts),
|
||||
})
|
||||
})
|
||||
@ -217,7 +238,8 @@ const updateField = (newValue, name) => {
|
||||
if (typeof name === 'string' && name.length) {
|
||||
form[name] = newValue
|
||||
if (name === 'hours') {
|
||||
const amount = form.hours ? hoursToAmount(form.hours) : GDD_PER_HOUR
|
||||
const hoursTransformed = validationSchema.value.fields.hours.cast(newValue)
|
||||
const amount = hoursTransformed ? hoursToAmount(hoursTransformed) : GDD_PER_HOUR
|
||||
form.amount = amount.toString()
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ vi.mock('vue-router', () => ({
|
||||
}))
|
||||
|
||||
const mockUseQuery = vi.fn()
|
||||
const mockUseLazyQuery = vi.fn()
|
||||
vi.mock('@vue/apollo-composable', () => ({
|
||||
useQuery: (...args) => {
|
||||
mockUseQuery(...args)
|
||||
@ -35,6 +36,12 @@ vi.mock('@vue/apollo-composable', () => ({
|
||||
error: ref(null),
|
||||
}
|
||||
},
|
||||
useLazyQuery: (...args) => {
|
||||
mockUseLazyQuery(...args)
|
||||
return {
|
||||
refetch: vi.fn(() => true),
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/composables/useToast', () => ({
|
||||
|
||||
@ -48,7 +48,9 @@
|
||||
<community-switch
|
||||
:disabled="isBalanceEmpty"
|
||||
:model-value="form.targetCommunity"
|
||||
:community-identifier="autoCommunityIdentifier"
|
||||
@update:model-value="updateField($event, 'targetCommunity')"
|
||||
@communities-loaded="setCommunities"
|
||||
/>
|
||||
</BCol>
|
||||
</BRow>
|
||||
@ -62,7 +64,7 @@
|
||||
:label="$t('form.recipient')"
|
||||
:placeholder="$t('form.identifier')"
|
||||
:rules="validationSchema.fields.identifier"
|
||||
:disabled="isBalanceEmpty"
|
||||
:disabled="isBalanceEmpty || isCommunitiesEmpty"
|
||||
:disable-smart-valid-state="disableSmartValidState"
|
||||
@update:model-value="updateField"
|
||||
/>
|
||||
@ -171,6 +173,8 @@ const props = defineProps({
|
||||
const entityDataToForm = computed(() => ({ ...props }))
|
||||
const form = reactive({ ...entityDataToForm.value })
|
||||
const disableSmartValidState = ref(false)
|
||||
const communities = ref([])
|
||||
const autoCommunityIdentifier = ref('')
|
||||
|
||||
const emit = defineEmits(['set-transaction'])
|
||||
|
||||
@ -191,6 +195,10 @@ const userIdentifier = computed(() => {
|
||||
return null
|
||||
})
|
||||
|
||||
function setCommunities(returnedCommunities) {
|
||||
communities.value = returnedCommunities
|
||||
}
|
||||
|
||||
const validationSchema = computed(() => {
|
||||
const amountSchema = number()
|
||||
.required()
|
||||
@ -214,7 +222,25 @@ const validationSchema = computed(() => {
|
||||
return object({
|
||||
memo: memoSchema,
|
||||
amount: amountSchema,
|
||||
identifier: identifierSchema,
|
||||
// todo: found a better way, because this validation test has side effects
|
||||
identifier: identifierSchema.test(
|
||||
'community-is-reachable',
|
||||
'form.validation.identifier.communityIsReachable',
|
||||
(value) => {
|
||||
const parts = value.split('/')
|
||||
// early exit if no community id is in identifier string
|
||||
if (parts.length !== 2) {
|
||||
return true
|
||||
}
|
||||
return communities.value.some((community) => {
|
||||
return (
|
||||
community.uuid === parts[0] ||
|
||||
community.name === parts[0] ||
|
||||
community.url === parts[0]
|
||||
)
|
||||
})
|
||||
},
|
||||
),
|
||||
})
|
||||
} else {
|
||||
// don't need identifier schema if it is a transaction link or identifier was set via url
|
||||
@ -224,7 +250,6 @@ const validationSchema = computed(() => {
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const formIsInvalid = computed(() => !validationSchema.value.isValidSync(form))
|
||||
|
||||
const updateField = (newValue, name) => {
|
||||
@ -234,6 +259,7 @@ const updateField = (newValue, name) => {
|
||||
}
|
||||
|
||||
const isBalanceEmpty = computed(() => props.balance <= 0)
|
||||
const isCommunitiesEmpty = computed(() => communities.value.length === 0)
|
||||
|
||||
const { result: userResult, error: userError } = useQuery(
|
||||
user,
|
||||
@ -258,8 +284,35 @@ watch(userError, (error) => {
|
||||
}
|
||||
})
|
||||
|
||||
// if identifier contain valid community identifier of a reachable community:
|
||||
// set it as target community and change community-switch to show only current value, instead of select
|
||||
watch(
|
||||
() => form.identifier,
|
||||
(value) => {
|
||||
autoCommunityIdentifier.value = ''
|
||||
const parts = value.split('/')
|
||||
if (parts.length === 2) {
|
||||
const com = communities.value.find(
|
||||
(community) =>
|
||||
community.uuid === parts[0] || community.name === parts[0] || community.url === parts[0],
|
||||
)
|
||||
if (com) {
|
||||
form.targetCommunity = com
|
||||
autoCommunityIdentifier.value = com.uuid
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
function onSubmit() {
|
||||
const transformedForm = validationSchema.value.cast(form)
|
||||
const parts = transformedForm.identifier.split('/')
|
||||
if (parts.length === 2) {
|
||||
transformedForm.identifier = parts[1]
|
||||
transformedForm.targetCommunity = communities.value.find((com) => {
|
||||
return com.uuid === parts[0] || com.name === parts[0] || com.url === parts[0]
|
||||
})
|
||||
}
|
||||
emit('set-transaction', {
|
||||
...transformedForm,
|
||||
selected: radioSelected.value,
|
||||
|
||||
@ -6,15 +6,30 @@
|
||||
:is-redeem-jwt-link="isRedeemJwtLink"
|
||||
class="redeem-community-selection"
|
||||
>
|
||||
<BCard bg-variant="muted" text-variant="dark" border-variant="info">
|
||||
<BRow bg-variant="muted" text-variant="dark">
|
||||
<h1 v-if="linkData.amount === ''">{{ $t('gdd_per_link.redeemlink-error') }}</h1>
|
||||
<h1 v-if="!isContributionLink && linkData.amount !== ''">
|
||||
<template v-if="linkData.senderUser">
|
||||
{{ linkData.senderUser.firstName }}
|
||||
{{ $t('transaction-link.send_you') }} {{ linkData.amount }} {{ $t('GDD-long') }}
|
||||
</template>
|
||||
</h1>
|
||||
<BRow>
|
||||
<BCol class="mb-4" cols="12">
|
||||
<b>{{ linkData.memo }}</b>
|
||||
</BCol>
|
||||
</BRow>
|
||||
<BRow v-if="!isContributionLink && linkData.amount !== ''">
|
||||
<BCol class="mb-4" cols="12">
|
||||
<BRow>
|
||||
<BCol v-if="!isRedeemJwtLink">
|
||||
{{ $t('gdd_per_link.recipientCommunitySelection') }}
|
||||
<BCol v-if="!isRedeemJwtLink" class="fw-bold">
|
||||
<div v-if="isForeignCommunitySelected">
|
||||
{{ $t('gdd_per_link.recipientCommunityRedirection') }}
|
||||
</div>
|
||||
<div v-else>
|
||||
{{ $t('gdd_per_link.recipientCommunitySelection') }}
|
||||
</div>
|
||||
</BCol>
|
||||
<BCol v-else>{{ $t('gdd_per_link.recipientCommunityFix') }}</BCol>
|
||||
</BRow>
|
||||
<h3>
|
||||
<BRow>
|
||||
@ -25,11 +40,7 @@
|
||||
@update:model-value="setRecipientCommunity"
|
||||
/>
|
||||
</BCol>
|
||||
<BCol v-else>
|
||||
{{ currentRecipientCommunity.name }}
|
||||
</BCol>
|
||||
<BCol v-if="isForeignCommunitySelected" sm="12" md="6" class="mt-4 mt-lg-0">
|
||||
<p>{{ $t('gdd_per_link.switchCommunity') }}</p>
|
||||
<BButton variant="gradido" @click="onSwitch">
|
||||
{{ $t('gdd_per_link.to-switch') }}
|
||||
</BButton>
|
||||
@ -37,13 +48,8 @@
|
||||
</BRow>
|
||||
</h3>
|
||||
</BCol>
|
||||
<template v-if="linkData.senderUser">
|
||||
{{ linkData.senderUser.firstName }}
|
||||
{{ $t('transaction-link.send_you') }} {{ $filters.GDD(linkData.amount) }}
|
||||
</template>
|
||||
</h1>
|
||||
<b>{{ linkData.memo }}</b>
|
||||
</BCard>
|
||||
</BRow>
|
||||
</BRow>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
@ -142,16 +148,16 @@ async function onSwitch(event) {
|
||||
// console.log('RedeemCommunitySelection.onSwitch... props=', props)
|
||||
if (isForeignCommunitySelected.value) {
|
||||
// console.log('RedeemCommunitySelection.onSwitch vor createRedeemJwt params:', {
|
||||
// gradidoId: props.linkData.senderUser?.gradidoID,
|
||||
// senderCommunityUuid: senderCommunity.value.uuid,
|
||||
// senderCommunityName: senderCommunity.value.name,
|
||||
// recipientCommunityUuid: currentRecipientCommunity.value.uuid,
|
||||
// code: props.redeemCode,
|
||||
// amount: props.linkData.amount,
|
||||
// memo: props.linkData.memo,
|
||||
// firstName: props.linkData.senderUser?.firstName,
|
||||
// alias: props.linkData.senderUser?.alias,
|
||||
// validUntil: props.linkData.validUntil,
|
||||
// gradidoId: props.linkData.senderUser?.gradidoID,
|
||||
// senderCommunityUuid: senderCommunity.value.uuid,
|
||||
// senderCommunityName: senderCommunity.value.name,
|
||||
// recipientCommunityUuid: currentRecipientCommunity.value.uuid,
|
||||
// code: props.redeemCode,
|
||||
// amount: props.linkData.amount,
|
||||
// memo: props.linkData.memo,
|
||||
// firstName: props.linkData.senderUser?.firstName,
|
||||
// alias: props.linkData.senderUser?.alias,
|
||||
// validUntil: props.linkData.validUntil,
|
||||
// })
|
||||
// eslint-disable-next-line no-useless-catch
|
||||
try {
|
||||
|
||||
@ -1,41 +1,31 @@
|
||||
<template>
|
||||
<div class="redeem-select-community">
|
||||
<redeem-community-selection
|
||||
v-model:recipient-community="recipientCommunity"
|
||||
:link-data="props.linkData"
|
||||
:redeem-code="props.redeemCode"
|
||||
:is-transaction-link-loaded="props.isTransactionLinkLoaded"
|
||||
:is-contribution-link="props.isContributionLink"
|
||||
:is-redeem-jwt-link="props.isRedeemJwtLink"
|
||||
/>
|
||||
|
||||
<BCard v-if="props.isTransactionLinkLoaded">
|
||||
<div class="mb-2">
|
||||
<h2>{{ $t('gdd_per_link.redeem') }}</h2>
|
||||
<redeem-community-selection
|
||||
v-model:recipient-community="recipientCommunity"
|
||||
:link-data="props.linkData"
|
||||
:redeem-code="props.redeemCode"
|
||||
:is-transaction-link-loaded="props.isTransactionLinkLoaded"
|
||||
:is-contribution-link="props.isContributionLink"
|
||||
:is-redeem-jwt-link="props.isRedeemJwtLink"
|
||||
/>
|
||||
<div v-if="!isForeignCommunitySelected">
|
||||
<h2>{{ $t('gdd_per_link.redeem-community') }} {{ recipientCommunity.name }}</h2>
|
||||
<BRow>
|
||||
<BCol sm="12" md="6">
|
||||
<p>{{ $t('gdd_per_link.no-account') }}</p>
|
||||
<BButton variant="primary" :to="routeWithParamsAndQuery('Register')">
|
||||
{{ $t('gdd_per_link.to-register') }}
|
||||
</BButton>
|
||||
</BCol>
|
||||
<BCol sm="12" md="6" class="mt-4 mt-lg-0">
|
||||
<p>{{ $t('gdd_per_link.has-account') }}</p>
|
||||
<BButton variant="gradido" :to="routeWithParamsAndQuery('Login')">
|
||||
{{ $t('gdd_per_link.to-login') }}
|
||||
</BButton>
|
||||
</BCol>
|
||||
</BRow>
|
||||
</div>
|
||||
|
||||
<BRow>
|
||||
<BCol sm="12" md="6">
|
||||
<p>{{ $t('gdd_per_link.no-account') }}</p>
|
||||
<BButton
|
||||
variant="primary"
|
||||
:disabled="isForeignCommunitySelected"
|
||||
:to="routeWithParamsAndQuery('Register')"
|
||||
>
|
||||
{{ $t('gdd_per_link.to-register') }}
|
||||
</BButton>
|
||||
</BCol>
|
||||
<BCol sm="12" md="6" class="mt-4 mt-lg-0">
|
||||
<p>{{ $t('gdd_per_link.has-account') }}</p>
|
||||
<BButton
|
||||
variant="gradido"
|
||||
:disabled="isForeignCommunitySelected"
|
||||
:to="routeWithParamsAndQuery('Login')"
|
||||
>
|
||||
{{ $t('gdd_per_link.to-login') }}
|
||||
</BButton>
|
||||
</BCol>
|
||||
</BRow>
|
||||
</BCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -5,6 +5,8 @@ import { createStore } from 'vuex'
|
||||
import Navbar from './Navbar.vue'
|
||||
import { BImg, BNavbar, BNavbarBrand, BNavbarNav } from 'bootstrap-vue-next'
|
||||
import AppAvatar from '@/components/AppAvatar.vue'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import CONFIG from '@/config'
|
||||
|
||||
// Mock vue-avatar
|
||||
vi.mock('vue-avatar', () => ({
|
||||
@ -17,6 +19,16 @@ vi.mock('vue-avatar', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: 'en',
|
||||
messages: {
|
||||
en: {
|
||||
'copied-to-clipboard': 'copied-to-clipboard',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const createVuexStore = (state = {}) =>
|
||||
createStore({
|
||||
state: () => ({
|
||||
@ -24,6 +36,7 @@ const createVuexStore = (state = {}) =>
|
||||
lastName: 'User',
|
||||
gradidoID: 'current-user-id',
|
||||
email: 'test@example.com',
|
||||
username: 'username',
|
||||
...state,
|
||||
}),
|
||||
})
|
||||
@ -41,7 +54,10 @@ describe('Navbar', () => {
|
||||
store = createVuexStore(storeState)
|
||||
return mount(Navbar, {
|
||||
global: {
|
||||
plugins: [store, router],
|
||||
plugins: [store, router, i18n],
|
||||
stubs: {
|
||||
IBiClipboard: true,
|
||||
},
|
||||
mocks: {
|
||||
$t: (msg) => msg,
|
||||
},
|
||||
@ -89,7 +105,9 @@ describe('Navbar', () => {
|
||||
})
|
||||
|
||||
it('has the email address', () => {
|
||||
expect(wrapper.find('div[data-test="navbar-item-email"]').text()).toBe('test@example.com')
|
||||
expect(wrapper.find('div[data-test="navbar-item-gradido-id"]').text()).toBe(
|
||||
`${CONFIG.COMMUNITY_NAME}/username`,
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -17,9 +17,9 @@
|
||||
<BImg class="sheet-img position-absolute zindex-1" :src="sheet"></BImg>
|
||||
|
||||
<BNavbarNav class="ms-auto" right>
|
||||
<router-link to="/settings">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="me-3">
|
||||
<div class="align-items-center">
|
||||
<router-link to="/settings">
|
||||
<div class="d-flex me-3">
|
||||
<app-avatar
|
||||
class="vue3-avatar"
|
||||
:name="username.username"
|
||||
@ -31,10 +31,19 @@
|
||||
</div>
|
||||
<div>
|
||||
<div data-test="navbar-item-username">{{ username.username }}</div>
|
||||
<div data-test="navbar-item-email">{{ $store.state.email }}</div>
|
||||
</div>
|
||||
</router-link>
|
||||
<div class="small navbar-like-link" data-test="navbar-item-gradido-id">
|
||||
{{ gradidoId }}
|
||||
<a
|
||||
class="copy-clipboard-button"
|
||||
:title="$t('copy-to-clipboard')"
|
||||
@click="copyToClipboard(gradidoId)"
|
||||
>
|
||||
<IBiClipboard></IBiClipboard>
|
||||
</a>
|
||||
</div>
|
||||
</router-link>
|
||||
</div>
|
||||
</BNavbarNav>
|
||||
</BNavbar>
|
||||
<!-- <div class="alert-box">
|
||||
@ -47,11 +56,20 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CONFIG from '@/config'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
export default {
|
||||
name: 'Navbar',
|
||||
props: {
|
||||
balance: { type: Number, required: true },
|
||||
},
|
||||
setup() {
|
||||
const toast = useAppToast()
|
||||
return {
|
||||
toast,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
logo: '/img/brand/gradido-logo.png',
|
||||
@ -65,6 +83,18 @@ export default {
|
||||
initials: `${this.$store.state.firstName[0]}${this.$store.state.lastName[0]}`,
|
||||
}
|
||||
},
|
||||
gradidoId() {
|
||||
const name = this.$store.state.username
|
||||
? this.$store.state.username
|
||||
: this.$store.state.gradidoId
|
||||
return `${CONFIG.COMMUNITY_NAME}/${name}`
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
copyToClipboard(content) {
|
||||
navigator.clipboard.writeText(content)
|
||||
this.toast.toastSuccess(this.$t('gradidoid-copied-to-clipboard'))
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -83,6 +113,10 @@ export default {
|
||||
font-size: 2.25rem;
|
||||
}
|
||||
|
||||
.navbar-like-link {
|
||||
color: rgba(var(--bs-link-color-rgb));
|
||||
}
|
||||
|
||||
button.navbar-toggler > span.navbar-toggler-icon {
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(4, 112, 6, 1)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
9
frontend/src/graphql/communities.graphql
Normal file
9
frontend/src/graphql/communities.graphql
Normal file
@ -0,0 +1,9 @@
|
||||
query reachableCommunities {
|
||||
reachableCommunities {
|
||||
foreign
|
||||
uuid
|
||||
name
|
||||
description
|
||||
url
|
||||
}
|
||||
}
|
||||
@ -91,19 +91,6 @@ export const listGDTEntriesQuery = gql`
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const selectCommunities = gql`
|
||||
query {
|
||||
communities {
|
||||
uuid
|
||||
name
|
||||
description
|
||||
foreign
|
||||
url
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const queryOptIn = gql`
|
||||
query ($optIn: String!) {
|
||||
queryOptIn(optIn: $optIn)
|
||||
|
||||
@ -3,4 +3,10 @@ fragment userFields on User {
|
||||
firstName
|
||||
lastName
|
||||
alias
|
||||
}
|
||||
}
|
||||
|
||||
query user($identifier: String!, $communityIdentifier: String) {
|
||||
user(identifier: $identifier, communityIdentifier: $communityIdentifier) {
|
||||
...userFields
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
"85": "85%",
|
||||
"Chat": "Chat",
|
||||
"GDD": "GDD",
|
||||
"GDD-long": "Gradido",
|
||||
"GDT": "GDT",
|
||||
"GMS": {
|
||||
"title": "Geo Matching System GMS (in Entwicklung)",
|
||||
@ -230,6 +231,7 @@
|
||||
},
|
||||
"gddSendAmount": "Das Feld {_field_} muss eine Zahl zwischen {min} und {max} mit höchstens zwei Nachkommastellen sein.",
|
||||
"identifier": {
|
||||
"communityIsReachable": "Community nicht gefunden oder nicht erreichbar!",
|
||||
"required": "Der Empfänger ist ein Pflichtfeld.",
|
||||
"typeError": "Der Empfänger muss eine Email, ein Nutzernamen oder eine Gradido ID sein."
|
||||
},
|
||||
@ -271,16 +273,16 @@
|
||||
"no-account": "Du besitzt noch kein Gradido Konto?",
|
||||
"no-redeem": "Du darfst deinen eigenen Link nicht einlösen!",
|
||||
"not-copied": "Dein Gerät lässt das Kopieren leider nicht zu! Bitte kopiere den Link von Hand!",
|
||||
"recipientCommunityFix": "Empfänger-Gemeinschaft des Link-Guthabens...",
|
||||
"recipientCommunitySelection": "Wähle deine Gemeinschaft zum Einlösen des Link-Guthabens...",
|
||||
"recipientCommunityRedirection": "Zum Gutschreiben weiterleiten zu:",
|
||||
"recipientCommunitySelection": "Gradido-Gemeinschaft wählen:",
|
||||
"redeem": "Einlösen",
|
||||
"redeem-community": "Gutschreiben in ",
|
||||
"redeemed": "Erfolgreich eingelöst! Deinem Konto wurden {n} GDD gutgeschrieben.",
|
||||
"redeemed-at": "Der Link wurde bereits am {date} eingelöst.",
|
||||
"redeemlink-error": "Dieser Einlöse-Link ist nicht vollständig.",
|
||||
"switchCommunity": "Du hast eine andere Gemeinschaft ausgewählt...",
|
||||
"to-login": "Log dich ein",
|
||||
"to-register": "Registriere ein neues Konto.",
|
||||
"to-switch": "Wechsle zur Gemeinschaft",
|
||||
"to-switch": "Jetzt weiterleiten",
|
||||
"validUntil": "Gültig bis",
|
||||
"validUntilDate": "Der Link ist bis zum {date} gültig."
|
||||
},
|
||||
@ -300,6 +302,7 @@
|
||||
"raise": "Erhöhung",
|
||||
"recruited-member": "Eingeladenes Mitglied"
|
||||
},
|
||||
"gradidoid-copied-to-clipboard": "Gradido ID wurde in die Zwischenablage kopiert.",
|
||||
"h": "h",
|
||||
"info": "Information",
|
||||
"language": "Sprache",
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
"85": "85%",
|
||||
"Chat": "Chat",
|
||||
"GDD": "GDD",
|
||||
"GDD-long": "Gradido",
|
||||
"GDT": "GDT",
|
||||
"GMS": {
|
||||
"title": "Geo Matching System GMS (in develop)",
|
||||
@ -230,6 +231,7 @@
|
||||
},
|
||||
"gddSendAmount": "The {_field_} field must be a number between {min} and {max} with at most two digits after the decimal point.",
|
||||
"identifier": {
|
||||
"communityIsReachable": "Community not found our not reachable!",
|
||||
"required": "The recipient is a required field.",
|
||||
"typeError": "The recipient must be an email, a username or a Gradido ID."
|
||||
},
|
||||
@ -271,16 +273,16 @@
|
||||
"no-account": "You don't have a Gradido account yet?",
|
||||
"no-redeem": "You not allowed to redeem your own link!",
|
||||
"not-copied": "Unfortunately, your device does not allow copying! Please copy the link by hand!",
|
||||
"recipientCommunityFix": "Recipient Community of the Link-Balance...",
|
||||
"recipientCommunitySelection": "Select your Community to redeem the link-deposit...",
|
||||
"recipientCommunityRedirection": "Redirect for crediting to:",
|
||||
"recipientCommunitySelection": "Select Gradido community:",
|
||||
"redeem": "Redeem",
|
||||
"redeem-community": "Credit in ",
|
||||
"redeemed": "Successfully redeemed! Your account has been credited with {n} GDD.",
|
||||
"redeemed-at": "The link was already redeemed on {date}.",
|
||||
"redeemlink-error": "This redemption link is not complete.",
|
||||
"switchCommunity": "You have selected a foreign Community...",
|
||||
"to-login": "Log in",
|
||||
"to-register": "Register a new account.",
|
||||
"to-switch": "Switch to Community",
|
||||
"to-switch": "Redirect now",
|
||||
"validUntil": "Valid until",
|
||||
"validUntilDate": "The link is valid until {date}."
|
||||
},
|
||||
@ -300,6 +302,7 @@
|
||||
"raise": "Increase",
|
||||
"recruited-member": "Invited member"
|
||||
},
|
||||
"gradidoid-copied-to-clipboard": "Gradido ID copied to clipboard.",
|
||||
"h": "h",
|
||||
"info": "Information",
|
||||
"language": "Language",
|
||||
|
||||
@ -185,6 +185,11 @@
|
||||
"to1": "para",
|
||||
"validation": {
|
||||
"gddSendAmount": "El campo {_field_} debe ser un número entre {min} y {max} con un máximo de dos decimales",
|
||||
"identifier": {
|
||||
"communityIsReachable": "Comunidad no encontrada o no alcanzable!",
|
||||
"required": "El destinatario es un campo obligatorio.",
|
||||
"typeError": "El destinatario debe ser un email, un nombre de usuario o un Gradido ID."
|
||||
},
|
||||
"is-not": "No es posible transferirte Gradidos a ti mismo",
|
||||
"requiredField": "El campo {fieldName} es obligatorio",
|
||||
"usernmae-regex": "El nombre de usuario debe comenzar con una letra seguida de al menos dos caracteres alfanuméricos.",
|
||||
@ -216,12 +221,16 @@
|
||||
"no-account": "Aún no tienes una cuenta de Gradido?",
|
||||
"no-redeem": "No puedes canjear tu propio enlace!",
|
||||
"not-copied": "¡Desafortunadamente, su dispositivo no permite copiar! Copie el enlace manualmente!",
|
||||
"recipientCommunityRedirection": "Redirigir para abonar a:",
|
||||
"recipientCommunitySelection": "Seleccionar comunidad Gradido:",
|
||||
"redeem": "Canjear",
|
||||
"redeem-community": "Acreditar en ",
|
||||
"redeemed": "¡Canjeado con éxito! Tu cuenta ha sido acreditada con {n} GDD.",
|
||||
"redeemed-at": "El enlace ya se canjeó el {date}.",
|
||||
"redeemed-title": "canjeado",
|
||||
"to-login": "iniciar sesión",
|
||||
"to-register": "Registre una nueva cuenta.",
|
||||
"to-switch": "Redirigir ahora",
|
||||
"validUntil": "Válido hasta",
|
||||
"validUntilDate": "El enlace es válido hasta el {date} ."
|
||||
},
|
||||
@ -242,6 +251,7 @@
|
||||
"raise": "Aumento",
|
||||
"recruited-member": "Miembro invitado"
|
||||
},
|
||||
"gradidoid-copied-to-clipboard": "Gradido ID copiado al portapapeles.",
|
||||
"info": "Información",
|
||||
"language": "Idioma",
|
||||
"link-load": "recargar el último enlace | recargar los últimos {n} enlaces",
|
||||
|
||||
@ -191,6 +191,11 @@
|
||||
"validation": {
|
||||
"gddCreationTime": "Le champ {_field_} doit comprendre un nombre entre {min} et {max} avec un maximum de une décimale.",
|
||||
"gddSendAmount": "Le champ {_field_} doit comprendre un nombre entre {min} et {max} avec un maximum de deux chiffres après la virgule",
|
||||
"identifier": {
|
||||
"communityIsReachable": "Communauté non trouvée ou non joignable!",
|
||||
"required": "Le destinataire est un champ obligatoire.",
|
||||
"typeError": "Le destinataire doit être un email, un nom d'utilisateur ou un Gradido ID."
|
||||
},
|
||||
"is-not": "Vous ne pouvez pas vous envoyer de Gradido à vous-même",
|
||||
"requiredField": "Le champ {fieldName} est obligatoire",
|
||||
"usernmae-regex": "Le nom d'utilisateur doit commencer par une lettre, suivi d'au moins deux caractères alphanumériques.",
|
||||
@ -222,12 +227,16 @@
|
||||
"no-account": "Vous n'avez pas encore de compte Gradido?",
|
||||
"no-redeem": "Vous n'êtes pas autorisé à percevoir votre propre lien!",
|
||||
"not-copied": "Malheureusement votre appareil ne permet pas de copier! Veuillez copier le lien manuellement svp!",
|
||||
"recipientCommunityRedirection": "Pour créditer, rediriger vers:",
|
||||
"recipientCommunitySelection": "Choisir la communauté Gradido :",
|
||||
"redeem": "Encaisser",
|
||||
"redeem-community": "Créditer dans ",
|
||||
"redeemed": "Encaissé avec succès! Votre compte est crédité de {n} GDD.",
|
||||
"redeemed-at": "Le lien a déjà été perçu le {date}.",
|
||||
"redeemed-title": "encaisser",
|
||||
"to-login": "Connexion",
|
||||
"to-register": "Enregistrer un nouveau compte.",
|
||||
"to-switch": "Rediriger maintenant",
|
||||
"validUntil": "Valide jusqu'au",
|
||||
"validUntilDate": "Le lien est valide jusqu'au {date}."
|
||||
},
|
||||
@ -249,6 +258,7 @@
|
||||
"raise": "Augmentation",
|
||||
"recruited-member": "Membre invité"
|
||||
},
|
||||
"gradidoid-copied-to-clipboard": "Gradido ID copié dans le presse-papier.",
|
||||
"h": "h",
|
||||
"info": "Information",
|
||||
"language": "Langage",
|
||||
|
||||
@ -185,6 +185,11 @@
|
||||
"to1": "aan",
|
||||
"validation": {
|
||||
"gddSendAmount": "Het veld {_field_} moet een getal tussen {min} en {max} met maximaal twee cijfers achter de komma zijn",
|
||||
"identifier": {
|
||||
"communityIsReachable": "Community niet gevonden of niet bereikbaar!",
|
||||
"required": "Ontvanger is een verplicht veld.",
|
||||
"typeError": "Ontvanger moet een email, een gebruikersnaam of een Gradido ID zijn."
|
||||
},
|
||||
"is-not": "Je kunt geen Gradidos aan jezelf overmaken",
|
||||
"requiredField": "{fieldName} is verplicht",
|
||||
"usernmae-regex": "De gebruikersnaam moet met een letter beginnen, waarop minimaal twee alfanumerieke tekens dienen te volgen.",
|
||||
@ -216,12 +221,16 @@
|
||||
"no-account": "Je hebt nog geen Gradido rekening?",
|
||||
"no-redeem": "Je mag je eigen link niet inwisselen!",
|
||||
"not-copied": "Jouw apparaat laat het kopiëren helaas niet toe! Kopieer de link alsjeblieft met de hand!",
|
||||
"recipientCommunityRedirection": "Naar crediteren doorsturen naar:",
|
||||
"recipientCommunitySelection": "Gradido-gemeenschap selecteren:",
|
||||
"redeem": "Inwisselen",
|
||||
"redeem-community": "Crediteren in ",
|
||||
"redeemed": "Succesvol ingewisseld! Op jouw rekening werden {n} GDD bijgeschreven.",
|
||||
"redeemed-at": "De link werd al op {date} ingewisseld.",
|
||||
"redeemed-title": "ingewisseld",
|
||||
"to-login": "Inloggen",
|
||||
"to-register": "Registreer een nieuwe rekening.",
|
||||
"to-switch": "Nu doorsturen",
|
||||
"validUntil": "Geldig tot",
|
||||
"validUntilDate": "De link is geldig tot {date}."
|
||||
},
|
||||
@ -242,6 +251,7 @@
|
||||
"raise": "Verhoging",
|
||||
"recruited-member": "Uitgenodigd lid"
|
||||
},
|
||||
"gradidoid-copied-to-clipboard": "Gradido ID gekopieerd naar klembord.",
|
||||
"info": "Informatie",
|
||||
"language": "Taal",
|
||||
"link-load": "de laatste link herladen | de laatste links herladen",
|
||||
|
||||
@ -164,10 +164,15 @@ const itemType = computed(() => {
|
||||
}
|
||||
if (store.state.token && store.state.tokenTime) {
|
||||
if (tokenExpiresInSeconds.value < 5) {
|
||||
// console.log('TransactionLink.itemType... REDEEM_SELECT_COMMUNITY')
|
||||
// console.log(
|
||||
// 'TransactionLink.itemType... CONFIG.CROSS_TX_REDEEM_LINK_ACTIVE=',
|
||||
// CONFIG.CROSS_TX_REDEEM_LINK_ACTIVE,
|
||||
// )
|
||||
if (CONFIG.CROSS_TX_REDEEM_LINK_ACTIVE) {
|
||||
// console.log('TransactionLink.itemType... REDEEM_SELECT_COMMUNITY')
|
||||
return 'REDEEM_SELECT_COMMUNITY'
|
||||
} else {
|
||||
// console.log('TransactionLink.itemType... LOGGED_OUT')
|
||||
return 'LOGGED_OUT'
|
||||
}
|
||||
}
|
||||
@ -216,10 +221,15 @@ const itemType = computed(() => {
|
||||
return 'VALID'
|
||||
}
|
||||
}
|
||||
// console.log(
|
||||
// 'TransactionLink.itemType... vor last return CONFIG.CROSS_TX_REDEEM_LINK_ACTIVE=',
|
||||
// CONFIG.CROSS_TX_REDEEM_LINK_ACTIVE,
|
||||
// )
|
||||
if (CONFIG.CROSS_TX_REDEEM_LINK_ACTIVE) {
|
||||
// console.log('TransactionLink.itemType...last return= REDEEM_SELECT_COMMUNITY')
|
||||
return 'REDEEM_SELECT_COMMUNITY'
|
||||
} else {
|
||||
// console.log('TransactionLink.itemType... last return= LOGGED_OUT')
|
||||
return 'LOGGED_OUT'
|
||||
}
|
||||
})
|
||||
@ -338,7 +348,7 @@ function setRedeemJwtLinkInformation() {
|
||||
const deepCopy = JSON.parse(JSON.stringify(result.value))
|
||||
// console.log('TransactionLink.setRedeemJwtLinkInformation... deepCopy=', deepCopy)
|
||||
if (deepCopy) {
|
||||
// recipientUser is only set if the user is logged in
|
||||
// console.log('TransactionLink.setRedeemJwtLinkInformation... recipientUser is only set if the user is logged in')
|
||||
if (store.state.gradidoID !== null) {
|
||||
// console.log('TransactionLink.setRedeemJwtLinkInformation... gradidoID=', store.state.gradidoID)
|
||||
deepCopy.queryTransactionLink.recipientUser = {
|
||||
|
||||
@ -28,10 +28,21 @@ export const memo = string()
|
||||
|
||||
export const identifier = string()
|
||||
.required('form.validation.identifier.required')
|
||||
.test(
|
||||
'valid-parts',
|
||||
'form.validation.identifier.partsError',
|
||||
(value) => (value.match(/\//g) || []).length <= 1, // allow only one or zero slash
|
||||
)
|
||||
.test('valid-identifier', 'form.validation.identifier.typeError', (value) => {
|
||||
const isEmail = !!EMAIL_REGEX.test(value)
|
||||
const isUsername = !!value.match(USERNAME_REGEX)
|
||||
let userPart = value
|
||||
const parts = value.split('/')
|
||||
if (parts.length === 2) {
|
||||
userPart = parts[1]
|
||||
}
|
||||
|
||||
const isEmail = !!EMAIL_REGEX.test(userPart)
|
||||
const isUsername = !!userPart.match(USERNAME_REGEX)
|
||||
// TODO: use valibot and rules from shared
|
||||
const isGradidoId = validateUuid(value) && versionUuid(value) === 4
|
||||
const isGradidoId = validateUuid(userPart) && versionUuid(userPart) === 4
|
||||
return isEmail || isUsername || isGradidoId
|
||||
})
|
||||
|
||||
@ -125,7 +125,7 @@ export default defineConfig(async ({ command }) => {
|
||||
GRAPHQL_URI: CONFIG.GRAPHQL_URI, // null,
|
||||
ADMIN_AUTH_PATH: CONFIG.ADMIN_AUTH_PATH ?? null, // it is the only env without exported default
|
||||
ADMIN_AUTH_URL: CONFIG.ADMIN_AUTH_URL, // null,
|
||||
COMMUNITY_NAME: null,
|
||||
COMMUNITY_NAME: CONFIG.COMMUNITY_NAME,
|
||||
COMMUNITY_REGISTER_PATH: null,
|
||||
COMMUNITY_REGISTER_URL: null,
|
||||
COMMUNITY_DESCRIPTION: null,
|
||||
|
||||
@ -13,6 +13,7 @@ export class SendCoinsJwtPayloadType extends JwtPayloadType {
|
||||
senderUserUuid: string
|
||||
senderUserName: string
|
||||
senderAlias?: string | null
|
||||
transactionLinkId?: number | null
|
||||
|
||||
constructor(
|
||||
handshakeID: string,
|
||||
@ -25,6 +26,7 @@ export class SendCoinsJwtPayloadType extends JwtPayloadType {
|
||||
senderUserUuid: string,
|
||||
senderUserName: string,
|
||||
senderAlias?: string | null,
|
||||
transactionLinkId?: number | null,
|
||||
) {
|
||||
super(handshakeID)
|
||||
this.tokentype = SendCoinsJwtPayloadType.SEND_COINS_TYPE
|
||||
@ -37,5 +39,6 @@ export class SendCoinsJwtPayloadType extends JwtPayloadType {
|
||||
this.senderUserUuid = senderUserUuid
|
||||
this.senderUserName = senderUserName
|
||||
this.senderAlias = senderAlias
|
||||
this.transactionLinkId = transactionLinkId
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user