guys!
i am developing an app similar to https://airtasker.com where users outsource tasks. the taskers would bid to the tasks, and wait for the user to approve their bids.
these are the involved collections:
- tasks
- transactions
- bids
basically, this function should:
check if a transaction exists with the given taskId. a transaction is added if the user starts to approve bids. i allow multiple taskers to complete the task.
if a transaction doesn't exist, it should add a new one, mark the status ongoing if it reaches the required manpower (otherwise pending), and update the bids collection to mark the bid accepted.
if a transaction exists, it should check if the current approved list from the transactions collection is equal to the manpower
if it hasn't reached the quota manpower yet, push a new tasker and access the bids collection to mark the bid accepted.
if after the last condition, the approved list already reached the quota manpower, mark the task close, and change the status of the transaction as ongoing
but i keep getting this error:
Uncaught (in promise) Error: Every document read in a transaction must also be written. at Transaction.commit (transaction.js:128) at eval (sync_engine.js:244)
here's my code:
const acceptOffer = async (taskerId, taskId, bidId, offer) => {
let bulk
try {
const taskRef = db.collection('tasks').doc(taskId)
const transRef = db.collection('transactions').doc(taskId)
const bidRef = db.collection('bids').doc(bidId)
const fees = solveFees(offer)
bulk = await db
.runTransaction(async t => {
const transdoc = await t.get(transRef)
const taskdoc = await t.get(taskRef)
const manpower = await taskdoc.get('manpower')
let status = 'pending'
if (manpower === 1) {
status = 'ongoing'
}
if (!transdoc.exists) {
t.set(transRef, {
taskId,
status, // pending, ongoing, completed
approved: [
{ taskerId, ...fees }
]
})
t.update(bidRef, {
accepted: true
})
} else {
const approved = await transdoc.get('approved')
if (manpower < approved.length) {
approved.push({ taskerId, ...fees })
t.update(transRef, { approved })
t.update(bidRef, { accepted: true })
if (manpower === approved.length) {
t.update(taskRef, { open: false })
t.update(transRef, { status: 'ongoing' })
}
}
}
})
} catch (e) {
bulk = e
console.log('nag error', e)
throw e
}
if (bulk.success) {
swal('Offer accepted!', '', 'success')
} else {
swal('Oh, no!',
'This task might already be approved',
'error'
)
}
}
i have been stuck here since i don't understand where the transaction failed. any help is very much appreciated.
thank you!
to those who are having the same problem, here is my (hackish) solution:
for every condition,
add a document write (could be a
set() update()
ordelete()
) that corresponds to each of the document reads which in my code: the use ofget()
s.and return a Promise
here's the updated code:
I ran into the same issue. As long as google will not be able to sent validation errors with better errors than just that the client was not allowed to write the data (security rules). I prefer to handle it on client site. So I use transactions for example to validate that a referenced doc is still available when I write data. (for example I have write an
order
document that references to acustomer
and want be sure that the customer still exists.) So I have to read it but actually there is no need to write it.I came up with something close to nrions solution but tried to have a more general approach for it so I wrote a wrapper for runTransaction. Of cause it is not the cleanest way to do it but maybe it is useful for others.