Search code examples
javascriptparse-server

ParseServer Cloud Code function finishing before promise has been fulfilled


I have a scenario in which I'm trying to determine how many new "payers" a company has for the current month. The code I have works, I can log the results and get the number of expected new payers for the company. However, the method is returning 0 because the final code is executed before the Promise has finished.

I think the problem is that the promises are not chained, but nested, I tried chaining them but it just gave errors. Is there anything obviously wrong with the code below?

Parse.Cloud.define('cloudMethod', function(request, response) {
  if (!request.user) {
    response.error('Invalid User')
    return
  }

  // Set up now date
  const now = new Date()
  const thisMonthYear = now.getMonth().toString() + now.getFullYear().toString()

  // Setup empty array to hold new monthly payer matches
  const newMonthlyDonors = []

  // Setup User query
  const User = Parse.Object.extend('User')
  const userQuery = new Parse.Query(User)

  // Step 1: Get company pointer from user
  userQuery
    .equalTo('username', request.user.get('username'))
    .first()
    .then(function(user) {

      // Step 2: Count payers for that company
      var Payment = Parse.Object.extend('Payment')
      var paymentQuery = new Parse.Query(Payment)
      paymentQuery.equalTo('company', user.get('company'))

      // Create a trivial resolved promise as a base case.
      var promise = Parse.Promise.as()

      paymentQuery.distinct('user').then(function(results) {
        // Step 3: Loop through each distinct payer
        _.each(results, function(result) {
          // For each item, extend the promise with a function.
          promise = promise.then(function() {
            // Setup new Payment query
            const firstPaymentQuery = new Parse.Query(Payment)

            /*
              Step 4:
              Query Payment class by this user,
              set payment dates in ascending order
              and then take the first one.
            */
            firstPaymentQuery
              .equalTo('user', result)
              .ascending('paymentDate')
              .first()
              .then(function(firstPayment) {
                // Set up user date
                const firstPaymentDate = new Date(firstPayment.get('paymentDate'))
                const firstPaymentMonthYear = firstPaymentDate.getMonth().toString() + firstPaymentDate.getFullYear().toString()

                /*
                Step 5:
                See if the user's first payment date is equal to the current month
                */
                if (firstPaymentMonthYear === thisMonthYear) {
                  return newMonthlyDonors.push(result)
                }
                else {
                  return
                }
              }, function(error) {
                response.error('Query to get users first payment date failed')
              })
          })
        })

        return promise
      }).then(function() {
        /*
          FIXME:
          This is getting called before the queries above can run.
          Which is why it's returning 0...
        */

        // Return the matches for this month
        response.success(newMonthlyDonors.length)
      }, function(error) {
        response.error('total user count for company failed')
      })
  },
  function(error) {
    console.log('Error retrieving User')
    console.log(error)
  })
})

Solution

  • The problem was I wasn’t returning the query as the result of the first promise called in the _.each() so the returned result within that query was never returned.

    Referencing the big block of code in the question:

    This:

    /*
      Step 4:
      Query Payment class by this user,
      set payment dates in ascending order
      and then take the first one.
    */
    firstPaymentQuery…
    

    Needs to be this:

    /*
      Step 4:
      Query Payment class by this user,
      set payment dates in ascending order
      and then take the first one.
    */
    return firstPaymentQuery…