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//invoices//charges/""to point to.. // "/partners//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() ) }) })