639 lines
20 KiB
JavaScript
639 lines
20 KiB
JavaScript
import {setupApphierarchy, findCollectionDefaultIndex,
|
|
basicAppHierarchyCreator_WithFields_AndIndexes} from "./specHelpers";
|
|
import { joinKey } from "../src/common";
|
|
import {some} from "lodash";
|
|
import {_deleteIndex} from "../src/indexApi/delete";
|
|
import {permission} from "../src/authApi/permissions";
|
|
import { getExactNodeForPath } from "../src/templateApi/hierarchy";
|
|
|
|
describe("buildIndex > Global index", () => {
|
|
|
|
it("should index a record when record node is not decendant", async () => {
|
|
const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const customer = recordApi.getNew(
|
|
appHierarchy.customerRecord.collectionNodeKey(),
|
|
"customer");
|
|
|
|
customer.surname = "thedog";
|
|
|
|
await recordApi.save(customer);
|
|
|
|
const outstandingInvoice = recordApi.getNewChild(
|
|
customer.key,
|
|
"invoices",
|
|
"invoice"
|
|
);
|
|
|
|
outstandingInvoice.totalIncVat = 100;
|
|
outstandingInvoice.paidAmount = 50;
|
|
|
|
await recordApi.save(outstandingInvoice);
|
|
|
|
const paidInvoice = recordApi.getNewChild(
|
|
customer.key,
|
|
"invoices",
|
|
"invoice"
|
|
);
|
|
|
|
paidInvoice.totalIncVat = 200;
|
|
paidInvoice.paidAmount = 200;
|
|
|
|
await recordApi.save(paidInvoice);
|
|
|
|
const indexKey = appHierarchy.outstandingInvoicesIndex.nodeKey();
|
|
await _deleteIndex(app, indexKey, false);
|
|
|
|
await indexApi.buildIndex(indexKey);
|
|
const indexItems = await indexApi.listItems(indexKey);
|
|
|
|
expect(indexItems.length).toBe(1);
|
|
expect(indexItems[0].key).toBe(outstandingInvoice.key);
|
|
});
|
|
|
|
it("should index records from 2 seperate tree branches", async () => {
|
|
const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const customer = recordApi.getNew(
|
|
appHierarchy.customerRecord.collectionNodeKey(),
|
|
"customer");
|
|
|
|
customer.surname = "thedog";
|
|
|
|
await recordApi.save(customer);
|
|
|
|
const invoice = recordApi.getNewChild(
|
|
customer.key,
|
|
"invoices",
|
|
"invoice"
|
|
);
|
|
|
|
invoice.totalIncVat = 100;
|
|
invoice.paidAmount = 50;
|
|
|
|
await recordApi.save(invoice);
|
|
|
|
const partner = recordApi.getNew(
|
|
appHierarchy.partnerRecord.collectionNodeKey(),
|
|
"partner");
|
|
|
|
partner.surname = "thedog";
|
|
|
|
await recordApi.save(partner);
|
|
|
|
const partnerInvoice = recordApi.getNewChild(
|
|
partner.key,
|
|
"invoices",
|
|
"invoice"
|
|
);
|
|
|
|
partnerInvoice.totalIncVat = 100;
|
|
partnerInvoice.paidAmount = 50;
|
|
|
|
await recordApi.save(partnerInvoice);
|
|
|
|
const indexKey = appHierarchy.outstandingInvoicesIndex.nodeKey();
|
|
await _deleteIndex(app, indexKey, false);
|
|
|
|
await indexApi.buildIndex(indexKey);
|
|
const indexItems = await indexApi.listItems(indexKey);
|
|
|
|
expect(indexItems.length).toBe(2);
|
|
expect(indexItems[0].key).toBe(invoice.key);
|
|
expect(indexItems[1].key).toBe(partnerInvoice.key);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe("buildIndex > TopLevelCollection", () => {
|
|
|
|
it("should index a record when it is a nested decendant of the collection node", async() => {
|
|
const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const customer = recordApi.getNew(
|
|
appHierarchy.customerRecord.collectionNodeKey(),
|
|
"customer");
|
|
|
|
customer.surname = "thedog";
|
|
|
|
await recordApi.save(customer);
|
|
|
|
const invoice = recordApi.getNewChild(
|
|
customer.key,
|
|
"invoices",
|
|
"invoice"
|
|
);
|
|
|
|
invoice.totalIncVat = 100;
|
|
invoice.paidAmount = 50;
|
|
|
|
await recordApi.save(invoice);
|
|
|
|
const indexKey = appHierarchy.customerInvoicesIndex.nodeKey();
|
|
await _deleteIndex(app, indexKey, false);
|
|
|
|
await indexApi.buildIndex(indexKey);
|
|
const indexItems = await indexApi.listItems(indexKey);
|
|
|
|
expect(indexItems.length).toBe(1);
|
|
expect(indexItems[0].key).toBe(invoice.key);
|
|
|
|
});
|
|
|
|
it("should not index a record when it is not decendant", async() => {
|
|
const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const partner = recordApi.getNew(
|
|
appHierarchy.partnerRecord.collectionNodeKey(),
|
|
"partner");
|
|
|
|
partner.surname = "thedog";
|
|
|
|
await recordApi.save(partner);
|
|
|
|
const invoice = recordApi.getNewChild(
|
|
partner.key,
|
|
"invoices",
|
|
"invoice"
|
|
);
|
|
|
|
invoice.totalIncVat = 100;
|
|
invoice.paidAmount = 50;
|
|
|
|
await recordApi.save(invoice);
|
|
|
|
const indexKey = appHierarchy.customerInvoicesIndex.nodeKey();
|
|
await _deleteIndex(app, indexKey, false);
|
|
|
|
await indexApi.buildIndex(indexKey);
|
|
const indexItems = await indexApi.listItems(indexKey);
|
|
|
|
expect(indexItems.length).toBe(0);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe("buildIndex > nested collection", () => {
|
|
|
|
it("should build a single record into index", async () => {
|
|
|
|
const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const customer = recordApi.getNew(
|
|
appHierarchy.customerRecord.collectionNodeKey(),
|
|
"customer");
|
|
|
|
customer.surname = "thedog";
|
|
|
|
await recordApi.save(customer);
|
|
|
|
const invoice = recordApi.getNewChild(
|
|
customer.key,
|
|
"invoices",
|
|
"invoice"
|
|
);
|
|
|
|
invoice.totalIncVat = 100;
|
|
invoice.paidAmount = 50;
|
|
|
|
await recordApi.save(invoice);
|
|
|
|
const indexKey = joinKey(customer.key, "invoice_index");
|
|
await _deleteIndex(app, indexKey, false);
|
|
|
|
const indexNode = getExactNodeForPath(appHierarchy.root)(indexKey);
|
|
await indexApi.buildIndex(indexNode.nodeKey());
|
|
const indexItems = await indexApi.listItems(indexKey);
|
|
|
|
expect(indexItems.length).toBe(1);
|
|
expect(indexItems[0].key).toBe(invoice.key);
|
|
|
|
});
|
|
|
|
it("should build multiple records, from different parents into index", async () => {
|
|
|
|
const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const customer = recordApi.getNew(
|
|
appHierarchy.customerRecord.collectionNodeKey(),
|
|
"customer");
|
|
|
|
customer.surname = "thedog";
|
|
|
|
await recordApi.save(customer);
|
|
|
|
const invoice = recordApi.getNewChild(
|
|
customer.key,
|
|
"invoices",
|
|
"invoice"
|
|
);
|
|
|
|
invoice.totalIncVat = 100;
|
|
invoice.paidAmount = 50;
|
|
|
|
await recordApi.save(invoice);
|
|
|
|
const customer2 = recordApi.getNew(
|
|
appHierarchy.customerRecord.collectionNodeKey(),
|
|
"customer");
|
|
|
|
customer2.surname = "thedog";
|
|
|
|
await recordApi.save(customer2);
|
|
|
|
const invoice2 = recordApi.getNewChild(
|
|
customer2.key,
|
|
"invoices",
|
|
"invoice"
|
|
);
|
|
|
|
invoice2.totalIncVat = 100;
|
|
invoice2.paidAmount = 50;
|
|
|
|
await recordApi.save(invoice2);
|
|
|
|
const indexKey = joinKey(customer.key, "invoice_index");
|
|
await _deleteIndex(app, indexKey, false);
|
|
|
|
const indexNode = getExactNodeForPath(appHierarchy.root)(indexKey);
|
|
await indexApi.buildIndex(
|
|
indexNode.nodeKey());
|
|
const indexItems = await indexApi.listItems(indexKey);
|
|
|
|
expect(indexItems.length).toBe(1);
|
|
expect(some(indexItems, i => i.key === invoice.key)).toBeTruthy();
|
|
|
|
const indexItems2 = await indexApi.listItems(
|
|
joinKey(customer2.key, "invoice_index")
|
|
);
|
|
|
|
expect(indexItems2.length).toBe(1);
|
|
expect(some(indexItems2, i => i.key === invoice2.key)).toBeTruthy();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe("buildIndex > sharded index", () => {
|
|
|
|
it("should index a record into a sharded index", async() => {
|
|
const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const customer = recordApi.getNew(
|
|
appHierarchy.customerRecord.collectionNodeKey(),
|
|
"customer");
|
|
|
|
customer.surname = "thedog";
|
|
|
|
await recordApi.save(customer);
|
|
|
|
const indexKey = appHierarchy
|
|
.customersBySurnameIndex
|
|
.nodeKey();
|
|
|
|
await _deleteIndex(app, indexKey, false);
|
|
|
|
await indexApi.buildIndex(indexKey);
|
|
const indexItems = await indexApi.listItems(indexKey);
|
|
|
|
expect(indexItems.length).toBe(1);
|
|
expect(indexItems[0].key).toBe(customer.key);
|
|
|
|
});
|
|
|
|
it("should index multiple record into a sharded index", async() => {
|
|
const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const customer1 = recordApi.getNew(
|
|
appHierarchy.customerRecord.collectionNodeKey(),
|
|
"customer");
|
|
customer1.surname = "thedog";
|
|
await recordApi.save(customer1);
|
|
|
|
const customer2 = recordApi.getNew(
|
|
appHierarchy.customerRecord.collectionNodeKey(),
|
|
"customer");
|
|
customer2.surname = "Zeecat";
|
|
await recordApi.save(customer2);
|
|
|
|
const indexKey = appHierarchy
|
|
.customersBySurnameIndex
|
|
.nodeKey();
|
|
|
|
await _deleteIndex(app, indexKey, false);
|
|
|
|
await indexApi.buildIndex(indexKey);
|
|
const indexItems = await indexApi.listItems(indexKey);
|
|
|
|
expect(indexItems.length).toBe(2);
|
|
expect(some(indexItems, i => i.key === customer1.key)).toBeTruthy();
|
|
expect(some(indexItems, i => i.key === customer2.key)).toBeTruthy();
|
|
|
|
});
|
|
|
|
it("should index multiple records into a sharded and nested index", async() => {
|
|
const {recordApi, indexApi, appHierarchy, app} = await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const customer = recordApi.getNew(
|
|
appHierarchy.customerRecord.collectionNodeKey(),
|
|
"customer");
|
|
customer.surname = "thedog";
|
|
await recordApi.save(customer);
|
|
|
|
const invoiceCollectionKey = joinKey(
|
|
customer.key, "invoices");
|
|
|
|
const invoice1 = recordApi.getNew(
|
|
invoiceCollectionKey,
|
|
"invoice"
|
|
);
|
|
invoice1.totalIncVat = 10;
|
|
invoice1.paidAmount = 10;
|
|
await recordApi.save(invoice1);
|
|
|
|
const invoice2 = recordApi.getNew(
|
|
invoiceCollectionKey,
|
|
"invoice"
|
|
);
|
|
invoice2.totalIncVat = 10;
|
|
invoice2.paidAmount = 0;
|
|
await recordApi.save(invoice2);
|
|
|
|
const indexKey = joinKey(
|
|
customer.key,
|
|
appHierarchy.invoicesByOutstandingIndex.name);
|
|
|
|
await _deleteIndex(app, indexKey, false);
|
|
|
|
await indexApi.buildIndex(
|
|
appHierarchy.invoicesByOutstandingIndex.nodeKey());
|
|
const indexItems = await indexApi.listItems(indexKey);
|
|
|
|
expect(indexItems.length).toBe(2);
|
|
expect(some(indexItems, i => i.key === invoice1.key)).toBeTruthy();
|
|
expect(some(indexItems, i => i.key === invoice2.key)).toBeTruthy();
|
|
|
|
const outstandingRange = {totalIncVat:1, paidAmount:0};
|
|
const outstandingItems = await indexApi.listItems(indexKey, {
|
|
rangeStartParams: outstandingRange,
|
|
rangeEndParams: outstandingRange
|
|
});
|
|
expect(outstandingItems.length).toBe(1);
|
|
|
|
});
|
|
|
|
it("should build reverse reference index", async () => {
|
|
const {recordApi, indexApi, appHierarchy, app} =
|
|
await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const referencedCustomer = recordApi.getNew("/customers", "customer");
|
|
referencedCustomer.surname = "Zecat";
|
|
await recordApi.save(referencedCustomer);
|
|
|
|
const referencingCustomer = recordApi.getNew("/customers", "customer");
|
|
referencingCustomer.surname = "Ledog";
|
|
referencingCustomer.referredBy = {
|
|
key: referencedCustomer.key, value: referencedCustomer.surname
|
|
};
|
|
referencingCustomer.isalive = true;
|
|
|
|
await recordApi.save(referencingCustomer);
|
|
|
|
const indexKey = joinKey(referencedCustomer.key, "referredToCustomers");
|
|
|
|
await _deleteIndex(app, indexKey, false);
|
|
|
|
await indexApi.buildIndex(
|
|
appHierarchy.referredToCustomersReverseIndex.nodeKey());
|
|
|
|
const indexItems = await indexApi.listItems(indexKey);
|
|
|
|
expect(indexItems.length).toBe(1);
|
|
expect(some(indexItems, i => i.key === referencingCustomer.key)).toBeTruthy();
|
|
});
|
|
|
|
});
|
|
|
|
describe("buildIndex > reverse reference index", () => {
|
|
|
|
it("should build a single record into index", async () => {
|
|
const {recordApi, indexApi, appHierarchy, app} =
|
|
await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const partner1 = recordApi.getNew("/partners", "partner");
|
|
partner1.businessName = "ACME inc";
|
|
await recordApi.save(partner1);
|
|
|
|
const customer = recordApi.getNew("/customers", "customer");
|
|
customer.surname = "Ledog";
|
|
customer.partner = {
|
|
key: partner1.key, value: partner1.businessName
|
|
};
|
|
customer.isalive = true;
|
|
|
|
await recordApi.save(customer);
|
|
|
|
const indexKey = joinKey(partner1.key, "partnerCustomers");
|
|
|
|
await _deleteIndex(app, indexKey, false);
|
|
|
|
await indexApi.buildIndex(
|
|
appHierarchy.partnerCustomersReverseIndex.nodeKey());
|
|
const indexItems = await indexApi.listItems(indexKey);
|
|
|
|
expect(indexItems.length).toBe(1);
|
|
expect(some(indexItems, i => i.key === customer.key)).toBeTruthy();
|
|
});
|
|
|
|
it("should build multiple records into an index, when referencing same record", async () => {
|
|
const {recordApi, indexApi, appHierarchy, app} =
|
|
await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const partner1 = recordApi.getNew("/partners", "partner");
|
|
partner1.businessName = "ACME inc";
|
|
await recordApi.save(partner1);
|
|
|
|
const customer1 = recordApi.getNew("/customers", "customer");
|
|
customer1.surname = "Ledog";
|
|
customer1.partner = {
|
|
key: partner1.key, value: partner1.businessName
|
|
};
|
|
customer1.isalive = true;
|
|
|
|
await recordApi.save(customer1);
|
|
|
|
const customer2 = recordApi.getNew("/customers", "customer");
|
|
customer2.surname = "Zeecat";
|
|
customer2.partner = {
|
|
key: partner1.key, value: partner1.businessName
|
|
};
|
|
customer2.isalive = true;
|
|
|
|
await recordApi.save(customer2);
|
|
|
|
const indexKey = joinKey(partner1.key, "partnerCustomers");
|
|
|
|
await _deleteIndex(app, indexKey, false);
|
|
|
|
await indexApi.buildIndex(
|
|
appHierarchy.partnerCustomersReverseIndex.nodeKey());
|
|
const indexItems = await indexApi.listItems(indexKey);
|
|
|
|
expect(indexItems.length).toBe(2);
|
|
expect(some(indexItems, i => i.key === customer1.key)).toBeTruthy();
|
|
expect(some(indexItems, i => i.key === customer2.key)).toBeTruthy();
|
|
});
|
|
|
|
|
|
it("should build multiple records into seperate indexes, when referencing different records", async () => {
|
|
const {recordApi, indexApi, appHierarchy, app} =
|
|
await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const partner1 = recordApi.getNew("/partners", "partner");
|
|
partner1.businessName = "ACME inc";
|
|
await recordApi.save(partner1);
|
|
|
|
const partner2 = recordApi.getNew("/partners", "partner");
|
|
partner2.businessName = "ACME inc";
|
|
await recordApi.save(partner2);
|
|
|
|
const customer1 = recordApi.getNew("/customers", "customer");
|
|
customer1.surname = "Ledog";
|
|
customer1.partner = {
|
|
key: partner1.key, value: partner1.businessName
|
|
};
|
|
customer1.isalive = true;
|
|
|
|
await recordApi.save(customer1);
|
|
|
|
const customer2 = recordApi.getNew("/customers", "customer");
|
|
customer2.surname = "Zeecat";
|
|
customer2.partner = {
|
|
key: partner2.key, value: partner1.businessName
|
|
};
|
|
customer2.isalive = true;
|
|
|
|
await recordApi.save(customer2);
|
|
|
|
const indexKey1 = joinKey(partner1.key, "partnerCustomers");
|
|
const indexKey2 = joinKey(partner2.key, "partnerCustomers");
|
|
|
|
await _deleteIndex(app, indexKey1, false);
|
|
|
|
await _deleteIndex(app, indexKey2, false);
|
|
|
|
await indexApi.buildIndex(
|
|
appHierarchy.partnerCustomersReverseIndex.nodeKey());
|
|
|
|
const indexItems1 = await indexApi.listItems(indexKey1);
|
|
|
|
expect(indexItems1.length).toBe(1);
|
|
expect(some(indexItems1, i => i.key === customer1.key)).toBeTruthy();
|
|
|
|
const indexItems2 = await indexApi.listItems(indexKey2);
|
|
|
|
expect(indexItems2.length).toBe(1);
|
|
expect(some(indexItems2, i => i.key === customer2.key)).toBeTruthy();
|
|
});
|
|
|
|
it.skip("should build record into index, when referencing and referenced records are in multiple nested collections", async () => {
|
|
|
|
// this currently fails because it is currntly assumed that the reference index should be either
|
|
// - Top level index
|
|
// - An ancestor index
|
|
// this test sets "customers/<id>/invoices/<id>/charges/<id>""to point to..
|
|
// "/partners/<id>/invoices/default"
|
|
//
|
|
// To work as intended, we would need to somehow find the index by:
|
|
// - customer.partner.key + /invoices/default
|
|
// bearing in mind that the customer is an ancestor.
|
|
|
|
const {recordApi, indexApi, appHierarchy, app} =
|
|
await setupApphierarchy(
|
|
basicAppHierarchyCreator_WithFields_AndIndexes
|
|
);
|
|
|
|
const partner = recordApi.getNew("/partners", "partner");
|
|
partner.businessName = "ACME inc";
|
|
await recordApi.save(partner);
|
|
|
|
const partnerInvoice = recordApi.getNew(
|
|
joinKey(partner.key, "invoices"), "invoice"
|
|
);
|
|
await recordApi.save(partnerInvoice);
|
|
|
|
|
|
const customer = recordApi.getNew("/customers", "customer");
|
|
customer.surname = "Ledog";
|
|
await recordApi.save(customer);
|
|
|
|
const customerInvoice = recordApi.getNew(
|
|
joinKey(customer.key, "invoices"), "invoice"
|
|
);
|
|
await recordApi.save(customerInvoice);
|
|
|
|
const charge = recordApi.getNew(
|
|
joinKey(customerInvoice.key, "charges"), "charge"
|
|
);
|
|
charge.partnerInvoice = {
|
|
key: partnerInvoice.key, createdDate: "something"
|
|
};
|
|
await recordApi.save(charge);
|
|
|
|
|
|
const indexKey = joinKey(partnerInvoice.key, "partnerCharges");
|
|
|
|
await _deleteIndex(app, indexKey, false);
|
|
|
|
await indexApi.buildIndex(
|
|
appHierarchy.partnerChargesReverseIndex.nodeKey());
|
|
|
|
const indexItems = await indexApi.listItems(indexKey);
|
|
|
|
expect(indexItems.length).toBe(1);
|
|
expect(some(indexItems, i => i.key === charge.key)).toBeTruthy();
|
|
});
|
|
|
|
it("should throw error when user user does not have permission", async () => {
|
|
const {indexApi, app, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes);
|
|
app.removePermission(permission.manageIndex.get());
|
|
expect(
|
|
indexApi.buildIndex(appHierarchy.partnerCustomersReverseIndex.nodeKey())
|
|
).rejects.toThrow(/Unauthorized/);
|
|
});
|
|
|
|
it("should not depend on having any other permissions", async () => {
|
|
const {app, indexApi, appHierarchy} = await setupApphierarchy(basicAppHierarchyCreator_WithFields_AndIndexes);
|
|
app.withOnlyThisPermission(permission.manageIndex.get());
|
|
await indexApi.buildIndex(appHierarchy.partnerCustomersReverseIndex.nodeKey());
|
|
});
|
|
|
|
}); |