import { map, filter, groupBy, split, some, find } from "lodash/fp" import { LOCK_FILENAME, TRANSACTIONS_FOLDER, idSep, isUpdate, nodeKeyHashFromBuildFolder, isBuildIndexFolder, getTransactionId, isDelete, isCreate, } from "./transactionsCommon" import { joinKey, $, none, isSomething } from "../common" import { getLastPartInKey, getNodeFromNodeKeyHash, } from "../templateApi/hierarchy" import { _load } from "../recordApi/load" export const retrieve = async app => { const transactionFiles = await app.datastore.getFolderContents( TRANSACTIONS_FOLDER ) let transactions = [] if (some(isBuildIndexFolder)(transactionFiles)) { const buildIndexFolder = find(isBuildIndexFolder)(transactionFiles) transactions = await retrieveBuildIndexTransactions( app, joinKey(TRANSACTIONS_FOLDER, buildIndexFolder) ) } if (transactions.length > 0) return transactions return await retrieveStandardTransactions(app, transactionFiles) } const retrieveBuildIndexTransactions = async (app, buildIndexFolder) => { const childFolders = await app.datastore.getFolderContents(buildIndexFolder) if (childFolders.length === 0) { // cleanup await app.datastore.deleteFolder(buildIndexFolder) return [] } const getTransactionFiles = async (childFolderIndex = 0) => { if (childFolderIndex >= childFolders.length) return [] const childFolderKey = joinKey( buildIndexFolder, childFolders[childFolderIndex] ) const files = await app.datastore.getFolderContents(childFolderKey) if (files.length === 0) { await app.datastore.deleteFolder(childFolderKey) return await getTransactionFiles(childFolderIndex + 1) } return { childFolderKey, files } } const transactionFiles = await getTransactionFiles() if (transactionFiles.files.length === 0) return [] const transactions = $(transactionFiles.files, [map(parseTransactionId)]) for (const t of transactions) { const transactionContent = await app.datastore.loadJson( joinKey(transactionFiles.childFolderKey, t.fullId) ) t.record = await _load(app, transactionContent.recordKey) } transactions.indexNode = $(buildIndexFolder, [ getLastPartInKey, nodeKeyHashFromBuildFolder, getNodeFromNodeKeyHash(app.hierarchy), ]) transactions.folderKey = transactionFiles.childFolderKey return transactions } const retrieveStandardTransactions = async (app, transactionFiles) => { const transactionIds = $(transactionFiles, [ filter(f => f !== LOCK_FILENAME && !isBuildIndexFolder(f)), map(parseTransactionId), ]) const transactionIdsByRecord = $(transactionIds, [groupBy("recordId")]) const dedupedTransactions = [] const verify = async t => { if (t.verified === true) return t const id = getTransactionId(t.recordId, t.transactionType, t.uniqueId) const transaction = await app.datastore.loadJson( joinKey(TRANSACTIONS_FOLDER, id) ) if (isDelete(t)) { t.record = transaction.record t.verified = true return t } const rec = await _load(app, transaction.recordKey) if (rec.transactionId === id) { t.record = rec if (transaction.oldRecord) { t.oldRecord = transaction.oldRecord } t.verified = true } else { t.verified = false } return t } const pickOne = async (trans, forType) => { const transForType = filter(forType)(trans) if (transForType.length === 1) { const t = await verify(transForType[0]) return t.verified === true ? t : null } for (let t of transForType) { t = await verify(t) if (t.verified === true) { return t } } return null } for (const recordId in transactionIdsByRecord) { const transIdsForRecord = transactionIdsByRecord[recordId] if (transIdsForRecord.length === 1) { const t = await verify(transIdsForRecord[0]) if (t.verified) { dedupedTransactions.push(t) } continue } if (some(isDelete)(transIdsForRecord)) { const t = await verify(find(isDelete)(transIdsForRecord)) if (t.verified) { dedupedTransactions.push(t) } continue } if (some(isUpdate)(transIdsForRecord)) { const upd = await pickOne(transIdsForRecord, isUpdate) if (isSomething(upd) && upd.verified) { dedupedTransactions.push(upd) } continue } if (some(isCreate)(transIdsForRecord)) { const cre = await pickOne(transIdsForRecord, isCreate) if (isSomething(cre)) { dedupedTransactions.push(cre) } continue } } const duplicates = $(transactionIds, [ filter(t => none(ddt => ddt.uniqueId === t.uniqueId)(dedupedTransactions)), ]) const deletePromises = map(t => app.datastore.deleteFile( joinKey( TRANSACTIONS_FOLDER, getTransactionId(t.recordId, t.transactionType, t.uniqueId) ) ) )(duplicates) await Promise.all(deletePromises) return dedupedTransactions } const parseTransactionId = id => { const splitId = split(idSep)(id) return { recordId: splitId[0], transactionType: splitId[1], uniqueId: splitId[2], fullId: id, } }