budibase/packages/core/src/transactions/retrieve.js

204 lines
5.1 KiB
JavaScript

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,
}
}