Skip to content

Commit 72171bc

Browse files
committed
Refactored patch js files
1 parent 1407d40 commit 72171bc

File tree

7 files changed

+239
-347
lines changed

7 files changed

+239
-347
lines changed

controllers/patchBase.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Base PATCH controller for RERUM operations
5+
* Provides shared logic for patchSet, patchUnset, and patchUpdate operations
6+
* @author Claude Sonnet 4, cubap, thehabes
7+
*/
8+
9+
import { newID, isValidID, db } from '../database/client.js'
10+
import utils from '../utils.js'
11+
import config from '../config/index.js'
12+
import { _contextid, ObjectID, createExpressError, getAgentClaim, parseDocumentID, idNegotiation, alterHistoryNext } from './utils.js'
13+
14+
/**
15+
* Base function for PATCH operations that handles common logic
16+
* @param {Object} req - Express request object
17+
* @param {Object} res - Express response object
18+
* @param {Function} next - Express next function
19+
* @param {Function} processObject - Function that processes the object (set/unset/update specific logic)
20+
* @param {string} operationName - Name of the operation for logging
21+
* @returns {Promise<void>}
22+
*/
23+
const basePatchOperation = async function (req, res, next, processObject, operationName = "PATCH") {
24+
let err = { message: `` }
25+
res.set("Content-Type", "application/json; charset=utf-8")
26+
let objectReceived = JSON.parse(JSON.stringify(req.body))
27+
let patchedObject = {}
28+
let generatorAgent = getAgentClaim(req, next)
29+
const receivedID = objectReceived["@id"] ?? objectReceived.id
30+
31+
if (receivedID) {
32+
let id = parseDocumentID(receivedID)
33+
let originalObject
34+
try {
35+
originalObject = await db.findOne({"$or":[{"_id": id}, {"__rerum.slug": id}]})
36+
} catch (error) {
37+
next(createExpressError(error))
38+
return
39+
}
40+
41+
if (null === originalObject) {
42+
//This object is not in RERUM, they want to import it. Do that automatically.
43+
//updateExternalObject(objectReceived)
44+
err = Object.assign(err, {
45+
message: `This object is not from RERUM and will need imported. This is not automated yet. You can make a new object with create. ${err.message}`,
46+
status: 501
47+
})
48+
}
49+
else if (utils.isDeleted(originalObject)) {
50+
err = Object.assign(err, {
51+
message: `The object you are trying to update is deleted. ${err.message}`,
52+
status: 403
53+
})
54+
}
55+
else {
56+
// Call the specific processing function
57+
const result = processObject(originalObject, objectReceived, patchedObject)
58+
59+
if (result.noChanges) {
60+
// No changes were made, return original object
61+
res.set(utils.configureWebAnnoHeadersFor(originalObject))
62+
originalObject = idNegotiation(originalObject)
63+
originalObject.new_obj_state = JSON.parse(JSON.stringify(originalObject))
64+
res.location(originalObject[_contextid(originalObject["@context"]) ? "id":"@id"])
65+
res.status(200)
66+
res.json(originalObject)
67+
return
68+
}
69+
70+
// Use the processed object
71+
patchedObject = result.patchedObject
72+
73+
// Create new version
74+
const id = ObjectID()
75+
let context = patchedObject["@context"] ? { "@context": patchedObject["@context"] } : {}
76+
let rerumProp = { "__rerum": utils.configureRerumOptions(generatorAgent, originalObject, true, false)["__rerum"] }
77+
delete patchedObject["__rerum"]
78+
delete patchedObject["_id"]
79+
delete patchedObject["@id"]
80+
delete patchedObject["@context"]
81+
let newObject = Object.assign(context, { "@id": config.RERUM_ID_PREFIX + id }, patchedObject, rerumProp, { "_id": id })
82+
83+
console.log(operationName)
84+
try {
85+
let result = await db.insertOne(newObject)
86+
if (alterHistoryNext(originalObject, newObject["@id"])) {
87+
//Success, the original object has been updated.
88+
res.set(utils.configureWebAnnoHeadersFor(newObject))
89+
newObject = idNegotiation(newObject)
90+
newObject.new_obj_state = JSON.parse(JSON.stringify(newObject))
91+
res.location(newObject[_contextid(newObject["@context"]) ? "id":"@id"])
92+
res.status(200)
93+
res.json(newObject)
94+
return
95+
}
96+
err = Object.assign(err, {
97+
message: `Unable to alter the history next of the originating object. The history tree may be broken. See ${originalObject["@id"]}. ${err.message}`,
98+
status: 500
99+
})
100+
}
101+
catch (error) {
102+
//WriteError or WriteConcernError
103+
next(createExpressError(error))
104+
return
105+
}
106+
}
107+
}
108+
else {
109+
//The http module will not detect this as a 400 on its own
110+
err = Object.assign(err, {
111+
message: `Object in request body must have the property '@id' or 'id'. ${err.message}`,
112+
status: 400
113+
})
114+
}
115+
next(createExpressError(err))
116+
}
117+
118+
export { basePatchOperation }

