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

Daniel Axtens dja at axtens.net
Thu Aug 19 12:15:07 AEST 2021


Hi,

This fixes all my functional issues. Applying your other patches, this
now counts to at least 102 without issue and displays a network error
appropriately when I kill the server.

At some point I want to turn this all into a tristate: add a 'pending'
state in the frontend while we wait for the network request to resolve,
but that's complex to do and is only really useful in the edgecase where
the request fails for some reason. So it shouldn't block inclusion now.

Pending Stephen's stylistic comments:
Reviewed-by: Daniel Axtens <dja at axtens.net>

Stephen, I'll let you merge this once you're happy with the style.

Kind regards,
Daniel

> 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.
>
> Signed-off-by: Raxel Gutierrez <raxel at google.com>
> ---
>  htdocs/js/rest.js | 110 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 110 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..5be5682e
> --- /dev/null
> +++ b/htdocs/js/rest.js
> @@ -0,0 +1,110 @@
> +/**
> + * 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
> +        }).catch(error => {
> +            handleErrorMessages(error);
> +        });
> +}
> +
> +/**
> + * 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 currentMessageCount = messages.firstChild.textContent.match('^([0-9]+)')[0];
> +        const newMessageCount = parseInt(currentMessageCount) + 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