diff --git a/backend/src/auth/ADMIN_RIGHTS.ts b/backend/src/auth/ADMIN_RIGHTS.ts index db0b2a4d1..e95935fd0 100644 --- a/backend/src/auth/ADMIN_RIGHTS.ts +++ b/backend/src/auth/ADMIN_RIGHTS.ts @@ -7,4 +7,5 @@ export const ADMIN_RIGHTS = [ RIGHTS.COMMUNITY_UPDATE, RIGHTS.COMMUNITY_BY_UUID, RIGHTS.COMMUNITY_BY_IDENTIFIER, + RIGHTS.HOME_COMMUNITY, ] diff --git a/backend/src/auth/DLT_CONNECTOR_RIGHTS.ts b/backend/src/auth/DLT_CONNECTOR_RIGHTS.ts index 3186082b8..399b7c2d4 100644 --- a/backend/src/auth/DLT_CONNECTOR_RIGHTS.ts +++ b/backend/src/auth/DLT_CONNECTOR_RIGHTS.ts @@ -1,3 +1,3 @@ import { RIGHTS } from './RIGHTS' -export const DLT_CONNECTOR_RIGHTS = [RIGHTS.COMMUNITY_BY_IDENTIFIER] +export const DLT_CONNECTOR_RIGHTS = [RIGHTS.COMMUNITY_BY_IDENTIFIER, RIGHTS.HOME_COMMUNITY] diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index 670302e1d..c8f02976b 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -60,5 +60,6 @@ export enum RIGHTS { UNDELETE_USER = 'UNDELETE_USER', COMMUNITY_BY_UUID = 'COMMUNITY_BY_UUID', COMMUNITY_BY_IDENTIFIER = 'COMMUNITY_BY_IDENTIFIER', + HOME_COMMUNITY = 'HOME_COMMUNITY', COMMUNITY_UPDATE = 'COMMUNITY_UPDATE', } diff --git a/backend/src/graphql/resolver/CommunityResolver.test.ts b/backend/src/graphql/resolver/CommunityResolver.test.ts index 5c35a8570..6b4da54cf 100644 --- a/backend/src/graphql/resolver/CommunityResolver.test.ts +++ b/backend/src/graphql/resolver/CommunityResolver.test.ts @@ -17,10 +17,10 @@ import { logger, i18n as localization } from '@test/testSetup' import { userFactory } from '@/seeds/factory/user' import { login, updateHomeCommunityQuery } from '@/seeds/graphql/mutations' -import { getCommunities, communitiesQuery, getCommunityQuery } from '@/seeds/graphql/queries' +import { getCommunities, communitiesQuery, getHomeCommunityQuery, getCommunityByIdentifierQuery } from '@/seeds/graphql/queries' import { peterLustig } from '@/seeds/users/peter-lustig' -import { getCommunity } from './util/communities' +import { getCommunityByUuid } from './util/communities' // to do: We need a setup for the tests that closes the connection let mutate: ApolloServerTestClient['mutate'], @@ -460,7 +460,7 @@ describe('CommunityResolver', () => { await mutate({ mutation: login, variables: peterLoginData }) // HomeCommunity is still created in userFactory - homeCom = await getCommunity({ communityIdentifier: admin.communityUuid }) + homeCom = await getCommunityByUuid(admin.communityUuid) foreignCom1 = DbCommunity.create() foreignCom1.foreign = true @@ -490,12 +490,12 @@ describe('CommunityResolver', () => { it('finds the home-community by uuid', async () => { await expect( query({ - query: getCommunityQuery, + query: getCommunityByIdentifierQuery, variables: { communityIdentifier: homeCom?.communityUuid }, }), ).resolves.toMatchObject({ data: { - community: { + communityByIdentifier: { id: homeCom?.id, foreign: homeCom?.foreign, name: homeCom?.name, @@ -509,15 +509,14 @@ describe('CommunityResolver', () => { }) }) - it('finds the home-community by foreign', async () => { + it('finds the home-community', async () => { await expect( query({ - query: getCommunityQuery, - variables: { foreign: false }, + query: getHomeCommunityQuery, }), ).resolves.toMatchObject({ data: { - community: { + homeCommunity: { id: homeCom?.id, foreign: homeCom?.foreign, name: homeCom?.name, diff --git a/backend/src/graphql/resolver/CommunityResolver.ts b/backend/src/graphql/resolver/CommunityResolver.ts index 75ac4e8bc..951a8dcb0 100644 --- a/backend/src/graphql/resolver/CommunityResolver.ts +++ b/backend/src/graphql/resolver/CommunityResolver.ts @@ -1,9 +1,8 @@ import { IsNull, Not } from '@dbTools/typeorm' import { Community as DbCommunity } from '@entity/Community' import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' -import { Resolver, Query, Authorized, Mutation, Args } from 'type-graphql' +import { Resolver, Query, Authorized, Mutation, Args, Arg } from 'type-graphql' -import { CommunityArgs } from '@arg//CommunityArgs' import { EditCommunityInput } from '@input/EditCommunityInput' import { Community } from '@model/Community' import { FederatedCommunity } from '@model/FederatedCommunity' @@ -11,7 +10,7 @@ import { FederatedCommunity } from '@model/FederatedCommunity' import { RIGHTS } from '@/auth/RIGHTS' import { LogError } from '@/server/LogError' -import { getCommunity } from './util/communities' +import { getCommunityByIdentifier, getCommunityByUuid, getHomeCommunity } from './util/communities' @Resolver() export class CommunityResolver { @@ -44,10 +43,20 @@ export class CommunityResolver { @Authorized([RIGHTS.COMMUNITY_BY_IDENTIFIER]) @Query(() => Community) - async community(@Args() communityArgs: CommunityArgs): Promise { - const community = await getCommunity(communityArgs) + async communityByIdentifier(@Arg('communityIdentifier') communityIdentifier: string): Promise { + const community = await getCommunityByIdentifier(communityIdentifier) if (!community) { - throw new LogError('community not found', communityArgs) + throw new LogError('community not found', communityIdentifier) + } + return new Community(community) + } + + @Authorized([RIGHTS.HOME_COMMUNITY]) + @Query(() => Community) + async homeCommunity(): Promise { + const community = await getHomeCommunity() + if (!community) { + throw new LogError('no home community exist') } return new Community(community) } @@ -55,7 +64,7 @@ export class CommunityResolver { @Authorized([RIGHTS.COMMUNITY_UPDATE]) @Mutation(() => Community) async updateHomeCommunity(@Args() { uuid, gmsApiKey }: EditCommunityInput): Promise { - const homeCom = await getCommunity({ communityIdentifier: uuid }) + const homeCom = await getCommunityByUuid(uuid) if (!homeCom) { throw new LogError('HomeCommunity with uuid not found: ', uuid) } diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 6db74f0f5..56c928995 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -38,7 +38,7 @@ import { calculateBalance } from '@/util/validate' import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualTransactions' import { BalanceResolver } from './BalanceResolver' -import { getCommunity, getCommunityName, isHomeCommunity } from './util/communities' +import { getCommunityByIdentifier, getCommunityName, isHomeCommunity } from './util/communities' import { findUserByIdentifier } from './util/findUserByIdentifier' import { getLastTransaction } from './util/getLastTransaction' import { getTransactionList } from './util/getTransactionList' @@ -452,7 +452,7 @@ export class TransactionResolver { if (!CONFIG.FEDERATION_XCOM_SENDCOINS_ENABLED) { throw new LogError('X-Community sendCoins disabled per configuration!') } - const recipCom = await getCommunity({ communityIdentifier: recipientCommunityIdentifier }) + const recipCom = await getCommunityByIdentifier(recipientCommunityIdentifier) logger.debug('recipient commuity: ', recipCom) if (recipCom === null) { throw new LogError( diff --git a/backend/src/graphql/resolver/util/communities.ts b/backend/src/graphql/resolver/util/communities.ts index 0d8850181..790a4d67c 100644 --- a/backend/src/graphql/resolver/util/communities.ts +++ b/backend/src/graphql/resolver/util/communities.ts @@ -1,56 +1,14 @@ -import { FindOptionsWhere } from '@dbTools/typeorm' +import { FindOneOptions } from '@dbTools/typeorm' import { Community as DbCommunity } from '@entity/Community' -import { isURL } from 'class-validator' -import { CommunityArgs } from '@/graphql/arg/CommunityArgs' -import { LogError } from '@/server/LogError' -import { isUUID4 } from '@/util/validate' - -function getCommunityFindOptions({ - communityIdentifier, - foreign, -}: CommunityArgs): FindOptionsWhere { - if (communityIdentifier === undefined && foreign === undefined) { - throw new LogError('one of communityIdentifier or foreign must be set') +function findWithCommunityIdentifier(communityIdentifier: string): FindOneOptions { + return { + where: [ + { communityUuid: communityIdentifier }, + { name: communityIdentifier }, + { url: communityIdentifier }, + ], } - - const where: FindOptionsWhere = {} - if (communityIdentifier != null) { - if (isURL(communityIdentifier)) { - where.url = communityIdentifier - } else if (isUUID4(communityIdentifier)) { - where.communityUuid = communityIdentifier - } else { - where.name = communityIdentifier - } - } - - if (typeof foreign === 'boolean') { - where.foreign = foreign - } - return where -} - -/** - * Retrieves a community from the database based on the provided identifier and foreign status. - * @param communityIdentifier The identifier (URL, UUID, or name) of the community - * @param foreign Optional. If provided it represents the foreign status of the community. - * @returns A promise that resolves to a DbCommunity object if found, or null if not found. - */ -export async function getCommunity(communityArgs: CommunityArgs): Promise { - return DbCommunity.findOne({ where: getCommunityFindOptions(communityArgs) }) -} - -/** - * Retrieves a community from the database based on the provided identifier and foreign status. - * @param communityIdentifier The identifier (URL, UUID, or name) of the community - * @param foreign Optional. If provided it represents the foreign status of the community. - * @returns A promise that resolves to a DbCommunity object if found, or throw if not found. - */ -export async function getCommunityOrFail(communityArgs: CommunityArgs): Promise { - return DbCommunity.findOneOrFail({ - where: getCommunityFindOptions(communityArgs), - }) } /** @@ -59,7 +17,15 @@ export async function getCommunityOrFail(communityArgs: CommunityArgs): Promise< * @returns A promise that resolves to true if a non-foreign community exists with the given identifier, otherwise false. */ export async function isHomeCommunity(communityIdentifier: string): Promise { - return (await getCommunity({ communityIdentifier, foreign: false })) !== null + // The !! operator in JavaScript or TypeScript is a shorthand for converting a value to a boolean. + // It essentially converts any truthy value to true and any falsy value to false. + return !!(await DbCommunity.findOne({ + where: [ + { foreign: false, communityUuid: communityIdentifier }, + { foreign: false, name: communityIdentifier }, + { foreign: false, url: communityIdentifier }, + ], + })) } /** @@ -67,35 +33,56 @@ export async function isHomeCommunity(communityIdentifier: string): Promise { - return getCommunityOrFail({ foreign: false }) + return await DbCommunity.findOneOrFail({ + where: [{ foreign: false }], + }) } /** + * TODO: Check if it is needed, because currently it isn't used at all * Retrieves the URL of the community with the given identifier. * @param communityIdentifier The identifier (URL, UUID, or name) of the community. * @returns A promise that resolves to the URL of the community or throw if no community with this identifier was found */ export async function getCommunityUrl(communityIdentifier: string): Promise { - return (await getCommunityOrFail({ communityIdentifier })).url + return (await DbCommunity.findOneOrFail(findWithCommunityIdentifier(communityIdentifier))).url } /** + * TODO: Check if it is needed, because currently it isn't used at all * Checks if a community with the given identifier exists and has an authenticatedAt property set. * @param communityIdentifier The identifier (URL, UUID, or name) of the community. * @returns A promise that resolves to true if a community with an authenticatedAt property exists with the given identifier, * otherwise false. */ export async function isCommunityAuthenticated(communityIdentifier: string): Promise { - // The !! operator is used to convert the result to a boolean value. - return !!(await getCommunity({ communityIdentifier }))?.authenticatedAt + // The !! operator in JavaScript or TypeScript is a shorthand for converting a value to a boolean. + // It essentially converts any truthy value to true and any falsy value to false. + return !!(await DbCommunity.findOne(findWithCommunityIdentifier(communityIdentifier))) + ?.authenticatedAt } /** * Retrieves the name of the community with the given identifier. - * @param communityIdentifier The identifier (URL, UUID, or name) of the community. + * @param communityIdentifier The identifier (URL, UUID) of the community. * @returns A promise that resolves to the name of the community. If the community does not exist or has no name, * an empty string is returned. */ export async function getCommunityName(communityIdentifier: string): Promise { - return (await getCommunity({ communityIdentifier }))?.name ?? '' + const community = await DbCommunity.findOne({ + where: [{ communityUuid: communityIdentifier }, { url: communityIdentifier }], + }) + + return community?.name ? community.name : '' +} +export async function getCommunityByUuid(communityUuid: string): Promise { + return await DbCommunity.findOne({ + where: [{ communityUuid }], + }) +} + +export async function getCommunityByIdentifier( + communityIdentifier: string, +): Promise { + return await DbCommunity.findOne(findWithCommunityIdentifier(communityIdentifier)) } diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 49dec61b5..0afbc2db2 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -134,9 +134,25 @@ export const communitiesQuery = gql` } ` -export const getCommunityQuery = gql` - query ($communityIdentifier: String, $foreign: Boolean) { - community(communityIdentifier: $communityIdentifier, foreign: $foreign) { +export const getCommunityByIdentifierQuery = gql` + query ($communityIdentifier: String!) { + communityByIdentifier(communityIdentifier: $communityIdentifier) { + id + foreign + name + description + url + creationDate + uuid + authenticatedAt + gmsApiKey + } + } +` + +export const getHomeCommunityQuery = gql` + query { + homeCommunity { id foreign name diff --git a/dlt-connector/src/client/BackendClient.ts b/dlt-connector/src/client/BackendClient.ts index 68745fd5a..77356f5d8 100644 --- a/dlt-connector/src/client/BackendClient.ts +++ b/dlt-connector/src/client/BackendClient.ts @@ -8,9 +8,9 @@ import { CommunityDraft } from '@/graphql/input/CommunityDraft' import { logger } from '@/logging/logger' import { LogError } from '@/server/LogError' -const communityByForeign = gql` - query ($foreign: Boolean) { - community(foreign: $foreign) { +const homeCommunity = gql` + query { + homeCommunity { uuid foreign creationDate @@ -18,7 +18,7 @@ const communityByForeign = gql` } ` interface Community { - community: { + homeCommunity: { uuid: string foreign: boolean creationDate: string @@ -75,19 +75,19 @@ export class BackendClient { public async getHomeCommunityDraft(): Promise { logger.info('check home community on backend') const { data, errors } = await this.client.rawRequest( - communityByForeign, + homeCommunity, + {}, { - foreign: false, + authorization: 'Bearer ' + (await this.createJWTToken()), }, - { authorization: 'Bearer ' + (await this.createJWTToken()) }, ) if (errors) { throw new LogError('error getting home community from backend', errors) } const communityDraft = new CommunityDraft() - communityDraft.uuid = data.community.uuid - communityDraft.foreign = data.community.foreign - communityDraft.createdAt = data.community.creationDate + communityDraft.uuid = data.homeCommunity.uuid + communityDraft.foreign = data.homeCommunity.foreign + communityDraft.createdAt = data.homeCommunity.creationDate return communityDraft } diff --git a/dlt-connector/src/index.ts b/dlt-connector/src/index.ts index 28b8b0a0e..bec35e1df 100644 --- a/dlt-connector/src/index.ts +++ b/dlt-connector/src/index.ts @@ -49,7 +49,7 @@ async function main() { throw new LogError('cannot create backend client') } // wait for backend server to be ready - await waitForServer(backend, 1000, 8) + await waitForServer(backend, 2500, 10) const communityDraft = await backend.getHomeCommunityDraft() const addCommunityContext = new AddCommunityContext(communityDraft)