[PATCH 3/3] static: add rest.js to handle PATCH requests & respective responses

Daniel Axtens dja at axtens.net
Tue Aug 17 12:31:09 AEST 2021


Raxel Gutierrez <raxel at google.com> writes:

> 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. 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.

I think most of my comments from the fuller series still apply, in particular:

 - the counter goes 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 2. I think just have
   a global variable to store the number of items successfully updated?

 - you need to handle the case where `fetch()` fails. (e.g. network issues)

[error display (when I cherry picked the rest of the fuller series on
top of this smaller series - and I may have resolved a conflict wrong)
is still a bit quirky but I don't think that's the fault of this patch.]

It is genuinely quite exciting to be calling the API from the
front-end, feels like patchwork is finally entering the 2010s! :P

Kind regards,
Daniel

>
> Signed-off-by: Raxel Gutierrez <raxel at google.com>
> ---
>  htdocs/js/rest.js | 107 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 107 insertions(+)
>  create mode 100644 htdocs/js/rest.js
>
> diff --git a/htdocs/js/rest.js b/htdocs/js/rest.js
> new file mode 100644
> index 00000000..f8e0a981
> --- /dev/null
> +++ b/htdocs/js/rest.js
> @@ -0,0 +1,107 @@
> +/**
> + * Sends PATCH requests to update objects' properties using the REST API.
> + * @param {string} url Path to the REST API endpoint.
> + * @param {{field: string, value: string}} data
> + *     field: Name of the property field to update.
> + *     value: Value to update the property field to.
> + * @param {{none: string, some: string}} updateMessage
> + *     none: Message when object update failed due to errors.
> + *     some: Message when object update successful.
> + */
> +async function updateProperty(url, data, updateMessage) {
> +    const request = new Request(url, {
> +        method: 'PATCH',
> +        mode: "same-origin",
> +        headers: {
> +            "X-CSRFToken": Cookies.get("csrftoken"),
> +            "Content-Type": "application/json",
> +        },
> +        body: JSON.stringify(data),
> +    });
> +
> +    return await fetch(request)
> +        .then(response => {
> +            let message = updateMessage.some;
> +            let success = true;
> +            if (!response.ok) {
> +                response.text().then(text => {
> +                    const responseObject = JSON.parse(text);
> +                    // Add error messages from response to page
> +                    for (const [key,value] of Object.entries(responseObject)) {
> +                        if (Array.isArray(value)) {
> +                            for (const error of value) {
> +                                handleErrorMessages(key + ": " + error);
> +                            }
> +                        } else {
> +                            handleErrorMessages(key + ": " + value);
> +                        }
> +                    }
> +                });
> +                // Update message to be unsuccessful
> +                message = updateMessage.none;
> +                success = false;
> +            }
> +            handleUpdateMessages(message, success);
> +            return response.ok
> +        });
> +}
> +
> +/**
> + * Populates update messages for API REST requests.
> + * @param {string} messageContent Text for update message.
> + */
> +function handleUpdateMessages(messageContent, success) {
> +    // Replace error and failure update messages with success update message
> +    const errorContainer = document.getElementById("errors");
> +    let messages = document.getElementsByClassName("messages")[0];
> +    if (success && errorContainer.firstChild != null) {
> +        messages.replaceChildren();
> +        errorContainer.replaceChildren();
> +    } else if (!success) {
> +        messages.replaceChildren();
> +    }
> +
> +    // Increment counter of consecutive success update messages
> +    if (messages.firstChild != null) {
> +        const newMessageCount = parseInt(messages.firstChild.textContent.slice(0,1)) + 1
> +        messageContent = newMessageCount + messageContent.slice(1);
> +    }
> +
> +    // Create new message element and add to list
> +    const message = document.createElement("li");
> +    message.setAttribute("class", "message");
> +    if (success) {
> +        message.classList.add("class", "success");
> +    } else {
> +        message.classList.add("class", "error");
> +    }
> +    message.textContent = messageContent;
> +    messages.replaceChildren(...[message]);
> +}
> +
> +/**
> + * Populates error messages for API REST requests.
> + * @param {string} errorMessage Text for error message.
> + */
> +function handleErrorMessages(errorMessage) {
> +    let errorContainer = document.getElementById("errors");
> +    let errorHeader = document.getElementById("errors-header");
> +    let errorList = document.getElementsByClassName("error-list")[0];
> +
> +    // Create errors list and error header if container contents removed
> +    if (errorList == null) {
> +        errorHeader = document.createElement("p");
> +        errorList = document.createElement("ul");
> +        errorHeader.setAttribute("id", "errors-header")
> +        errorHeader.textContent = "The following errors were encountered while making updates:";
> +        errorList.setAttribute("class", "error-list");
> +        errorContainer.appendChild(errorHeader);
> +        errorContainer.appendChild(errorList);
> +    }
> +
> +    const error = document.createElement("li");
> +    error.textContent = errorMessage;
> +    errorList.appendChild(error);
> +}
> +
> +export { updateProperty };
> -- 
> 2.33.0.rc1.237.g0d66db33f3-goog
>
> _______________________________________________
> Patchwork mailing list
> Patchwork at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/patchwork


More information about the Patchwork mailing list