api-platform / admin

A beautiful and fully-featured administration interface builder for hypermedia APIs
https://api-platform.com/docs/admin/
MIT License
480 stars 130 forks source link

`params.previousData` is undefined in `update` call with `hydraDataProvider` #520

Open floriandammeyer opened 8 months ago

floriandammeyer commented 8 months ago

API Platform version(s) affected: 3.4.4

Description
We need to use PATCH instead of PUT to update existing resources, so I drew inspiration from this comment https://github.com/api-platform/admin/issues/370#issuecomment-831749350 and overwrote the update method of our data provider, which is an instance of hydraDataProvider.

The new update method should create a diff between the current resource data (given in params.data) and the previous data (given in params.previousData) and then execute a PATCH request.

However, params.previousData is undefined when a user edits a resource and clicks the save button.

I figured it may be caused by some previous error on the page where this behavior occurs, but there are no errors in the JavaScript console on that page. The first error that occurs is that params.previousData is undefined when our diff function is called.

We haven't added much custom logic to our admin implementation, most things are done by the default implementations and automations of API Platform Admin.

What could cause this issue? Is params.previousData supposed to be empty?

How to reproduce

// We use the built-in fetchHydra() and parseHydraDocumentation() functions, 
// but wrapped them to handle authorization as shown in the API Platform Admin docs and examples
const baseDataProvider = hydraDataProvider({
    entrypoint: ENTRYPOINT,
    httpClient: fetchHydraWithAuthentication, 
    apiDocumentationParser: apiDocumentationParser
});

baseDataProvider.update = function(resource, params) {
    return this.httpClient(`${this.entrypoint}/${resource}/${params.id}`, {
        method: 'PATCH',
        body: JSON.stringify(diff(params.data, params.previousData)), 
    }).then(({json}) => ({data: json}));
};

// We use lodash helpers to implement this diff function
const diff = (object, base) => {
    return transform(object, (result, value, key) => {
        if (!isEqual(value, base[key])) {
            result[key] = isObject(value) && isObject(base[key]) ? diff(value, base[key]) : value;
        }
    });
};