budibase/packages/core/test/indexApi.buildIndex.spec.js

611 lines
18 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 { getExactNodeForKey } 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 = getExactNodeForKey(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 = getExactNodeForKey(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()
)
})
})