adminjs
Version:
Admin panel for apps written in node.js
107 lines (103 loc) • 4.8 kB
JavaScript
import BaseResource from '../../adapters/resource/base-resource.js';
import { mergeResourceOptions } from '../build-feature/index.js';
export class NoDatabaseAdapterError extends Error {
constructor(database) {
const message = 'There are no adapters supporting one of the database you provided';
super(message);
this.database = database;
this.name = 'NoDatabaseAdapterError';
}
}
export class NoResourceAdapterError extends Error {
constructor(resource) {
const message = 'There are no adapters supporting one of the resource you provided';
super(message);
this.resource = resource;
this.name = 'NoResourceAdapterError';
}
}
export class ResourcesFactory {
constructor(admin, adapters = []) {
this.adapters = adapters;
this.admin = admin;
}
buildResources({
databases,
resources
}) {
const optionsResources = this._convertResources(resources);
// fetch only those resources from database which weren't previously given as a resource
const databaseResources = this._convertDatabases(databases).filter(dr => !optionsResources.find(optionResource => optionResource.resource.id() === dr.id()));
return this._decorateResources([...databaseResources, ...optionsResources]);
}
/**
* Changes database give by the user in configuration to list of supported resources
* @param {Array<any>} databases list of all databases given by the user in
* {@link AdminJSOptions}
* @return {Array<BaseResource>} list of all resources from given databases
*/
_convertDatabases(databases) {
return databases.reduce((memoArray, db) => {
const databaseAdapter = this.adapters.find(adapter => adapter.Database.isAdapterFor(db));
if (!databaseAdapter) {
throw new NoDatabaseAdapterError(db);
}
return memoArray.concat(new databaseAdapter.Database(db).resources());
}, []);
}
/**
* Maps resources given by user to resources supported by AdminJS.
*
* @param {any[]} resources array of all resources given by the user
* in {@link AdminJSOptions}
* @param {any} resources[].resource optionally user can give resource along
* with options
* @param {Object} resources[].options options given along with the resource
* @return {Object[]} list of Objects with resource and options
* keys
*
* @example
* AdminJS._convertResources([rawAdminModel, {resource: rawUserMode, options: {}}])
* // => returns: [AdminModel, {resource: UserModel, options: {}}]
* // where AdminModel and UserModel were converted by appropriate database adapters.
*/
_convertResources(resources) {
return resources.map(rawResource => {
// resource can be given either by a value or within an object within resource key
const resourceObject = rawResource.resource || rawResource;
const resourceAdapter = this.adapters.find(adapter => adapter.Resource.isAdapterFor(resourceObject));
if (!resourceAdapter && !(resourceObject instanceof BaseResource)) {
throw new NoResourceAdapterError(resourceObject);
}
return {
resource: resourceAdapter ? new resourceAdapter.Resource(resourceObject) : resourceObject,
options: rawResource.options,
features: rawResource.features
};
});
}
/**
* Assigns decorator to each resource and initializes it with `options` and current `admin`
* instance
* @param {Array<Object | BaseResource>} resources array of all mapped resources given by the
* user in {@link AdminJSOptions} along with
* options
* @param {BaseResource} resources[].resource optionally user can give resource along
* with options
* @param {Object} [resources[].options] options for given resource
* @return {BaseResource[]} list of resources with decorator assigned
*/
_decorateResources(resources) {
return resources.map(resourceObject => {
const resource = resourceObject.resource || resourceObject;
const {
features = [],
options = {}
} = resourceObject;
const optionsFromFeatures = features.reduce((opts, feature) => feature(this.admin, opts), {});
resource.assignDecorator(this.admin, mergeResourceOptions(optionsFromFeatures, options));
return resource;
});
}
}
export default ResourcesFactory;