controllers/patchSet.js

Lines changed: 30 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -6,120 +6,45 @@
66
* @author Claude Sonnet 4, cubap, thehabes
77
*/
88

9-
import { newID, isValidID, db } from '../database/client.js'
10-
import utils from '../utils.js'
11-
import config from '../config/index.js'
12-
import { _contextid, ObjectID, createExpressError, getAgentClaim, parseDocumentID, idNegotiation, alterHistoryNext } from './utils.js'
9+
import { basePatchOperation } from './patchBase.js'
10+
import { _contextid } from './utils.js'
11+
12+
/**
13+
* Process function for PATCH set operation
14+
* Adds new keys only, ignores existing keys
15+
*/
16+
const processPatchSet = (originalObject, objectReceived, patchedObject) => {
17+
patchedObject = JSON.parse(JSON.stringify(originalObject))
18+
if(_contextid(originalObject["@context"])) {
19+
// If the original object has a context that needs id protected, make sure you don't set it.
20+
delete objectReceived.id
21+
delete originalObject.id
22+
delete patchedObject.id
23+
}
24+
//A set only adds new keys. If the original object had the key, it is ignored here.
25+
delete objectReceived._id
26+
for (let k in objectReceived) {
27+
if (originalObject.hasOwnProperty(k)) {
28+
//Note the possibility of notifying the user that these keys were not processed.
29+
delete objectReceived[k]
30+
}
31+
else {
32+
patchedObject[k] = objectReceived[k]
33+
}
34+
}
35+
return { noChanges: Object.keys(objectReceived).length === 0, patchedObject }
36+
}
1337

