ra-core
Version:
Core components of react-admin, a frontend Framework for building admin applications on top of REST services, using ES6, React
383 lines • 15.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyCallbacks = exports.withLifecycleCallbacks = void 0;
/**
* Extend a dataProvider to execute callbacks before and after read and write calls.
*
* @param {DataProvider} dataProvider The dataProvider to wrap
* @param {ResourceCallbacks[]} handlers An array of ResourceCallbacks
*
* @typedef {Object} ResourceCallbacks
* @property {string} resource The resource name
* @property {AfterCreate} [afterCreate] A callback (or array of callbacks) executed after create
* @property {AfterDelete} [afterDelete] A callback (or array of callbacks) executed after delete
* @property {AfterDeleteMany} [afterDeleteMany] A callback (or array of callbacks) executed after deleteMany
* @property {AfterGetList} [afterGetList] A callback (or array of callbacks) executed after getList
* @property {AfterGetMany} [afterGetMany] A callback (or array of callbacks) executed after getMany
* @property {AfterGetManyReference} [afterGetManyReference] A callback (or array of callbacks) executed after getManyReference
* @property {AfterGetOne} [afterGetOne] A callback (or array of callbacks) executed after getOne
* @property {AfterRead} [afterRead] A callback (or array of callbacks) executed after read (getList, getMany, getManyReference, getOne)
* @property {AfterSave} [afterSave] A callback (or array of callbacks) executed after save (create, update, updateMany)
* @property {AfterUpdate} [afterUpdate] A callback (or array of callbacks) executed after update
* @property {AfterUpdateMany} [afterUpdateMany] A callback (or array of callbacks) executed after updateMany
* @property {BeforeCreate} [beforeCreate] A callback (or array of callbacks) executed before create
* @property {BeforeDelete} [beforeDelete] A callback (or array of callbacks) executed before delete
* @property {BeforeDeleteMany} [beforeDeleteMany] A callback (or array of callbacks) executed before deleteMany
* @property {BeforeGetList} [beforeGetList] A callback (or array of callbacks) executed before getList
* @property {BeforeGetMany} [beforeGetMany] A callback (or array of callbacks) executed before getMany
* @property {BeforeGetManyReference} [beforeGetManyReference] A callback (or array of callbacks) executed before getManyReference
* @property {BeforeGetOne} [beforeGetOne] A callback (or array of callbacks) executed before getOne
* @property {BeforeSave} [beforeSave] A callback (or array of callbacks) executed before save (create, update, updateMany)
* @property {BeforeUpdate} [beforeUpdate] A callback (or array of callbacks) executed before update
* @property {BeforeUpdateMany} [beforeUpdateMany] A callback (or array of callbacks) executed before updateMany
*
* Warnings:
* - As queries issued in the callbacks are not done through react-query,
* any change in the data will not be automatically reflected in the UI.
* - The callbacks are not executed in a transaction. In case of error,
* the backend may be left in an inconsistent state.
* - When calling the API directly using fetch or another client,
* the callbacks will not be executed, leaving the backend in a possibly inconsistent state.
* - If a callback triggers the query it's listening to, this will lead to a infinite loop.
*
* @example
*
* const dataProvider = withLifecycleCallbacks(
* jsonServerProvider("http://localhost:3000"),
* [
* {
* resource: "posts",
* afterRead: async (data, dataProvider, resource) => {
* // rename field to the record
* data.user_id = data.userId;
* return data;
* },
* // executed after create, update and updateMany
* afterSave: async (record, dataProvider, resource) => {
* // update the author's nb_posts
* const { total } = await dataProvider.getList("users", {
* filter: { id: record.user_id },
* pagination: { page: 1, perPage: 1 },
* });
* await dataProvider.update("users", {
* id: user.id,
* data: { nb_posts: total },
* previousData: user,
* });
* return record;
* },
* beforeDelete: async (params, dataProvider, resource) => {
* // delete all comments linked to the post
* const { data: comments } = await dataProvider.getManyReference(
* "comments",
* {
* target: "post_id",
* id: params.id,
* }
* );
* if (comments.length > 0) {
* await dataProvider.deleteMany("comments", {
* ids: comments.map((comment) => comment.id),
* });
* }
* // update the author's nb_posts
* const { data: post } = await dataProvider.getOne("posts", {
* id: params.id,
* });
* const { total } = await dataProvider.getList("users", {
* filter: { id: post.user_id },
* pagination: { page: 1, perPage: 1 },
* });
* await dataProvider.update("users", {
* id: user.id,
* data: { nb_posts: total - 1 },
* previousData: user,
* });
* return params;
* },
* },
* ]
* );
*/
const withLifecycleCallbacks = (dataProvider, handlers) => {
return {
...dataProvider,
getList: async function (resource, params) {
let newParams = params;
newParams = await (0, exports.applyCallbacks)({
name: 'beforeGetList',
params: newParams,
dataProvider,
handlers,
resource,
});
let result = await dataProvider.getList(resource, newParams);
result = await (0, exports.applyCallbacks)({
name: 'afterGetList',
params: result,
dataProvider,
handlers,
resource,
});
result.data = await Promise.all(result.data.map(record => (0, exports.applyCallbacks)({
name: 'afterRead',
params: record,
dataProvider,
handlers,
resource,
})));
return result;
},
getOne: async function (resource, params) {
let newParams = params;
newParams = await (0, exports.applyCallbacks)({
name: 'beforeGetOne',
params: newParams,
dataProvider,
handlers,
resource,
});
let result = await dataProvider.getOne(resource, newParams);
result = await (0, exports.applyCallbacks)({
name: 'afterGetOne',
params: result,
dataProvider,
handlers,
resource,
});
result.data = await (0, exports.applyCallbacks)({
name: 'afterRead',
params: result.data,
dataProvider,
handlers,
resource,
});
return result;
},
getMany: async function (resource, params) {
let newParams = params;
newParams = await (0, exports.applyCallbacks)({
name: 'beforeGetMany',
params: newParams,
dataProvider,
handlers,
resource,
});
let result = await dataProvider.getMany(resource, newParams);
result = await (0, exports.applyCallbacks)({
name: 'afterGetMany',
params: result,
dataProvider,
handlers,
resource,
});
result.data = await Promise.all(result.data.map(record => (0, exports.applyCallbacks)({
name: 'afterRead',
params: record,
dataProvider,
handlers,
resource,
})));
return result;
},
getManyReference: async function (resource, params) {
let newParams = params;
newParams = await (0, exports.applyCallbacks)({
name: 'beforeGetManyReference',
params: newParams,
dataProvider,
handlers,
resource,
});
let result = await dataProvider.getManyReference(resource, newParams);
result = await (0, exports.applyCallbacks)({
name: 'afterGetManyReference',
params: result,
dataProvider,
handlers,
resource,
});
result.data = await Promise.all(result.data.map(record => (0, exports.applyCallbacks)({
name: 'afterRead',
params: record,
dataProvider,
handlers,
resource,
})));
return result;
},
update: async function (resource, params) {
let newParams = params;
newParams = await (0, exports.applyCallbacks)({
name: 'beforeUpdate',
params: newParams,
dataProvider,
handlers,
resource,
});
newParams.data = await (0, exports.applyCallbacks)({
name: 'beforeSave',
params: newParams.data,
dataProvider,
handlers,
resource,
});
let result = await dataProvider.update(resource, newParams);
result = await (0, exports.applyCallbacks)({
name: 'afterUpdate',
params: result,
dataProvider,
handlers,
resource,
});
result.data = await (0, exports.applyCallbacks)({
name: 'afterSave',
params: result.data,
dataProvider,
handlers,
resource,
});
return result;
},
create: async function (resource, params) {
let newParams = params;
newParams = await (0, exports.applyCallbacks)({
name: 'beforeCreate',
params: newParams,
dataProvider,
handlers,
resource,
});
newParams.data = await (0, exports.applyCallbacks)({
name: 'beforeSave',
params: newParams.data,
dataProvider,
handlers,
resource,
});
let result = await dataProvider.create(resource, newParams);
result = await (0, exports.applyCallbacks)({
name: 'afterCreate',
params: result,
dataProvider,
handlers,
resource,
});
result.data = await (0, exports.applyCallbacks)({
name: 'afterSave',
params: result.data,
dataProvider,
handlers,
resource,
});
return result;
},
delete: async function (resource, params) {
let newParams = params;
newParams = await (0, exports.applyCallbacks)({
name: 'beforeDelete',
params: newParams,
dataProvider,
handlers,
resource,
});
let result = await dataProvider.delete(resource, newParams);
result = await (0, exports.applyCallbacks)({
name: 'afterDelete',
params: result,
dataProvider,
handlers,
resource,
});
return result;
},
updateMany: async function (resource, params) {
let newParams = params;
newParams = await (0, exports.applyCallbacks)({
name: 'beforeUpdateMany',
params: newParams,
dataProvider,
handlers,
resource,
});
newParams.data = await (0, exports.applyCallbacks)({
name: 'beforeSave',
params: newParams.data,
dataProvider,
handlers,
resource,
});
let result = await dataProvider.updateMany(resource, newParams);
result = await (0, exports.applyCallbacks)({
name: 'afterUpdateMany',
params: result,
dataProvider,
handlers,
resource,
});
const afterSaveHandlers = handlers.filter(h => (h.resource === resource || h.resource === '*') &&
h.afterSave);
if (afterSaveHandlers.length > 0) {
const { data: records } = await dataProvider.getMany(resource, {
//@ts-ignore
ids: result.data,
});
await Promise.all(records.map(record => (0, exports.applyCallbacks)({
name: 'afterSave',
params: record,
dataProvider,
handlers,
resource,
})));
}
return result;
},
deleteMany: async function (resource, params) {
let newParams = params;
newParams = await (0, exports.applyCallbacks)({
name: 'beforeDeleteMany',
params: newParams,
dataProvider,
handlers,
resource,
});
let result = await dataProvider.deleteMany(resource, newParams);
result = await (0, exports.applyCallbacks)({
name: 'afterDeleteMany',
params: result,
dataProvider,
handlers,
resource,
});
return result;
},
};
};
exports.withLifecycleCallbacks = withLifecycleCallbacks;
/**
* Apply callbacks to the params for the given resource and hook
* @param {DataProvider} dataProvider The dataProvider
* @param {ResourceCallbacks[]} handlers An array of ResourceCallbacks
* @param {string} resource The resource name
* @param {string} hook The hook name (beforeGetList, afterGetOne, etc.)
* @param {U} params The params / result to pass to the callbacks
* @returns {Promise<U>} The params / result after the callbacks have been applied
*/
const applyCallbacks = async function ({ name, params, dataProvider, handlers, resource, }) {
let newParams = params;
const handlersToApply = handlers.filter(h => (h.resource === resource || h.resource === '*') && h[name]);
for (const handler of handlersToApply) {
const callbacksValue = handler[name];
if (Array.isArray(callbacksValue)) {
for (const callback of callbacksValue ?? []) {
newParams = await callback(newParams, dataProvider, resource);
}
}
else {
newParams = await callbacksValue(newParams, dataProvider, resource);
}
}
return newParams;
};
exports.applyCallbacks = applyCallbacks;
//# sourceMappingURL=withLifecycleCallbacks.js.map