All files / src/databases/mongo/services populateService.ts

100% Statements 63/63
53.84% Branches 7/13
100% Functions 3/3
100% Lines 63/63

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 661x 1x 1x 1x 1x 1x 1x 1x 2x 3x 2x 2x 1x 1x 1x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 5x 5x 9x 9x 9x 6x 6x 6x 1x 1x 1x      
 
import { getProjectDatabaseModels } from '../../../helpers/getProjectModelsAndDaos'
import { findByAddressAll, getId, isObject, asArray } from 'topkat-utils'
import { MaybeArray } from 'typescript-generic-types'
import { AllDbIds } from '../../../cache/dbs/index.generated'
 
/** Transform populated fields into their respective _ids. Will modify the passed object */
export async function unPopulate<ModelName extends string>(dbName: AllDbIds, modelName: ModelName, fields: Record<string, any>) {
    await forEachPopulateField(dbName, modelName, fields, (val, addr, parent) => {
        parent[addr] = val !== null ? getId(val) : val
    })
}
 
const modelFlatObjCache = {} as { [dbId: string]: { [modelName: string]: { [populatedFieldNameFlat: string]: string } } }
 
async function forEachPopulateField<ModelName extends string>(
    dbName: AllDbIds,
    modelName: ModelName,
    fields: Record<string, any>,
    cb: (fieldValue: string | Record<string, any>, fieldAddrInParent: string | number, parent: MaybeArray<Record<string, any>>, modelName: string, addrFromRoot: string) => MaybePromise<any>,
    recursive = false
) {
    const models = await getProjectDatabaseModels()
 
    modelFlatObjCache[dbName] ??= {}
    modelFlatObjCache[dbName][modelName] ??= models[dbName][modelName]._getDefinitionObjFlat(false, def => def._refValue)
 
    const populateAddresses = modelFlatObjCache[dbName][modelName]
 
    // const { populateAddrFlatWithModelName } = models
    // const populateAddresses = populateAddrFlatWithModelName[dbName][modelName]
 
    for (const [addr, modelName] of Object.entries(populateAddresses)) {
        const actualPopulatedFieldFields = findByAddressAll(fields, addr.replace(/\[\d\]/g, '[*]'), true)
 
        if (actualPopulatedFieldFields && actualPopulatedFieldFields.length) {
 
            for (const [addr, valueMaybeArr, lastElmKey, parent] of actualPopulatedFieldFields) { // Eg: ['organizations[0]', { populatedOrg: true }] OR ['organizations[0]', '1234orgId']
                const isArray = Array.isArray(valueMaybeArr)
                let i = 0
                const arrValue = asArray(valueMaybeArr)
                if (!arrValue) continue
                for (const value of arrValue) {
                    await cb(
                        value,
                        isArray ? i : lastElmKey,
                        isArray ? parent[lastElmKey] : parent,
                        modelName,
                        isArray ? addr + `[${i}]` : addr
                    )
                    const updatedValue = isArray ? parent[lastElmKey][i] : parent[lastElmKey] // may have changed reference
                    if (recursive && isObject(updatedValue)) {
                        await forEachPopulateField(dbName, modelName, updatedValue, cb, recursive)
                    }
                    i++
                }
            }
        }
    }
}
 
// alias for readability
export const forEachPopulateFieldRecursive: typeof forEachPopulateField = async (dbName, modelName, fields, cb) => await forEachPopulateField(dbName, modelName, fields, cb, true)