204 lines
5.1 KiB
JavaScript
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,
|
|
}
|
|
}
|