0

I´m having a problem to return a promise inside my onCall function, while trying to retrieve sub-collections with getCollections()

I have a document with sub-collections. And each sub-collection has a document. I´m trying to retrieve all this structure structure

data structure

I have the code below with 2 structures, but they are failing to return the data. I´m not understanding the structure to\when return the value

const functions = require('firebase-functions');
const admin = require('firebase-admin')
admin.initializeApp()

exports.rs = functions.https.onCall( async (data, context) => {

  let cols = []
  return await new Promise( async (res, rej) => {
    let required_ref = admin.firestore().collection('required_services')
      .doc(data.user_id)


     return await required_ref.get()
      .then(async () => {
        let collections = await required_ref.getCollections()
        console.log('collections', collections)
        collections.forEach(async collection => {
          let docs = await collection.get()
          console.log('docs for collection', collection.id)
          docs.forEach(doc => {
            console.log('doc', doc)
            let doc_ = {
              id: doc.id,
              data: doc.data()
            }
            cols.push(doc_)
          })
        })
        console.log('inside async', cols)
        res(cols)

      })
  })
})

exports.requiresServices = functions.https.onCall( async (data, context) => {

  // const docs = () => {
    let required_ref = admin.firestore().collection('required_services')
      .doc(data.user_id)
    let cols = []

    return await required_ref.get()
      .then(async () => {
        let collections = await required_ref.getCollections()
        console.log('collections', collections)
        collections.forEach(async collection => {
          let docs = await collection.get()
          console.log('docs for collection', collection.id)
          docs.forEach(doc => {
            console.log('doc', doc)
            let doc_ = {
              id: doc.id,
              data: doc.data()
            }
            cols.push(doc_)
          })
        })
        console.log('inside async', cols)
        return cols
      })
})

exports.getRequiredServices = functions.https.onCall( (data, context) => {

    let required_ref = admin.firestore().collection('required_services')
      .doc(data.user_id)
    let cols = []

  let first = () => {
      console.log('inside first')
      let promise = new Promise( (res, rej) =>{
        required_ref.get().then( () => {
          return required_ref.getCollections().then(collections => {
            res(collections)
          })
        })
      } )
    return promise
  }

  let second = collections => {
    console.log('inside second')
      new Promise( (res, rej) => {
        collections.forEach(collection => {
          console.log('collection', collection)
          collection.get().then(docs => {

            return docs.forEach(doc => {
              console.log('each doc inside each collection first ', doc.id, '=>', doc.data())
              let doc_ = {
                id: doc.id,
                data: doc.data()
              }
              cols.push(doc_)
            })


          })
          console.log('return inside two, cols', cols)
        })
        res(cols)
      })
  }

  return first().then(second => second)//.then(third)

})

The error in the console is showing

Error: internal
    at new HttpsErrorImpl (index.cjs.js?001a:58)
    at _errorForResponse (index.cjs.js?001a:153)
    at Service.eval
Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
Adriel Werlich
  • 1,982
  • 5
  • 15
  • 30
  • Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it) and [don't `await` a `.then(…)` chain](https://stackoverflow.com/a/54387912/1048572)! – Bergi Jan 30 '19 at 15:37
  • @Bergi... I quite don´t understand these tools... I´m trying to figure it out what´s going on, actually....The code is working. But your are saying that I could improve the syntax? Or that something could broke along the way? Is that it? How could I improve this? I read that forEach can actually cause problems, and for..or would be better choice. Is that it? – Adriel Werlich Jan 30 '19 at 15:43
  • Yes, you need to improve the syntax. That `return await new Promise( async (res, rej) => {` is totally pointless. Just omit the whole line, and replace the `res()` call by a `return` statement. – Bergi Jan 30 '19 at 15:46
  • Regarding `forEach`, yes it causes problems, see the duplicate. – Bergi Jan 30 '19 at 15:46
  • Ok @Bergi, I have another snnipet based on your suggestions... but I don´t know how to share it here in the comments. I would like to post it for your check. Where I could share it? – Adriel Werlich Jan 30 '19 at 16:23
  • You might just edit your answer – Bergi Jan 30 '19 at 16:28
  • @Bergi... Ok, let me know if it´s better now... Or if still needs some more improvements... – Adriel Werlich Jan 30 '19 at 16:31

1 Answers1

1

I got a solution like this, if anyone need something related:

node js

exports.getCollections = functions.https.onCall( async (data, context) => {

  let required_ref = admin.firestore().collection('required_services')
    .doc(data.user_id)

  return await required_ref.get()
    .then(async () => {
      let collections = await required_ref.getCollections()
      console.log('collections', collections)
      return {'cols':collections}
    })
    .catch( err => {
      throw new functions.https.HttpsError('failed to connect', err)
    })
})

client side

let collections = this.$options.firebase.functions().httpsCallable('getCollections')

          collections({user_id: this.getUser().uid}).then(r => {
            debugger
            console.log('return from cloud function get required services', r)

            let collections_ = r.data.cols
            let docs = []
            collections_.forEach(async collection => {

              let ref_path = collection._referencePath.segments

              this.$options.firebase.firestore().collection(ref_path[0])
                .doc(ref_path[1]).collection(ref_path[2]).get()
                .then(async documents => {
                  let doc = await documents.docs[0].ref.get()

                  doc = {
                    id: doc.id,
                    path: doc.ref.path,
                    data: doc.data()
                  }

                  docs.push(doc)
                  console.log('doc path', documents.docs[0].ref.path)
                })
            })
          }).catch(err => console.error(err))

It works

edited - Based on Bergi suggestions

          let collections = this.$options.firebase.functions().httpsCallable('getRequiredServices')

          let r = await this.required_services = await collections({user_id: this.getUser().uid});
          debugger
          // console.log('return from cloud function get required services', r)

          let collections_ = r.data.cols
          let docs = [], docs_ = []

          for (let collection of collections_) {
            let path = collection._referencePath.segments

            let documents = await this.$options.firebase.firestore().collection(path[0])
                .doc(path[1]).collection(path[2]).get();
            console.log('__documents__', documents)

            for (let doc of documents.docs) {
              doc = await documents.docs[0].ref.get()
              doc = {
                id: doc.id,
                path: doc.ref.path,
                data: doc.data()
              }
              docs_.push(doc)
            }
          }
          console.log('docs_', docs_)
Community
  • 1
  • 1
Adriel Werlich
  • 1,982
  • 5
  • 15
  • 30