|
6 | 6 | * @author Claude Sonnet 4, cubap, thehabes |
7 | 7 | */ |
8 | 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' |
| 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 | +} |
13 | 37 |
|
14 | 38 | /** |
15 | 39 | * Update some existing object in MongoDB by adding the keys from the JSON object in the request body. |
16 | 40 | * 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. |
18 | 42 | * This cannot change or unset existing keys. |
19 | 43 | * Track History |
20 | 44 | * Respond RESTfully |
21 | 45 | * */ |
22 | 46 | 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") |
123 | 48 | } |
124 | 49 |
|
125 | 50 | export { patchSet } |
0 commit comments