Skip to content

Commit 8a6304b

Browse files
Raxel Gutierrezstephenfin
authored andcommitted
static: add rest.js to handle PATCH requests & respective responses
Add `rest.js` file to have a utilities JavaScript module that can be reused by any Patchwork JS files that make requests to the REST API. The comments for each function follow the Google JS Style guide [1] which is something that would be nice to have for better documented frontend code, especially for JS modules that export functions like rest.js. In particular, this patch provides the following function: - `updateProperty`: make PATCH requests that partially update the fields of an object given it's REST API endpoint specified by the caller. Also, the caller can specify the field(s) to modify and the associated content for update messages in the case of both failed successful requests that render to the current webpage. The caller receives whether the request was successful or not. The `rest.js` module can be further expanded to support and provide functions that allow for other requests (e.g. GET, POST, PUT) to the REST API. Also, add functions that handle update & error messages for these PATCH requests that match the Django messages framework format and form error styling. These functions are internal to the module and aren't exposed outside of the `rest.js` file. Error and accompanying failed update messages are replaced by successful update messages and vice versa. Consecutive successful update messages add to a counter of updated objects. Consecutive error messages stack up. Signed-off-by: Raxel Gutierrez <raxel@google.com> Reviewed-by: Daniel Axtens <dja@axtens.net> Reviewed-by: Stephen Finucane <stephen@that.guru>
1 parent 5de787c commit 8a6304b

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

htdocs/README.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ js
122122
:GitHub: https://github.com/js-cookie/js-cookie/
123123
:Version: 3.0.0
124124

125+
``rest.js.``
126+
Utility module for REST API requests to be used by other Patchwork JS files.
127+
128+
Part of Patchwork.
129+
125130
``selectize.min.js``
126131
Selectize is the hybrid of a ``textbox`` and ``<select>`` box. It's jQuery
127132
based and it has autocomplete and native-feeling keyboard navigation; useful

htdocs/js/rest.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* Sends PATCH requests to update objects' properties using the REST API.
3+
* @param {string} url Path to the REST API endpoint.
4+
* @param {{field: string, value: string}} data
5+
* field: Name of the property field to update.
6+
* value: Value to update the property field to.
7+
* @param {{error: string, success: string}} updateMessage
8+
* error: Message when object update failed due to errors.
9+
* success: Message when object update successful.
10+
* @return {boolean} Whether the request was successful.
11+
*/
12+
async function updateProperty(url, data, updateMessage) {
13+
const request = new Request(url, {
14+
method: 'PATCH',
15+
mode: "same-origin",
16+
headers: {
17+
// Get csrftoken using 'js-cookie' module
18+
"X-CSRFToken": Cookies.get("csrftoken"),
19+
"Content-Type": "application/json",
20+
},
21+
body: JSON.stringify(data),
22+
});
23+
24+
return await fetch(request)
25+
.then(response => {
26+
let message = updateMessage.success;
27+
if (!response.ok) {
28+
response.json().then(responseObject => {
29+
// Add error messages from response body to page
30+
// which can be an array of errors for a given key
31+
for (const [key, value] of Object.entries(responseObject)) {
32+
if (Array.isArray(value)) {
33+
for (const error of value) {
34+
handleErrorMessage(`${key} : ${error}`);
35+
}
36+
} else {
37+
handleErrorMessage(`${key} : ${value}`);
38+
}
39+
}
40+
});
41+
// Update message to be unsuccessful
42+
message = updateMessage.error;
43+
}
44+
handleUpdateMessage(message, response.ok);
45+
return response.ok
46+
}).catch(error => {
47+
handleErrorMessage(error);
48+
return false
49+
});
50+
}
51+
52+
/**
53+
* Populates update messages for API REST requests.
54+
* @param {string} message Text for update message.
55+
* @param {boolean} success Whether the request was successful.
56+
*/
57+
function handleUpdateMessage(message, success) {
58+
// Replace error and failure update messages with success update message
59+
const errorContainer = document.getElementById("errors");
60+
let messages = document.getElementsByClassName("messages")[0];
61+
if (success && errorContainer.firstChild != null) {
62+
messages.replaceChildren();
63+
errorContainer.replaceChildren();
64+
} else if (!success) {
65+
messages.replaceChildren();
66+
}
67+
68+
// Increment counter of consecutive success update messages
69+
if (messages.firstChild != null) {
70+
const currentMessageCount = messages.firstChild.textContent.match('^([0-9]+)');
71+
// Number matched in message
72+
if (currentMessageCount != null) {
73+
const newMessageCount = parseInt(currentMessageCount) + 1;
74+
message = newMessageCount + message.slice(1);
75+
} else {
76+
// No number matched in message
77+
message = "1" + message.slice(1);
78+
}
79+
}
80+
81+
// Create new message element and add to list
82+
const messageElem = document.createElement("li");
83+
messageElem.setAttribute("class", "message");
84+
if (success) {
85+
messageElem.classList.add("success");
86+
} else {
87+
messageElem.classList.add("error");
88+
}
89+
messageElem.textContent = message;
90+
messages.replaceChildren(...[messageElem]);
91+
}
92+
93+
/**
94+
* Populates error messages for API REST requests.
95+
* @param {string} message Text for error message.
96+
*/
97+
function handleErrorMessage(message) {
98+
let errorContainer = document.getElementById("errors");
99+
let errorHeader = document.getElementById("errors-header");
100+
let errorList = document.getElementsByClassName("error-list")[0];
101+
102+
// Create errors list and error header if container contents removed
103+
if (errorList == null) {
104+
errorHeader = document.createElement("p");
105+
errorList = document.createElement("ul");
106+
errorHeader.setAttribute("id", "errors-header")
107+
errorHeader.textContent = "The following errors were encountered while making updates:";
108+
errorList.setAttribute("class", "error-list");
109+
errorContainer.appendChild(errorHeader);
110+
errorContainer.appendChild(errorList);
111+
}
112+
113+
const error = document.createElement("li");
114+
error.textContent = message;
115+
errorList.appendChild(error);
116+
}
117+
118+
export { updateProperty };

0 commit comments

Comments
 (0)