-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathpatchUpdate.js
More file actions
140 lines (133 loc) · 6.59 KB
/
patchUpdate.js
File metadata and controls
140 lines (133 loc) · 6.59 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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/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, createExpressError, 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) {
const perfStart = Date.now()
console.log(`\x1b[36m[PERF] RERUM patchUpdate started for ${req.body?.["@id"] || req.body?.id}\x1b[0m`)
let err = { message: `` }
res.set("Content-Type", "application/json; charset=utf-8")
let objectReceived = JSON.parse(JSON.stringify(req.body))
let patchedObject = {}
let generatorAgent = getAgentClaim(req, next)
const receivedID = objectReceived["@id"] ?? objectReceived.id
if (receivedID) {
let id = parseDocumentID(receivedID)
let originalObject
try {
const findStart = Date.now()
originalObject = await db.findOne({"$or":[{"_id": id}, {"__rerum.slug": id}]})
console.log(`\x1b[35m[PERF] RERUM patchUpdate: db.findOne took ${Date.now() - findStart}ms\x1b[0m`)
} catch (error) {
next(createExpressError(error))
return
}
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 = JSON.parse(JSON.stringify(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 = JSON.parse(JSON.stringify(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 })
console.log("PATCH UPDATE")
try {
const insertStart = Date.now()
let result = await db.insertOne(newObject)
console.log(`\x1b[35m[PERF] RERUM patchUpdate: db.insertOne took ${Date.now() - insertStart}ms\x1b[0m`)
const historyStart = Date.now()
if (alterHistoryNext(originalObject, newObject["@id"])) {
console.log(`\x1b[35m[PERF] RERUM patchUpdate: alterHistoryNext took ${Date.now() - historyStart}ms\x1b[0m`)
console.log(`\x1b[32m[PERF] RERUM patchUpdate total: ${Date.now() - perfStart}ms\x1b[0m`)
//Success, the original object has been updated.
res.set(utils.configureWebAnnoHeadersFor(newObject))
newObject = idNegotiation(newObject)
newObject.new_obj_state = JSON.parse(JSON.stringify(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
next(createExpressError(error))
return
}
}
}
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
})
}
next(createExpressError(err))
}
export { patchUpdate }