Skip to content

Commit

Permalink
feat: check is referenced when delete
Browse files Browse the repository at this point in the history
- The resource will be association with other resource,
If we delete the resource that be referenced by other resource
it will ruin the binding association.

- Add `resourceRefBy` schema to store the association
- Remove unused schema `FHIRValidationFiles`
- Add functions as title does
> In `models/mongodb/common.js`
  • Loading branch information
Chinlinlee committed Nov 15, 2022
1 parent 6c6a40f commit ef49215
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 37 deletions.
20 changes: 18 additions & 2 deletions FHIR-mongoose-Models-Generator/resourceGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,8 @@ function generateResourceSchema (type) {
} , {
upsert : true
});
await storeResourceRefBy(item);
});
${type}Schema.pre('findOneAndUpdate' , async function (next) {
Expand Down Expand Up @@ -386,6 +388,9 @@ function generateResourceSchema (type) {
} catch (e) {
console.error(e);
}
await storeResourceRefBy(item);
return result;
});
Expand All @@ -397,6 +402,11 @@ function generateResourceSchema (type) {
let mongodb = require('../index');
let item = docToDelete.toObject();
delete item._id;
if (await checkResourceHaveReferenceByOthers(item)) {
next(\`The \${item.resourceType}:id->\${item.id} is referenced by multiple resource, please do not delete resource that have association\`);
}
item.meta.versionId = String(Number(item.meta.versionId)+1);
let version = item.meta.versionId;
Expand All @@ -412,13 +422,19 @@ function generateResourceSchema (type) {
next();
});
${type}Schema.post('findOneAndDelete', async function (resource) {
await updateRefBy(resource);
await deleteEmptyRefBy();
});
const ${type}Model = mongoose.model("${type}" , ${type}Schema , "${type}");
return ${type}Model;\r\n}`;

let importLibs = getImportLibs(result);
if (!importLibs.includes("const id = require")) {
importLibs =`const moment = require('moment');\r\nconst _ = require('lodash');\r\n${importLibs}const id = require('${config.requirePath}/id');\r\n`;
importLibs =`const moment = require('moment');\r\nconst _ = require('lodash');\r\n${importLibs}const id = require('${config.requirePath}/id');\r\nconst { storeResourceRefBy, updateRefBy, deleteEmptyRefBy, checkResourceHaveReferenceByOthers } = require("../common");\r\n`;
} else {
importLibs =`const moment = require('moment');\r\nconst _ = require('lodash');\r\n${importLibs}\r\n`;
importLibs =`const moment = require('moment');\r\nconst _ = require('lodash');\r\n${importLibs}\r\nconst { storeResourceRefBy, updateRefBy, deleteEmptyRefBy, checkResourceHaveReferenceByOthers } = require("../common");\r\n`;
}
code = `${importLibs}${code};`;
mkdirp.sync(config.resourcePath);
Expand Down
137 changes: 137 additions & 0 deletions models/mongodb/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
const jp = require("jsonpath");
const mongoose = require("mongoose");

/**
* Store the resource reference by which resources
* e.g.
* {
* resourceType: "Organization",
* id: "123",
* refBy: [
* {
* resourceType: "Patient",
* id:
* }
* ]
* }
* @param {Object} resource
*/
async function storeResourceRefBy(resource) {
let referenceInItem = jp.nodes(resource, "$..reference");
for (let refNode of referenceInItem) {
let referenceSplit = refNode.value.split("/");
let id = referenceSplit[referenceSplit.length - 1];
let resourceType = referenceSplit[referenceSplit.length - 2];

await mongoose.model("resourceRefBy").findOneAndUpdate({
$and: [
{
resourceType: resourceType
},
{
id: id
}
]
}, {
$set: {
resourceType: resourceType,
id: id
},
$addToSet: {
refBy: {
resourceType: resource.resourceType,
id: resource.id
}
}
}, {
upsert: true
});
}

}

/**
* If resource not reference by any resource, delete this resource info in any refBy array
* > Use in post delete
* @param {Object} resource
*/
async function updateRefBy(resource) {
try {
await mongoose.model("resourceRefBy").updateMany({
$and: [
{
"refBy.resourceType": resource.resourceType
},
{
"refBy.id": resource.id
}
]
}, {
$pull: {
refBy: {
resourceType: resource.resourceType,
id: resource.id
}
}
});
} catch (e) {
throw e;
}
}

/**
* After updating refBy, some array will be empty that mean the resource is not referenced by any resource anymore.
* So, we need to delete document that have empty refBy array.
*/
async function deleteEmptyRefBy() {
try {
await mongoose.model("resourceRefBy").deleteMany({
$and: [
{
refBy: {
$exists: true
}
},
{
refBy: {
$size: 0
}
}
]
});
} catch (e) {
throw e;
}
}

/**
* We must check the resource is referenced by any resources when fire deleting.
* 1. If resource has referenced by any resource, throw error
* 2. Do next process otherwise.
* > Use in pre delete
*/
async function checkResourceHaveReferenceByOthers(resource) {
try {
let data = await mongoose.model("resourceRefBy").countDocuments({
$and: [
{
resourceType: resource.resourceType,
id: resource.id
}
]
}).limit(1);

if (data > 0) {
return true;
}
return false;
} catch (e) {
console.error(e);
throw e;
}
}

module.exports.storeResourceRefBy = storeResourceRefBy;
module.exports.updateRefBy = updateRefBy;
module.exports.deleteEmptyRefBy = deleteEmptyRefBy;
module.exports.checkResourceHaveReferenceByOthers = checkResourceHaveReferenceByOthers;
21 changes: 21 additions & 0 deletions models/mongodb/model/Patient.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ const {
Patient_Link
} = require('../FHIRDataTypesSchemaExport/FHIRDataTypesSchemaExport');
const id = require('../FHIRDataTypesSchema/id');
const {
storeResourceRefBy,
updateRefBy,
deleteEmptyRefBy,
checkResourceHaveReferenceByOthers
} = require("../common");
module.exports = function() {
require('mongoose-schema-jsonschema')(mongoose);
const Patient = {
Expand Down Expand Up @@ -234,6 +240,8 @@ module.exports = function() {
}, {
upsert: true
});

await storeResourceRefBy(item);
});

PatientSchema.pre('findOneAndUpdate', async function(next) {
Expand Down Expand Up @@ -270,6 +278,9 @@ module.exports = function() {
} catch (e) {
console.error(e);
}

await storeResourceRefBy(item);

return result;
});

Expand All @@ -281,6 +292,11 @@ module.exports = function() {
let mongodb = require('../index');
let item = docToDelete.toObject();
delete item._id;

if (await checkResourceHaveReferenceByOthers(item)) {
next(`The ${item.resourceType}:id->${item.id} is referenced by multiple resource, please do not delete resource that have association`);
}

item.meta.versionId = String(Number(item.meta.versionId) + 1);
let version = item.meta.versionId;

Expand All @@ -296,6 +312,11 @@ module.exports = function() {
next();
});

PatientSchema.post('findOneAndDelete', async function(resource) {
await updateRefBy(resource);
await deleteEmptyRefBy();
});

const PatientModel = mongoose.model("Patient", PatientSchema, "Patient");
return PatientModel;
};
35 changes: 0 additions & 35 deletions models/mongodb/staticModel/FHIRValidationFiles.js

This file was deleted.

73 changes: 73 additions & 0 deletions models/mongodb/staticModel/referenceBy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const instant = require("../FHIRDataTypesSchema/instant");

/**
* The schema to storing each resource is refer by which resources
* 1. Update data when update or create resource
* 2. Check resource is exist in this data to prevent delete the resource refer by another resources
* @Author Chin-Lin Lee <a5566qq2581@gmail.com>
*/

/**
*
* @param {import("mongoose")} mongodb
* @returns
*/
module.exports = function (mongodb) {
let basicInfo = new mongodb.Schema({
"resourceType": {
type: String,
required: true,
default: void 0
},
"id": {
type: String,
required: true,
default: void 0
}
}, {
_id : false,
id: false
});

let resourceRefBy = new mongodb.Schema({


}, {
versionKey : false
});

resourceRefBy.add(basicInfo);
resourceRefBy.add({
lastUpdated: {
...instant,
default: Date.now()
}
});
resourceRefBy.add(new mongodb.Schema({
refBy: {
type: [basicInfo],
default: void 0
}
}, {
_id : false,
id: false
}));

resourceRefBy.index({
"id": 1,
"resourceType": 1
}, {
background: true
});

resourceRefBy.index({
"refBy.id": 1,
"refBy.resourceType": 1
}, {
background: true
});


let resourceRefByModel = mongodb.model('resourceRefBy', resourceRefBy, 'resourceRefBy');
return resourceRefByModel;
};

0 comments on commit ef49215

Please sign in to comment.