All files / src/storage/methods getSyncChunk.ts

95.95% Statements 95/99
69.23% Branches 9/13
100% Functions 53/53
96.92% Lines 63/65

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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 13757x                 57x           47x           47x 47x 47x 47x   47x 47x 46x   47x   875x 137x     71x 53x     370x 47x     105x 47x     955x 62x     3540x 127x     1355x 37x     2140x 37x     95x 32x     255x 37x     945x 52x     110x 42x       888x 888x 564x 564x 564x 564x 710x 710x 710x 710x 710x 710x 306x 10816x 10816x 10816x 10816x 10816x         47x 74x 888x       47x                             21912x         710x 10956x 10956x 10956x 148028x      
import { sdk, table, verifyTruthy } from '../../index.client'
import { StorageReader } from '../StorageReader'
 
/**
 * Gets the next sync chunk of updated data from un-remoted storage (could be using a remote DB connection).
 * @param storage 
 * @param args 
 * @returns 
 */
export async function getSyncChunk(
    storage: StorageReader,
    args: sdk.RequestSyncChunkArgs
)
: Promise<sdk.SyncChunk>
{
    const r: sdk.SyncChunk = {
        fromStorageIdentityKey: '',
        toStorageIdentityKey: '',
        userIdentityKey: args.identityKey
    }
 
    let itemCount = args.maxItems
    let roughSize = args.maxRoughSize
    let i = 0
    let done = false
 
    const user = verifyTruthy(await storage.findUserByIdentityKey(args.identityKey))
    if (!args.since || user.updated_at > args.since)
        r.user = user
 
    const chunkers: ChunkerArgs[] = [
        {
            name: 'provenTx', maxDivider: 100, preAdd: () => { r.provenTxs = [] }, addItem: (i: table.ProvenTx) => { r.provenTxs!.push(i) },
            findItems: async (storage: StorageReader, args: sdk.FindForUserSincePagedArgs) => { return await storage.getProvenTxsForUser( args ) }
        },
        {
            name: 'outputBasket', maxDivider: 1, preAdd: () => { r.outputBaskets = [] }, addItem: (i: table.OutputBasket) => { r.outputBaskets!.push(i) },
            findItems: async (storage: StorageReader, args: sdk.FindForUserSincePagedArgs) => { return await storage.findOutputBaskets({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) }
        },
        {
            name: 'outputTag', maxDivider: 1, preAdd: () => { r.outputTags = [] }, addItem: (i: table.OutputTag) => { r.outputTags!.push(i) },
            findItems: async (storage: StorageReader, args: sdk.FindForUserSincePagedArgs) => { return await storage.findOutputTags({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) }
        },
        {
            name: 'txLabel', maxDivider: 1, preAdd: () => { r.txLabels = [] }, addItem: (i: table.TxLabel) => { r.txLabels!.push(i) },
            findItems: async (storage: StorageReader, args: sdk.FindForUserSincePagedArgs) => { return await storage.findTxLabels({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) }
        },
        {
            name: 'transaction', maxDivider: 25, preAdd: () => { r.transactions = [] }, addItem: (i: table.Transaction) => { r.transactions!.push(i) },
            findItems: async (storage: StorageReader, args: sdk.FindForUserSincePagedArgs) => { return await storage.findTransactions({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) }
        },
        {
            name: 'output', maxDivider: 25, preAdd: () => { r.outputs = [] }, addItem: (i: table.Output) => { r.outputs!.push(i) },
            findItems: async (storage: StorageReader, args: sdk.FindForUserSincePagedArgs) => { return await storage.findOutputs({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) }
        },
        {
            name: 'txLabelMap', maxDivider: 1, preAdd: () => { r.txLabelMaps = [] }, addItem: (i: table.TxLabelMap) => { r.txLabelMaps!.push(i) },
            findItems: async (storage: StorageReader, args: sdk.FindForUserSincePagedArgs) => { return await storage.getTxLabelMapsForUser(args) }
        },
        {
            name: 'outputTagMap', maxDivider: 1, preAdd: () => { r.outputTagMaps = [] }, addItem: (i: table.OutputTagMap) => { r.outputTagMaps!.push(i) },
            findItems: async (storage: StorageReader, args: sdk.FindForUserSincePagedArgs) => { return await storage.getOutputTagMapsForUser(args) }
        },
        {
            name: 'certificate', maxDivider: 25, preAdd: () => { r.certificates = [] }, addItem: (i: table.Certificate) => { r.certificates!.push(i) },
            findItems: async (storage: StorageReader, args: sdk.FindForUserSincePagedArgs) => { return await storage.findCertificates({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) }
        },
        {
            name: 'certificateField', maxDivider: 25, preAdd: () => { r.certificateFields = [] }, addItem: (i: table.CertificateField) => { r.certificateFields!.push(i) },
            findItems: async (storage: StorageReader, args: sdk.FindForUserSincePagedArgs) => { return await storage.findCertificateFields({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) }
        },
        {
            name: 'commission', maxDivider: 25, preAdd: () => { r.commissions = [] }, addItem: (i: table.Commission) => { r.commissions!.push(i) },
            findItems: async (storage: StorageReader, args: sdk.FindForUserSincePagedArgs) => { return await storage.findCommissions({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) }
        },
        {
            name: 'provenTxReq', maxDivider: 100, preAdd: () => { r.provenTxReqs = [] }, addItem: (i: table.ProvenTxReq) => { r.provenTxReqs!.push(i) },
            findItems: async (storage: StorageReader, args: sdk.FindForUserSincePagedArgs) => { return await storage.getProvenTxReqsForUser(args) }
        },
    ]
 
    const addItems = async (a: ChunkerArgs) => {
        if (i >= args.offsets.length) { done = true; return }
        let { offset, name: oname } = args.offsets[i++]
        Iif (a.name !== oname) throw new sdk.WERR_INVALID_PARAMETER('offsets', `in dependency order. '${a.name}' expected, found ${oname}.`);
        let preAddCalled = false
        for (; !done;) {
            const limit = Math.min(itemCount, Math.max(10, args.maxItems / a.maxDivider))
            Iif (limit <= 0) break;
            const items = await a.findItems(storage, { userId: user.userId, since: args.since, paged: { limit, offset } })
            checkEntityValues(items)
            if (!preAddCalled) { a.preAdd(); preAddCalled = true }
            if (items.length === 0) break;
            for (const item of items) {
                offset++
                a.addItem(item)
                itemCount--
                roughSize -= JSON.stringify(item).length
                if (itemCount <= 0 || roughSize < 0) { done = true; break; }
            }
        }
    }
 
    for (; !done;) {
        for (const c of chunkers) {
            await addItems(c)
        }
    }
 
    return r
}
 
type ChunkerArgs = {
        name: string,
        maxDivider: number,
        preAdd: () => void,
        addItem: (i: any) => void,
        findItems: (
            storage: StorageReader,
            args: sdk.FindForUserSincePagedArgs
        ) => Promise<any[]>,
}
 
function checkIsDate(v: any) {
    Iif (!(v instanceof Date))
        throw new sdk.WERR_INVALID_OPERATION('bad date')
}
 
function checkEntityValues(es: object[]) {
    for (const e of es) {
        checkIsDate(e['created_at'])
        checkIsDate(e['updated_at'])
        for (const key of Object.keys(e))
            Iif (e[key] === null)
                throw new sdk.WERR_INVALID_OPERATION()
    }
}