From ec9c78dbeff5a24ce936e7e71e20fd0898dbd3c4 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 23 Aug 2023 00:58:31 +0200 Subject: [PATCH] first draft of graphql client SendCoins --- .../federation/client/1_0/SendCoinsClient.ts | 69 +++++++++++++++++++ .../client/1_0/model/SendCoinsArgs.ts | 26 +++++++ .../client/1_0/query/revertSendCoins.ts | 25 +++++++ .../client/1_0/query/voteForSendCoins.ts | 25 +++++++ .../federation/client/1_1/SendCoinsClient.ts | 5 ++ .../client/SendCoinsClientFactory.ts | 62 +++++++++++++++++ 6 files changed, 212 insertions(+) create mode 100644 backend/src/federation/client/1_0/SendCoinsClient.ts create mode 100644 backend/src/federation/client/1_0/model/SendCoinsArgs.ts create mode 100644 backend/src/federation/client/1_0/query/revertSendCoins.ts create mode 100644 backend/src/federation/client/1_0/query/voteForSendCoins.ts create mode 100644 backend/src/federation/client/1_1/SendCoinsClient.ts create mode 100644 backend/src/federation/client/SendCoinsClientFactory.ts diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts new file mode 100644 index 000000000..0362d9c27 --- /dev/null +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -0,0 +1,69 @@ +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' +import { GraphQLClient } from 'graphql-request' + +import { LogError } from '@/server/LogError' +import { backendLogger as logger } from '@/server/logger' + +import { SendCoinsArgs } from './model/SendCoinsArgs' +import { revertSendCoins } from './query/revertSendCoins' +import { voteForSendCoins } from './query/voteForSendCoins' + +// eslint-disable-next-line camelcase +export class SendCoinsClient { + dbCom: DbFederatedCommunity + endpoint: string + client: GraphQLClient + + constructor(dbCom: DbFederatedCommunity) { + this.dbCom = dbCom + this.endpoint = `${dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/'}${ + dbCom.apiVersion + }/` + this.client = new GraphQLClient(this.endpoint, { + method: 'GET', + jsonSerializer: { + parse: JSON.parse, + stringify: JSON.stringify, + }, + }) + } + + voteForSendCoins = async (args: SendCoinsArgs): Promise => { + logger.debug('X-Com: voteForSendCoins against endpoint', this.endpoint) + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const { data } = await this.client.rawRequest(voteForSendCoins, { args }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (!data?.voteForSendCoins?.vote) { + logger.warn('X-Com: voteForSendCoins without response data from endpoint', this.endpoint) + return false + } + logger.debug( + 'X-Com: voteForSendCoins successful from endpoint', + this.endpoint, + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + data.voteForSendCoins.vote, + ) + return true + } catch (err) { + throw new LogError(`X-Com: voteForSendCoins failed for endpoint=${this.endpoint}:`, err) + } + } + + revertSendCoins = async (args: SendCoinsArgs): Promise => { + logger.debug(`X-Com: revertSendCoins against endpoint='${this.endpoint}'...`) + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const { data } = await this.client.rawRequest(revertSendCoins, { args }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (!data?.revertSendCoins?.acknowledged) { + logger.warn('X-Com: revertSendCoins without response data from endpoint', this.endpoint) + return false + } + logger.debug(`X-Com: revertSendCoins successful from endpoint=${this.endpoint}`) + return true + } catch (err) { + throw new LogError(`X-Com: revertSendCoins failed for endpoint=${this.endpoint}`, err) + } + } +} diff --git a/backend/src/federation/client/1_0/model/SendCoinsArgs.ts b/backend/src/federation/client/1_0/model/SendCoinsArgs.ts new file mode 100644 index 000000000..2ba743368 --- /dev/null +++ b/backend/src/federation/client/1_0/model/SendCoinsArgs.ts @@ -0,0 +1,26 @@ +import { Decimal } from 'decimal.js-light' +import { ArgsType, Field, Int } from 'type-graphql' + +@ArgsType() +export class SendCoinsArgs { + @Field(() => String) + communityReceiverIdentifier: string + + @Field(() => String) + userReceiverIdentifier: string + + @Field(() => Decimal) + amount: Decimal + + @Field(() => String) + memo: string + + @Field(() => String) + communitySenderIdentifier: string + + @Field(() => String) + userSenderIdentifier: string + + @Field(() => String) + userSenderName: string +} diff --git a/backend/src/federation/client/1_0/query/revertSendCoins.ts b/backend/src/federation/client/1_0/query/revertSendCoins.ts new file mode 100644 index 000000000..fd74feef1 --- /dev/null +++ b/backend/src/federation/client/1_0/query/revertSendCoins.ts @@ -0,0 +1,25 @@ +import { gql } from 'graphql-request' + +export const revertSendCoins = gql` + mutation ( + $communityReceiverIdentifier: String! + $userReceiverIdentifier: String! + $amount: Decimal! + $memo: String! + $communitySenderIdentifier: String! + $userSenderIdentifier: String! + $userSenderName: String! + ) { + revertSendCoins( + communityReceiverIdentifier: $communityReceiverIdentifier + userReceiverIdentifier: $userReceiverIdentifier + amount: $amount + memo: $memo + communitySenderIdentifier: $communitySenderIdentifier + userSenderIdentifier: $userSenderIdentifier + userSenderName: $userSenderName + ) { + acknowledged + } + } +` diff --git a/backend/src/federation/client/1_0/query/voteForSendCoins.ts b/backend/src/federation/client/1_0/query/voteForSendCoins.ts new file mode 100644 index 000000000..4f4dd892a --- /dev/null +++ b/backend/src/federation/client/1_0/query/voteForSendCoins.ts @@ -0,0 +1,25 @@ +import { gql } from 'graphql-request' + +export const voteForSendCoins = gql` + mutation ( + $communityReceiverIdentifier: String! + $userReceiverIdentifier: String! + $amount: Decimal! + $memo: String! + $communitySenderIdentifier: String! + $userSenderIdentifier: String! + $userSenderName: String! + ) { + voteForSendCoins( + communityReceiverIdentifier: $communityReceiverIdentifier + userReceiverIdentifier: $userReceiverIdentifier + amount: $amount + memo: $memo + communitySenderIdentifier: $communitySenderIdentifier + userSenderIdentifier: $userSenderIdentifier + userSenderName: $userSenderName + ) { + vote + } + } +` diff --git a/backend/src/federation/client/1_1/SendCoinsClient.ts b/backend/src/federation/client/1_1/SendCoinsClient.ts new file mode 100644 index 000000000..586f8561e --- /dev/null +++ b/backend/src/federation/client/1_1/SendCoinsClient.ts @@ -0,0 +1,5 @@ +// eslint-disable-next-line camelcase +import { SendCoinsClient as V1_0_SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient' + +// eslint-disable-next-line camelcase +export class SendCoinsClient extends V1_0_SendCoinsClient {} diff --git a/backend/src/federation/client/SendCoinsClientFactory.ts b/backend/src/federation/client/SendCoinsClientFactory.ts new file mode 100644 index 000000000..2c7b90f01 --- /dev/null +++ b/backend/src/federation/client/SendCoinsClientFactory.ts @@ -0,0 +1,62 @@ +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' + +// eslint-disable-next-line camelcase +import { SendCoinsClient as V1_0_SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient' +// eslint-disable-next-line camelcase +import { SendCoinsClient as V1_1_SendCoinsClient } from '@/federation/client/1_1/SendCoinsClient' +import { ApiVersionType } from '@/federation/enum/apiVersionType' + +// eslint-disable-next-line camelcase +type SendCoinsClient = V1_0_SendCoinsClient | V1_1_SendCoinsClient + +interface SendCoinsClientInstance { + id: number + // eslint-disable-next-line no-use-before-define + client: SendCoinsClient +} + +// eslint-disable-next-line @typescript-eslint/no-extraneous-class +export class SendCoinsClientFactory { + private static instanceArray: SendCoinsClientInstance[] = [] + + /** + * The Singleton's constructor should always be private to prevent direct + * construction calls with the `new` operator. + */ + // eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function + private constructor() {} + + private static createSendCoinsClient = (dbCom: DbFederatedCommunity) => { + switch (dbCom.apiVersion) { + case ApiVersionType.V1_0: + return new V1_0_SendCoinsClient(dbCom) + case ApiVersionType.V1_1: + return new V1_1_SendCoinsClient(dbCom) + default: + return null + } + } + + /** + * The static method that controls the access to the singleton instance. + * + * This implementation let you subclass the Singleton class while keeping + * just one instance of each subclass around. + */ + public static getInstance(dbCom: DbFederatedCommunity): SendCoinsClient | null { + const instance = SendCoinsClientFactory.instanceArray.find( + (instance) => instance.id === dbCom.id, + ) + if (instance) { + return instance.client + } + const client = SendCoinsClientFactory.createSendCoinsClient(dbCom) + if (client) { + SendCoinsClientFactory.instanceArray.push({ + id: dbCom.id, + client, + } as SendCoinsClientInstance) + } + return client + } +}