fixed bug where reference fields not being parsed when in index
This commit is contained in:
parent
921b31d595
commit
5d0d14f16a
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true
|
||||
"es6": true,
|
||||
"jest": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parser": "babel-eslint",
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
flatten,
|
||||
map,
|
||||
filter,
|
||||
isEqual
|
||||
} from 'lodash/fp';
|
||||
import { initialiseChildCollections } from '../collectionApi/initialise';
|
||||
import { validate } from './validate';
|
||||
|
@ -15,6 +16,7 @@ import {
|
|||
getExactNodeForPath,
|
||||
isRecord,
|
||||
getNode,
|
||||
getLastPartInKey,
|
||||
fieldReversesReferenceToNode,
|
||||
} from '../templateApi/hierarchy';
|
||||
import { mapRecord } from '../indexing/evaluate';
|
||||
|
@ -79,7 +81,6 @@ export const _save = async (app, record, context, skipValidation = false) => {
|
|||
getRecordFileName(recordClone.key),
|
||||
recordClone,
|
||||
);
|
||||
|
||||
await app.publish(events.recordApi.save.onRecordUpdated, {
|
||||
old: oldRecord,
|
||||
new: recordClone,
|
||||
|
@ -122,59 +123,6 @@ const initialiseReverseReferenceIndexes = async (app, record) => {
|
|||
}
|
||||
};
|
||||
|
||||
const maintainReferentialIntegrity = async (app, indexingApi, oldRecord, newRecord) => {
|
||||
/*
|
||||
FOREACH Field that reference this object
|
||||
- options Index node that for field
|
||||
- has options index changed for referenced record?
|
||||
- FOREACH reverse index of field
|
||||
- FOREACH referencingRecord in reverse index
|
||||
- Is field value still pointing to referencedRecord
|
||||
- Update referencingRecord.fieldName to new value
|
||||
- Save
|
||||
*/
|
||||
const recordNode = getExactNodeForPath(app.hierarchy)(newRecord.key);
|
||||
const referenceFields = fieldsThatReferenceThisRecord(
|
||||
app, recordNode,
|
||||
);
|
||||
|
||||
const updates = $(referenceFields, [
|
||||
map(f => ({
|
||||
node: getNode(
|
||||
app.hierarchy, f.typeOptions.indexNodeKey,
|
||||
),
|
||||
field: f,
|
||||
})),
|
||||
map(n => ({
|
||||
old: mapRecord(oldRecord, n.node),
|
||||
new: mapRecord(newRecord, n.node),
|
||||
indexNode: n.node,
|
||||
field: n.field,
|
||||
reverseIndexKeys: $(n.field.typeOptions.reverseIndexNodeKeys, [
|
||||
map(k => joinKey(
|
||||
newRecord.key,
|
||||
getLastPartInKey(k),
|
||||
)),
|
||||
]),
|
||||
})),
|
||||
filter(diff => !isEqual(diff.old)(diff.new)),
|
||||
]);
|
||||
|
||||
for (const update of updates) {
|
||||
for (const reverseIndexKey of update.reverseIndexKeys) {
|
||||
const rows = await listItems(app)(reverseIndexKey);
|
||||
|
||||
for (const key of map(r => r.key)(rows)) {
|
||||
const record = await _load(app, key);
|
||||
if (record[update.field.name].key === newRecord.key) {
|
||||
record[update.field.name] = update.new;
|
||||
await _save(app, indexingApi, record, undefined, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const fieldsThatReferenceThisRecord = (app, recordNode) => $(app.hierarchy, [
|
||||
getFlattenedHierarchy,
|
||||
filter(isRecord),
|
||||
|
|
|
@ -24,8 +24,24 @@ const hasStringValue = (ob, path) => has(ob, path)
|
|||
const isObjectWithKey = v => isObjectLike(v)
|
||||
&& hasStringValue(v, 'key');
|
||||
|
||||
const tryParseFromString = s => {
|
||||
|
||||
try {
|
||||
const asObj = JSON.parse(s);
|
||||
if(isObjectWithKey) {
|
||||
return parsedSuccess(asObj);
|
||||
}
|
||||
}
|
||||
catch(_) {
|
||||
// EMPTY
|
||||
}
|
||||
|
||||
return parsedFailed(s);
|
||||
}
|
||||
|
||||
const referenceTryParse = v => switchCase(
|
||||
[isObjectWithKey, parsedSuccess],
|
||||
[isString, tryParseFromString],
|
||||
[isNull, () => parsedSuccess(referenceNothing())],
|
||||
[defaultCase, parsedFailed],
|
||||
)(v);
|
||||
|
|
|
@ -2,7 +2,7 @@ import {setupApphierarchy,
|
|||
basicAppHierarchyCreator_WithFields,
|
||||
basicAppHierarchyCreator_WithFields_AndIndexes} from "./specHelpers";
|
||||
import {joinKey} from "../src/common";
|
||||
import {some, isArray} from "lodash";
|
||||
import {some, isArray, isObjectLike} from "lodash";
|
||||
|
||||
describe("recordApi > create > reindex", () => {
|
||||
|
||||
|
@ -107,6 +107,32 @@ describe("recordApi > create > reindex", () => {
|
|||
expect(customers[0].name).toBe("Ledog");
|
||||
});
|
||||
|
||||
it("should add reference field to index and reparse", async () => {
|
||||
const {recordApi, indexApi} =
|
||||
await setupApphierarchy(basicAppHierarchyCreator_WithFields);
|
||||
|
||||
const partner = recordApi.getNew("/partners", "partner");
|
||||
partner.businessName = "ACME";
|
||||
partner.phone = "098766e6";
|
||||
await recordApi.save(partner);
|
||||
|
||||
const customer = recordApi.getNew("/customers", "customer");
|
||||
customer.surname = "Ledog";
|
||||
customer.age = 9;
|
||||
customer.isalive = true,
|
||||
customer.createdDate = new Date();
|
||||
customer.partner = partner;
|
||||
await recordApi.save(customer);
|
||||
|
||||
const customers = await indexApi.listItems("/customer_index");
|
||||
|
||||
expect(customers.length).toBe(1);
|
||||
expect(isObjectLike(customer.partner)).toBeTruthy();
|
||||
expect(customers[0].partner.key).toBe(partner.key);
|
||||
expect(customers[0].partner.name).toBe(partner.businessName);
|
||||
expect(customers[0].partner.phone).toBe(partner.phone);
|
||||
});
|
||||
|
||||
it("should add to reverse reference index, when required", async () => {
|
||||
const {recordApi, indexApi} =
|
||||
await setupApphierarchy(basicAppHierarchyCreator_WithFields);
|
||||
|
@ -573,7 +599,7 @@ describe("referenced object changed", () => {
|
|||
|
||||
it("should update the reference", async () => {
|
||||
|
||||
const {recordApi, indexApi} =
|
||||
const { recordApi } =
|
||||
await setupApphierarchy(basicAppHierarchyCreator_WithFields);
|
||||
|
||||
const partner1 = recordApi.getNew("/partners", "partner");
|
||||
|
|
|
@ -189,6 +189,32 @@ describe('recordApi > save then load', () => {
|
|||
const savedAgain = await recordApi.load(saved.key);
|
||||
expect(savedAgain.surname).toBe(saved.surname);
|
||||
});
|
||||
|
||||
it("should maintain referential integrity", async () => {
|
||||
const {recordApi} =
|
||||
await setupApphierarchy(basicAppHierarchyCreator_WithFields);
|
||||
|
||||
const referredByCustomer = recordApi.getNew("/customers", "customer");
|
||||
referredByCustomer.surname = "Ledog";
|
||||
referredByCustomer.age = 9;
|
||||
referredByCustomer.isalive = true,
|
||||
referredByCustomer.createdDate = new Date();
|
||||
const savedReferredBy = await recordApi.save(referredByCustomer);
|
||||
|
||||
const referredCustomer = recordApi.getNew("/customers", "customer");
|
||||
referredCustomer.surname = "Zeecat";
|
||||
referredCustomer.age = 9;
|
||||
referredCustomer.isalive = true,
|
||||
referredCustomer.createdDate = new Date();
|
||||
referredCustomer.referredBy = referredByCustomer;
|
||||
await recordApi.save(referredCustomer);
|
||||
|
||||
savedReferredBy.surname = "Zeedog";
|
||||
await recordApi.save(savedReferredBy);
|
||||
|
||||
const loadedReferredTo = await recordApi.load(referredCustomer.key);
|
||||
expect(loadedReferredTo.referredBy.surname).toBe("Zeedog");
|
||||
});
|
||||
});
|
||||
|
||||
describe("save", () => {
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
import path from "path";
|
||||
import {getAppApis, getRecordApi,
|
||||
import {getRecordApi,
|
||||
getCollectionApi, getIndexApi, getActionsApi} from "../src";
|
||||
import memory from "./memory";
|
||||
import {setupDatastore} from "../src/appInitialise";
|
||||
import {configFolder, fieldDefinitions,
|
||||
templateDefinitions, isNothing,
|
||||
templateDefinitions,
|
||||
joinKey,
|
||||
isSomething} from "../src/common";
|
||||
import { getNewIndexTemplate } from "../src/templateApi/createNodes";
|
||||
import {indexTypes} from "../src/templateApi/indexes";
|
||||
import getTemplateApi from "../src/templateApi";
|
||||
import {getApplicationDefinition} from "../src/templateApi/getApplicationDefinition";
|
||||
import getAuthApi from "../src/authApi";
|
||||
import {createEventAggregator} from "../src/appInitialise/eventAggregator";
|
||||
import {filter, find} from "lodash/fp";
|
||||
|
@ -116,7 +115,7 @@ export const hierarchyFactory = (...additionalFeatures) => templateApi => {
|
|||
|
||||
const customerRecord = templateApi.getNewRecordTemplate(root, "customer");
|
||||
customerRecord.collectionName = "customers";
|
||||
findCollectionDefaultIndex(customerRecord).map = "return {surname:record.surname, isalive:record.isalive};";
|
||||
findCollectionDefaultIndex(customerRecord).map = "return {surname:record.surname, isalive:record.isalive, partner:record.partner};";
|
||||
|
||||
const partnerRecord = templateApi.getNewRecordTemplate(root, "partner");
|
||||
partnerRecord.collectionName = "partners";
|
||||
|
@ -157,7 +156,7 @@ export const withFields = (hierarchy, templateApi) => {
|
|||
|
||||
const partnersReferenceIndex = templateApi.getNewIndexTemplate(root);
|
||||
partnersReferenceIndex.name = "partnersReference";
|
||||
partnersReferenceIndex.map = "return {name:record.businessName};";
|
||||
partnersReferenceIndex.map = "return {name:record.businessName, phone:record.phone};";
|
||||
partnersReferenceIndex.allowedRecordNodeIds = [partnerRecord.nodeId];
|
||||
|
||||
const partnerCustomersReverseIndex = templateApi.getNewIndexTemplate(partnerRecord, indexTypes.reference);
|
||||
|
@ -172,7 +171,7 @@ export const withFields = (hierarchy, templateApi) => {
|
|||
newCustomerField("createddate", "datetime");
|
||||
newCustomerField("age", "number");
|
||||
newCustomerField("profilepic", "file");
|
||||
const customerPartnerField = newCustomerField("partner", "reference", undefined, {
|
||||
newCustomerField("partner", "reference", undefined, {
|
||||
indexNodeKey : "/partnersReference",
|
||||
displayValue : "name",
|
||||
reverseIndexNodeKeys : [joinKey(
|
||||
|
@ -205,6 +204,7 @@ export const withFields = (hierarchy, templateApi) => {
|
|||
|
||||
const newPartnerField = getNewFieldAndAdd(templateApi, partnerRecord);
|
||||
newPartnerField("businessName", "string");
|
||||
newPartnerField("phone", "string");
|
||||
|
||||
const newPartnerInvoiceField = getNewFieldAndAdd(templateApi, partnerInvoiceRecord);
|
||||
const partnerInvoiceTotalIncVatVield = newPartnerInvoiceField("totalIncVat", "number");
|
||||
|
@ -215,7 +215,7 @@ export const withFields = (hierarchy, templateApi) => {
|
|||
const newChargeField = getNewFieldAndAdd(templateApi, chargeRecord);
|
||||
newChargeField("amount", "number");
|
||||
|
||||
const chargePartnerInvoiceField = newChargeField("partnerInvoice", "reference", undefined, {
|
||||
newChargeField("partnerInvoice", "reference", undefined, {
|
||||
reverseIndexNodeKeys : [joinKey(
|
||||
partnerInvoiceRecord.nodeKey(), "partnerCharges"
|
||||
)],
|
||||
|
@ -236,7 +236,7 @@ export const withFields = (hierarchy, templateApi) => {
|
|||
customersReferenceIndex.filter = "record.isalive === true";
|
||||
customersReferenceIndex.allowedRecordNodeIds = [customerRecord.nodeId];
|
||||
|
||||
const invoiceCustomerField = newInvoiceField("customer", "reference", undefined, {
|
||||
newInvoiceField("customer", "reference", undefined, {
|
||||
indexNodeKey : "/customersReference",
|
||||
reverseIndexNodeKeys : [findCollectionDefaultIndex(invoiceRecord).nodeKey()],
|
||||
displayValue : "name"
|
||||
|
@ -376,15 +376,11 @@ export const setupApphierarchy = async (creator, disableCleanupTransactions=fals
|
|||
return apis;
|
||||
};
|
||||
|
||||
const disableCleanupTransactions = app => {
|
||||
|
||||
}
|
||||
|
||||
export const getNewFieldAndAdd = (templateApi, record) => (name, type, initial, typeOptions) => {
|
||||
const field = templateApi.getNewField(type);
|
||||
field.name = name;
|
||||
field.getInitialValue = !initial ? "default" : initial;
|
||||
if(!!typeOptions)
|
||||
if(typeOptions)
|
||||
field.typeOptions = typeOptions;
|
||||
templateApi.addField(record, field);
|
||||
return field;
|
||||
|
@ -397,8 +393,8 @@ export const stubEventHandler = () => {
|
|||
events.push({name, context});
|
||||
},
|
||||
events,
|
||||
getEvents: n => filter(e => e.name === n)
|
||||
(events)
|
||||
getEvents: n => filter(
|
||||
e => e.name === n)(events)
|
||||
};
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue