|
|
|
@ -14,7 +14,7 @@ const EventType = { |
|
|
|
MODEL_DELETE: "model:delete", |
|
|
|
} |
|
|
|
|
|
|
|
module.exports.EventType = EventType |
|
|
|
exports.EventType = EventType |
|
|
|
|
|
|
|
/** |
|
|
|
* Update link documents for a model - this is to be called by the model controller when a model is being changed. |
|
|
|
@ -22,16 +22,13 @@ module.exports.EventType = EventType |
|
|
|
* future quite easily (all updates go through one function). |
|
|
|
* @param {string} instanceId The ID of the instance in which the model change is occurring. |
|
|
|
* @param {object} model The model which is changing, whether it is being deleted, created or updated. |
|
|
|
* @returns {Promise<null>} When the update is complete this will respond successfully. |
|
|
|
* @returns {Promise<object>} When the update is complete this will respond successfully. Returns the model that was |
|
|
|
* operated upon. |
|
|
|
*/ |
|
|
|
module.exports.updateLinksForModel = async ({ |
|
|
|
eventType, |
|
|
|
instanceId, |
|
|
|
model, |
|
|
|
}) => { |
|
|
|
exports.updateLinksForModel = async ({ eventType, instanceId, model }) => { |
|
|
|
// can't operate without these properties
|
|
|
|
if (instanceId == null || model == null) { |
|
|
|
return null |
|
|
|
return model |
|
|
|
} |
|
|
|
let linkController = new LinkController({ |
|
|
|
instanceId, |
|
|
|
@ -39,15 +36,13 @@ module.exports.updateLinksForModel = async ({ |
|
|
|
model, |
|
|
|
}) |
|
|
|
if (!(await linkController.doesModelHaveLinkedFields())) { |
|
|
|
return null |
|
|
|
return model |
|
|
|
} |
|
|
|
switch (eventType) { |
|
|
|
case EventType.MODEL_SAVE: |
|
|
|
await linkController.modelSaved() |
|
|
|
break |
|
|
|
return await linkController.modelSaved() |
|
|
|
case EventType.MODEL_DELETE: |
|
|
|
await linkController.modelDeleted() |
|
|
|
break |
|
|
|
return await linkController.modelDeleted() |
|
|
|
default: |
|
|
|
throw "Type of event is not known, linked record handler requires update." |
|
|
|
} |
|
|
|
@ -61,9 +56,10 @@ module.exports.updateLinksForModel = async ({ |
|
|
|
* @param {object} record The record which is changing, e.g. created, updated or deleted. |
|
|
|
* @param {string} modelId The ID of the of the model which is being updated. |
|
|
|
* @param {object|null} model If the model has already been retrieved this can be used to reduce database gets. |
|
|
|
* @returns {Promise<null>} When the update is complete this will respond successfully. |
|
|
|
* @returns {Promise<object>} When the update is complete this will respond successfully. Returns the record that was |
|
|
|
* operated upon, cleaned up and prepared for writing to DB. |
|
|
|
*/ |
|
|
|
module.exports.updateLinksForRecord = async ({ |
|
|
|
exports.updateLinksForRecord = async ({ |
|
|
|
eventType, |
|
|
|
instanceId, |
|
|
|
record, |
|
|
|
@ -71,8 +67,8 @@ module.exports.updateLinksForRecord = async ({ |
|
|
|
model, |
|
|
|
}) => { |
|
|
|
// can't operate without these properties
|
|
|
|
if (instanceId == null || modelId == null) { |
|
|
|
return null |
|
|
|
if (instanceId == null || modelId == null || record == null) { |
|
|
|
return record |
|
|
|
} |
|
|
|
let linkController = new LinkController({ |
|
|
|
instanceId, |
|
|
|
@ -81,7 +77,7 @@ module.exports.updateLinksForRecord = async ({ |
|
|
|
record, |
|
|
|
}) |
|
|
|
if (!(await linkController.doesModelHaveLinkedFields())) { |
|
|
|
return null |
|
|
|
return record |
|
|
|
} |
|
|
|
switch (eventType) { |
|
|
|
case EventType.RECORD_SAVE: |
|
|
|
@ -94,6 +90,73 @@ module.exports.updateLinksForRecord = async ({ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Utility function to in parallel up a list of records with link info. |
|
|
|
* @param {string} instanceId The instance in which this record has been created. |
|
|
|
* @param {object[]} records A list records to be updated with link info. |
|
|
|
* @returns {Promise<object[]>} The updated records (this may be the same if no links were found). |
|
|
|
*/ |
|
|
|
exports.attachLinkInfoToRecords = async (instanceId, records) => { |
|
|
|
let recordPromises = [] |
|
|
|
for (let record of records) { |
|
|
|
recordPromises.push(exports.attachLinkInfoToRecord(instanceId, record)) |
|
|
|
} |
|
|
|
return await Promise.all(recordPromises) |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Update a record with information about the links that pertain to it. |
|
|
|
* @param {string} instanceId The instance in which this record has been created. |
|
|
|
* @param {object} record The record itself which is to be updated with info (if applicable). |
|
|
|
* @returns {Promise<object>} The updated record (this may be the same if no links were found). |
|
|
|
*/ |
|
|
|
exports.attachLinkInfoToRecord = async (instanceId, record) => { |
|
|
|
const recordId = record._id |
|
|
|
const modelId = record.modelId |
|
|
|
// get all links for record, ignore fieldName for now
|
|
|
|
const linkDocs = await exports.getLinkDocuments({ |
|
|
|
instanceId, |
|
|
|
modelId, |
|
|
|
recordId, |
|
|
|
includeDocs: true, |
|
|
|
}) |
|
|
|
if (linkDocs == null || linkDocs.length === 0) { |
|
|
|
return record |
|
|
|
} |
|
|
|
for (let linkDoc of linkDocs) { |
|
|
|
// work out which link pertains to this record
|
|
|
|
const doc = linkDoc.doc1.recordId === recordId ? linkDoc.doc1 : linkDoc.doc2 |
|
|
|
if (record[doc.fieldName] == null || record[doc.fieldName].count == null) { |
|
|
|
record[doc.fieldName] = { type: "link", count: 1 } |
|
|
|
} else { |
|
|
|
record[doc.fieldName].count++ |
|
|
|
} |
|
|
|
} |
|
|
|
return record |
|
|
|
} |
|
|
|
|
|
|
|
exports.createLinkView = async instanceId => { |
|
|
|
const db = new CouchDB(instanceId) |
|
|
|
const designDoc = await db.get("_design/database") |
|
|
|
const view = { |
|
|
|
map: function(doc) { |
|
|
|
if (doc.type === "link") { |
|
|
|
let doc1 = doc.doc1 |
|
|
|
let doc2 = doc.doc2 |
|
|
|
emit([doc1.modelId, 1, doc1.fieldName, doc1.recordId], doc2.recordId) |
|
|
|
emit([doc2.modelId, 1, doc2.fieldName, doc2.recordId], doc1.recordId) |
|
|
|
emit([doc1.modelId, 2, doc1.recordId], doc2.recordId) |
|
|
|
emit([doc2.modelId, 2, doc2.recordId], doc1.recordId) |
|
|
|
} |
|
|
|
}.toString(), |
|
|
|
} |
|
|
|
designDoc.views = { |
|
|
|
...designDoc.views, |
|
|
|
by_link: view, |
|
|
|
} |
|
|
|
await db.put(designDoc) |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Gets the linking documents, not the linked documents themselves. |
|
|
|
* @param {string} instanceId The instance in which we are searching for linked records. |
|
|
|
@ -103,17 +166,17 @@ module.exports.updateLinksForRecord = async ({ |
|
|
|
* @param {string|null} recordId The ID of the record which we want to find linking documents for - |
|
|
|
* if this is not specified then it will assume model or field level depending on whether the |
|
|
|
* field name has been specified. |
|
|
|
* @param {boolean|null} includeDoc whether to include docs in the response call, this is considerably slower so only |
|
|
|
* @param {boolean|null} includeDocs whether to include docs in the response call, this is considerably slower so only |
|
|
|
* use this if actually interested in the docs themselves. |
|
|
|
* @returns {Promise<object[]>} This will return an array of the linking documents that were found |
|
|
|
* (if any). |
|
|
|
*/ |
|
|
|
module.exports.getLinkDocuments = async ({ |
|
|
|
exports.getLinkDocuments = async ({ |
|
|
|
instanceId, |
|
|
|
modelId, |
|
|
|
fieldName, |
|
|
|
recordId, |
|
|
|
includeDoc, |
|
|
|
includeDocs, |
|
|
|
}) => { |
|
|
|
const db = new CouchDB(instanceId) |
|
|
|
let params |
|
|
|
@ -131,11 +194,16 @@ module.exports.getLinkDocuments = async ({ |
|
|
|
else { |
|
|
|
params = { startKey: [modelId, 1], endKey: [modelId, 1, {}] } |
|
|
|
} |
|
|
|
params.include_docs = !!includeDoc |
|
|
|
params.include_docs = !!includeDocs |
|
|
|
try { |
|
|
|
const response = await db.query("database/by_link", params) |
|
|
|
return response.rows.map(row => row.doc) |
|
|
|
} catch (err) { |
|
|
|
console.error(err) |
|
|
|
// check if the view doesn't exist, it should for all new instances
|
|
|
|
if (err != null && err.name === "not_found") { |
|
|
|
await exports.createLinkView(instanceId) |
|
|
|
} else { |
|
|
|
console.error(err) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|