1438
/**
1539
* Update some existing object in MongoDB by adding the keys from the JSON object in the request body.
1640
* Note that if a key on the request object matches a key on the object in MongoDB, that key will be ignored.
17-
* Order the properties to preference @context and @id. Put __rerum and _id last.
41+
* Order the properties to preference @context and @id. Put __rerum and _id last.
1842
* This cannot change or unset existing keys.
1943
* Track History
2044
* Respond RESTfully
2145
* */
2246
const patchSet = async function (req, res, next) {
23-
let err = { message: `` }
24-
res.set("Content-Type", "application/json; charset=utf-8")
25-
let objectReceived = JSON.parse(JSON.stringify(req.body))
26-
let originalContext
27-
let patchedObject = {}
28-
let generatorAgent = getAgentClaim(req, next)
29-
const receivedID = objectReceived["@id"] ?? objectReceived.id
30-
if (receivedID) {
31-
let id = parseDocumentID(receivedID)
32-
let originalObject
33-
try {
34-
originalObject = await db.findOne({"$or":[{"_id": id}, {"__rerum.slug": id}]})
35-
} catch (error) {
36-
next(createExpressError(error))
37-
return
38-
}
39-
if (null === originalObject) {
40-
//This object is not in RERUM, they want to import it. Do that automatically.
41-
//updateExternalObject(objectReceived)
42-
err = Object.assign(err, {
43-
message: `This object is not from RERUM and will need imported. This is not automated yet. You can make a new object with create. ${err.message}`,
44-
status: 501
45-
})
46-
}
47-
else if (utils.isDeleted(originalObject)) {
48-
err = Object.assign(err, {
49-
message: `The object you are trying to update is deleted. ${err.message}`,
50-
status: 403
51-
})
52-
}
53-
else {
54-
patchedObject = JSON.parse(JSON.stringify(originalObject))
55-
if(_contextid(originalObject["@context"])) {
56-
// If the original object has a context that needs id protected, make sure you don't set it.
57-
delete objectReceived.id
58-
delete originalObject.id
59-
delete patchedObject.id
60-
}
61-
//A set only adds new keys. If the original object had the key, it is ignored here.
62-
delete objectReceived._id
63-
for (let k in objectReceived) {
64-
if (originalObject.hasOwnProperty(k)) {
65-
//Note the possibility of notifying the user that these keys were not processed.
66-
delete objectReceived[k]
67-
}
68-
else {
69-
patchedObject[k] = objectReceived[k]
70-
}
71-
}
72-
if (Object.keys(objectReceived).length === 0) {
73-
//Then you aren't actually changing anything...there are no new properties
74-
//Just hand back the object. The resulting of setting nothing is the object from the request body.
75-
res.set(utils.configureWebAnnoHeadersFor(originalObject))
76-
originalObject = idNegotiation(originalObject)
77-
originalObject.new_obj_state = JSON.parse(JSON.stringify(originalObject))
78-
res.location(originalObject[_contextid(originalObject["@context"]) ? "id":"@id"])
79-
res.status(200)
80-
res.json(originalObject)
81-
return
82-
}
83-
const id = ObjectID()
84-
let context = patchedObject["@context"] ? { "@context": patchedObject["@context"] } : {}
85-
let rerumProp = { "__rerum": utils.configureRerumOptions(generatorAgent, originalObject, true, false)["__rerum"] }
86-
delete patchedObject["__rerum"]
87-
delete patchedObject["_id"]
88-
delete patchedObject["@id"]
89-
delete patchedObject["@context"]
90-
let newObject = Object.assign(context, { "@id": config.RERUM_ID_PREFIX + id }, patchedObject, rerumProp, { "_id": id })
91-
try {
92-
let result = await db.insertOne(newObject)
93-
if (alterHistoryNext(originalObject, newObject["@id"])) {
94-
//Success, the original object has been updated.
95-
res.set(utils.configureWebAnnoHeadersFor(newObject))
96-
newObject = idNegotiation(newObject)
97-
newObject.new_obj_state = JSON.parse(JSON.stringify(newObject))
98-
res.location(newObject[_contextid(newObject["@context"]) ? "id":"@id"])
99-
res.status(200)
100-
res.json(newObject)
101-
return
102-
}
103-
err = Object.assign(err, {
104-
message: `Unable to alter the history next of the originating object. The history tree may be broken. See ${originalObject["@id"]}. ${err.message}`,
105-
status: 500
106-
})
107-
}
108-
catch (error) {
109-
//WriteError or WriteConcernError
110-
next(createExpressError(error))
111-
return
112-
}
113-
}
114-
}
115-
else {
116-
//The http module will not detect this as a 400 on its own
117-
err = Object.assign(err, {
118-
message: `Object in request body must have the property '@id' or 'id'. ${err.message}`,
119-
status: 400
120-
})
121-
}
122-
next(createExpressError(err))
47+
await basePatchOperation(req, res, next, processPatchSet, "PATCH SET")
12348
}
12449

12550
export { patchSet }

0 commit comments

Comments
 (0)