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

207 lines
5.3 KiB
JavaScript
Raw Normal View History

2019-07-15 08:12:52 +02:00
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,
});
};