Skip to content

Commit

Permalink
fix(user): Implements fixes to correctly rebuild user in cache.
Browse files Browse the repository at this point in the history
- Separates retrieval of graph and transaction data from cache.
- Improves typing of interfaces to handle data changes in graph.
- Appends personal information to graph data (previously unhandled).
- Handle formatting of dates appropriately for reconstructed statements.

Closes #105
  • Loading branch information
mango-habanero committed Nov 27, 2023
1 parent b854dee commit 5da8969
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 20 deletions.
54 changes: 41 additions & 13 deletions src/lib/graph/user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { GraphQLClient } from 'graphql-request';
import { Redis as RedisClient } from 'ioredis';
import { UserService } from '@services/user';
import {handleResults} from "@lib/ussd";


export enum Gender { MALE = 'MALE', FEMALE = 'FEMALE' }
Expand Down Expand Up @@ -46,7 +47,7 @@ export interface GraphMarketplace {

export interface GraphTransaction {
sender_address: string;
date_block: number;
date_block: string;
recipient_address: string;
tx_hash: string;
tx_value: number;
Expand Down Expand Up @@ -207,14 +208,9 @@ export async function getGraphPersonalInformation(address: string, graphql: Grap
return data.personal_information[0]
}

export async function getFullGraphUserData(
address: string,
graphql: GraphQLClient,
interfaceIdentifier: string,
activated = true,
transactionsLimit = 9,
transactionSuccess = true){
const query = `query retrieveFullUserGraphData($activated: Boolean!, $address: String!, $interfaceIdentifier: String!, $transactionSuccess: Boolean!, $transactionsLimit: Int!) {

async function retrieveUserGraphData(graphql: GraphQLClient, interfaceIdentifier: string, activated = true) {
const query = `query retrieveUserGraphData($activated: Boolean!, $interfaceIdentifier: String!) {
users(where: {interface_identifier: {_eq: $interfaceIdentifier}, activated: {_eq: $activated}, interface_type: {_eq: USSD}}) {
id
accounts(limit: 1){
Expand All @@ -225,6 +221,19 @@ export async function getFullGraphUserData(
}
personal_information{ ${personalInformationFields} }
}
}`

const variables = {
activated,
interfaceIdentifier
}

return await graphql.request<{ users: GraphUser[] }>(query, variables)
}


async function retrieveUserGraphTransactions(graphql: GraphQLClient, address: string, transactionSuccess: boolean, transactionsLimit: number) {
const query = `query retrieveUserGraphTransactions($address: String!, $transactionSuccess: Boolean!, $transactionsLimit: Int!) {
transactions(where: {_or: [{recipient_address: {_eq: $address}}, {sender_address: {_eq: $address}}], success: {_eq: $transactionSuccess}}, limit: $transactionsLimit, order_by: {date_block: desc}) {
recipient_address
sender_address
Expand All @@ -234,16 +243,35 @@ export async function getFullGraphUserData(
date_block
tx_hash
}
}`;
}`

const variables = {
activated,
address,
interfaceIdentifier,
transactionSuccess,
transactionsLimit
}
return await graphql.request<{ users: GraphUser[], transactions: GraphTransaction[] }>(query, variables)

return await graphql.request<{ transactions: GraphTransaction[] }>(query, variables)
}


export async function getFullGraphUserData(
address: string,
graphql: GraphQLClient,
interfaceIdentifier: string,
activated = true,
transactionsLimit = 9,
transactionSuccess = true){

const promises: Promise<any>[] = []
promises.push(retrieveUserGraphData(graphql, interfaceIdentifier, activated))
promises.push(retrieveUserGraphTransactions(graphql, address, transactionSuccess, transactionsLimit))

const results = await handleResults(await Promise.allSettled(promises))
return {
users: results[0].users,
transactions: results[1].transactions
}
}

export async function updateGraphUser(graphql: GraphQLClient, id: number, user: Partial<GraphUser>): Promise<Partial<GraphUser>> {
Expand Down
12 changes: 6 additions & 6 deletions src/services/transfer.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { CachedVoucher, cashRounding, formatDate, getVoucherSymbol, handleResults } from '@lib/ussd';
import { CachedVoucher, cashRounding, getVoucherSymbol, handleResults } from '@lib/ussd';
import { PostgresDb } from '@fastify/postgres';
import { Redis as RedisClient } from 'ioredis';
import { ethers } from 'ethers';
import { getPhoneNumberFromAddress } from '@services/account';
import { getUserTag, User } from '@services/user';
import {generateUserTag, User} from '@services/user';
import { GraphQLClient } from 'graphql-request';
import { GraphTransaction } from '@lib/graph/user';
import { tHelpers } from '@i18n/translators';
Expand All @@ -25,12 +25,12 @@ export interface Transaction {
value: number;
}

async function getTransferUserTag(address: string, db: PostgresDb, redis: RedisClient) {
async function getTransferUserTag(address: string, db: PostgresDb, graphql: GraphQLClient, redis: RedisClient) {
const phoneNumber = await getPhoneNumberFromAddress(address, db, redis);
if (!phoneNumber) {
return null;
}
return await getUserTag(phoneNumber, redis);
return await generateUserTag(address, graphql, phoneNumber);
}

export async function generateStatement(
Expand All @@ -55,7 +55,7 @@ export async function generateStatement(

const userTags = await Promise.allSettled(
Array.from(addressSet).map(async (addr) => {
const userTag = await getTransferUserTag(addr, db, redis);
const userTag = await getTransferUserTag(addr, db, graphql, redis);
return [addr, userTag];
}),
);
Expand Down Expand Up @@ -84,7 +84,7 @@ export async function generateStatement(
recipient,
value: cashRounding(ethers.formatUnits(transaction.tx_value, 6)),
symbol,
timestamp: await formatDate(transaction.date_block),
timestamp: transaction.date_block,
transactionHash: transaction.tx_hash,
type: transactionType,
};
Expand Down
12 changes: 11 additions & 1 deletion src/services/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ async function parseFullGraphUser(users: Partial<GraphUser>[]) {
graph.account.vpas = account.vpas
}

if(user.personal_information) {
graph.personalInformation = user.personal_information
}

return graph
}

Expand Down Expand Up @@ -138,11 +142,17 @@ class ReconstructionService {
let activeVoucher;
if (heldVouchers.length > 0) {
activeVoucher = heldVouchers.find((voucher) => voucher.address === this.account.active_voucher_address);
if (!activeVoucher) {
console.log(`Could not find active voucher ${this.account.active_voucher_address} in held vouchers, reconstructing active voucher.`)
activeVoucher = await this.reconstructActiveVoucher();
heldVouchers = [activeVoucher, ...heldVouchers];
}
} else {
activeVoucher = await this.reconstructActiveVoucher();
heldVouchers = [activeVoucher];
}


if (!activeVoucher) {
throw new SystemError(`Could not reconstruct active voucher for ${this.account.address}`);
}
Expand Down Expand Up @@ -209,7 +219,7 @@ export class UserService {
const symbolMap = await generateSymbolMap(graphql, this.redis, transactions);
const [activeVoucher, heldVouchers] = await reconstructionService.reconstructVouchers(symbolMap);
const statement = await reconstructionService.reconstructStatement(db, transactions);
const tag = await getUserTag(account.phone_number, this.redis);
const tag = await generateUserTag(account.address, graphql, account.phone_number);

let user: DeepPartial<User> = { account, graph, tag, vouchers: { active: activeVoucher, held: heldVouchers } }

Expand Down

0 comments on commit 5da8969

Please sign in to comment.