-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathpatchUpdate.js
More file actions
126 lines (122 loc) · 5.79 KB
/
patchUpdate.js
File metadata and controls
126 lines (122 loc) · 5.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env node
/**
* PATCH Update controller for RERUM operations
* Handles PATCH updates that modify existing keys
* @author Claude Sonnet 4, cubap, thehabes
*/
import { newID, isValidID, db } from '../database/index.js'
import utils from '../utils.js'
import { _contextid, ObjectID, getAgentClaim, parseDocumentID, idNegotiation, alterHistoryNext } from './utils.js'
/**
* Update some existing object in MongoDB by changing the keys from the JSON object in the request body.
* Keys in the request body that do not exist in the original object will be ignored.
* Order the properties to preference @context and @id. Put __rerum and _id last.
* Track History
* Respond RESTfully
* */
const patchUpdate = async function (req, res, next) {
let err = { message: `` }
res.set("Content-Type", "application/json; charset=utf-8")
let objectReceived = utils.cloneObject(req.body)
let patchedObject = {}
let generatorAgent = getAgentClaim(req, next)
if (!generatorAgent) return
const receivedID = objectReceived["@id"] ?? objectReceived.id
if (receivedID) {
let id = parseDocumentID(receivedID)
let originalObject
try {
originalObject = await db.findOne({"$or":[{"_id": id}, {"__rerum.slug": id}]})
} catch (error) {
return next(utils.createExpressError(error))
}
if (null === originalObject) {
//This object is not in RERUM, they want to import it. Do that automatically.
//updateExternalObject(objectReceived)
err = Object.assign(err, {
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}`,
status: 501
})
}
else if (utils.isDeleted(originalObject)) {
err = Object.assign(err, {
message: `The object you are trying to update is deleted. ${err.message}`,
status: 403
})
}
else {
patchedObject = utils.cloneObject(originalObject)
delete objectReceived.__rerum //can't patch this
delete objectReceived._id //can't patch this
delete objectReceived["@id"] //can't patch this
// id is also protected in this case, so it can't be set.
if(_contextid(objectReceived["@context"])) delete objectReceived.id
//A patch only alters existing keys. Remove non-existent keys from the object received in the request body.
for (let k in objectReceived) {
if (originalObject.hasOwnProperty(k)) {
if (objectReceived[k] === null) {
delete patchedObject[k]
}
else {
patchedObject[k] = objectReceived[k]
}
}
else {
//Note the possibility of notifying the user that these keys were not processed.
delete objectReceived[k]
}
}
if (Object.keys(objectReceived).length === 0) {
//Then you aren't actually changing anything...only @id came through
//Just hand back the object. The resulting of patching nothing is the object unchanged.
res.set(utils.configureWebAnnoHeadersFor(originalObject))
originalObject = idNegotiation(originalObject)
originalObject.new_obj_state = utils.cloneObject(originalObject)
res.location(originalObject[_contextid(originalObject["@context"]) ? "id":"@id"])
res.status(200)
res.json(originalObject)
return
}
const id = ObjectID()
let context = patchedObject["@context"] ? { "@context": patchedObject["@context"] } : {}
let rerumProp = { "__rerum": utils.configureRerumOptions(generatorAgent, originalObject, true, false)["__rerum"] }
delete patchedObject["__rerum"]
delete patchedObject["_id"]
delete patchedObject["@id"]
// id is also protected in this case, so it can't be set.
if(_contextid(patchedObject["@context"])) delete patchedObject.id
delete patchedObject["@context"]
let newObject = Object.assign(context, { "@id": process.env.RERUM_ID_PREFIX + id }, patchedObject, rerumProp, { "_id": id })
try {
let result = await db.insertOne(newObject)
if (alterHistoryNext(originalObject, newObject["@id"])) {
//Success, the original object has been updated.
res.set(utils.configureWebAnnoHeadersFor(newObject))
newObject = idNegotiation(newObject)
newObject.new_obj_state = utils.cloneObject(newObject)
res.location(newObject[_contextid(newObject["@context"]) ? "id":"@id"])
res.status(200)
res.json(newObject)
return
}
err = Object.assign(err, {
message: `Unable to alter the history next of the originating object. The history tree may be broken. See ${originalObject["@id"]}. ${err.message}`,
status: 500
})
}
catch (error) {
//WriteError or WriteConcernError
return next(utils.createExpressError(error))
}
}
}
else {
//The http module will not detect this as a 400 on its own
err = Object.assign(err, {
message: `Object in request body must have the property '@id' or 'id'. ${err.message}`,
status: 400
})
}
return next(utils.createExpressError(err))
}
export { patchUpdate }