From 7aa7271c9fdca20f577a88827d7aee6fad4b7b88 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Mon, 25 Apr 2022 22:29:50 -0400 Subject: [PATCH] Update txn type --- common/charity.ts | 13 +++++--- common/txn.ts | 9 ++---- functions/src/transact.ts | 65 ++++++++++++++++++++++----------------- 3 files changed, 47 insertions(+), 40 deletions(-) diff --git a/common/charity.ts b/common/charity.ts index 086b65e9..5b5a1e64 100644 --- a/common/charity.ts +++ b/common/charity.ts @@ -1,4 +1,5 @@ export interface Charity { + id: string slug: string // Note, slugs double as charity IDs name: string website: string @@ -232,7 +233,11 @@ export const charities: Charity[] = [ blurb: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.", }, -].map((charity) => ({ - ...charity, - slug: charity.name.replace(/\s/g, '-'), -})) +].map((charity) => { + const slug = charity.name.toLowerCase().replace(/\s/g, '-') + return { + ...charity, + id: slug, + slug, + } +}) diff --git a/common/txn.ts b/common/txn.ts index 29ae08ac..f8b54e15 100644 --- a/common/txn.ts +++ b/common/txn.ts @@ -5,15 +5,10 @@ export type Txn = { createdTime: number fromId: string - // TODO: Do we really want to denormalize name/username/avatar here? - fromName: string - fromUsername: string - fromAvatarUrl?: string + fromType: 'user' | 'contract' | 'bank_of_manifold' toId: string - toName: string - toUsername: string - toAvatarUrl?: string + toType: 'user' | 'contract' | 'charity' | 'bank_of_manifold' amount: number diff --git a/functions/src/transact.ts b/functions/src/transact.ts index 2ba2571d..55b77999 100644 --- a/functions/src/transact.ts +++ b/functions/src/transact.ts @@ -2,30 +2,44 @@ import * as functions from 'firebase-functions' import * as admin from 'firebase-admin' import { User } from '../../common/user' -import { Txn, TxnCategory, TxnData } from '../../common/txn' +import { Txn } from '../../common/txn' +import { removeUndefinedProps } from '../../common/util/object' -export const transact = functions.runWith({ minInstances: 1 }).https.onCall( - async ( - data: { - amount: number - toId: string - category: TxnCategory - description?: string - txnData?: TxnData - }, - context - ) => { - const fromId = context?.auth?.uid - if (!fromId) return { status: 'error', message: 'Not authorized' } +export const transact = functions + .runWith({ minInstances: 1 }) + .https.onCall(async (data: Exclude, context) => { + const userId = context?.auth?.uid + if (!userId) return { status: 'error', message: 'Not authorized' } - const { amount, toId, category, description, txnData } = data + const { + amount, + fromType, + fromId, + toId, + toType, + category, + description, + data: txnData, + } = data + + if (fromType !== 'user') + return { + status: 'error', + message: "From type is only implemented for type 'user'.", + } + + if (fromId !== userId) + return { + status: 'error', + message: 'Must be authenticated with userId equal to specified fromId.', + } if (amount <= 0 || isNaN(amount) || !isFinite(amount)) return { status: 'error', message: 'Invalid amount' } - // run as transaction to prevent race conditions + // Run as transaction to prevent race conditions. return await firestore.runTransaction(async (transaction) => { - const fromDoc = firestore.doc(`users/${fromId}`) + const fromDoc = firestore.doc(`users/${userId}`) const fromSnap = await transaction.get(fromDoc) if (!fromSnap.exists) { return { status: 'error', message: 'User not found' } @@ -48,26 +62,20 @@ export const transact = functions.runWith({ minInstances: 1 }).https.onCall( const newTxnDoc = firestore.collection(`txns/`).doc() - const txn: Txn = { + const txn: Txn = removeUndefinedProps({ id: newTxnDoc.id, createdTime: Date.now(), - fromId, - fromName: fromUser.name, - fromUsername: fromUser.username, - fromAvatarUrl: fromUser.avatarUrl, - + fromType, toId, - toName: toUser.name, - toUsername: toUser.username, - toAvatarUrl: toUser.avatarUrl, + toType, amount, category, description, data: txnData, - } + }) transaction.create(newTxnDoc, txn) transaction.update(fromDoc, { balance: fromUser.balance - amount }) @@ -75,7 +83,6 @@ export const transact = functions.runWith({ minInstances: 1 }).https.onCall( return { status: 'success', txnId: newTxnDoc.id } }) - } -) + }) const firestore = admin.firestore()