You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					308 lines
				
				11 KiB
			
		
		
			
		
	
	
					308 lines
				
				11 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								"use strict";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Object.defineProperty(exports, "__esModule", {
							 | 
						||
| 
								 | 
							
								  value: true
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								exports.default = void 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _populator = _interopRequireDefault(require("../utils/populator/populator"));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _viewHelpers = _interopRequireDefault(require("../utils/view-helpers/view-helpers"));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _configurationError = _interopRequireDefault(require("../utils/errors/configuration-error"));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _notFoundError = _interopRequireDefault(require("../utils/errors/not-found-error"));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _forbiddenError = _interopRequireDefault(require("../utils/errors/forbidden-error"));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _requestParser = require("../utils/request-parser");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _actionErrorHandler = _interopRequireDefault(require("../services/action-error-handler/action-error-handler"));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _validateParam = require("../../utils/param-converter/validate-param");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* eslint-disable max-len */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* eslint no-unused-vars: 0 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Controller responsible for the auto-generated API: `/admin_root/api/...`, where
							 | 
						||
| 
								 | 
							
								 * _admin_root_ is the `rootPath` given in {@link AdminJSOptions}.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * The best way to utilise it is to use {@link ApiClient} on the frontend.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * ### Available API endpoints
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * <div class='table-container'>
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * | Endpoint                 | Method                | Description |
							 | 
						||
| 
								 | 
							
								 * |--------------------------|-----------------------|-------------|
							 | 
						||
| 
								 | 
							
								 * | .../api/resources/{resourceId}/actions/{action} | {@link ApiController#resourceAction} | Perform customized resource action |
							 | 
						||
| 
								 | 
							
								 * | .../api/resources/{resourceId}/records/{recordId}/{action} | {@link ApiController#recordAction} | Perform customized record action |
							 | 
						||
| 
								 | 
							
								 * | .../api/resources/{resourceId}/bulk/{action}?recordIds={recordIds} | {@link ApiController#bulkAction} | Perform customized bulk action |
							 | 
						||
| 
								 | 
							
								 * | .../api/pages/{pageName}_ | {@link ApiController#page} | Perform customized page action |
							 | 
						||
| 
								 | 
							
								 * | .../api/dashboard_ | {@link ApiController#dashboard} | Perform customized dashboard action |
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * </div>
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * ### Responsibility
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * In general this controllers takes handler functions you define in {@link AdminJSOptions} and:
							 | 
						||
| 
								 | 
							
								 * - find all the [context information]{@link ActionContext} which is needed by the action
							 | 
						||
| 
								 | 
							
								 *   and is passed to the {@link Action#handler}, {@link Action#before} and {@link Action#after}
							 | 
						||
| 
								 | 
							
								 * - checks if action can be invoked by particular user {@link Action#isAccessible}
							 | 
						||
| 
								 | 
							
								 * - invokes {@link Action#before} and {@link Action#after} hooks
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * You probably don't want to modify it, but you can call its methods by using {@link ApiClient}
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @hideconstructor
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class ApiController {
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * @param {Object} options
							 | 
						||
| 
								 | 
							
								   * @param {AdminJSOptions} options.admin
							 | 
						||
| 
								 | 
							
								   * @param {CurrentAdmin} [currentAdmin]
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  constructor({
							 | 
						||
| 
								 | 
							
								    admin
							 | 
						||
| 
								 | 
							
								  }, currentAdmin) {
							 | 
						||
| 
								 | 
							
								    this._admin = admin;
							 | 
						||
| 
								 | 
							
								    this.currentAdmin = currentAdmin;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Returns context for given action
							 | 
						||
| 
								 | 
							
								   * @private
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param   {ActionRequest}  request  request object
							 | 
						||
| 
								 | 
							
								   * @return  {Promise<ActionContext>} action context
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async getActionContext(request) {
							 | 
						||
| 
								 | 
							
								    const {
							 | 
						||
| 
								 | 
							
								      resourceId,
							 | 
						||
| 
								 | 
							
								      action: actionName
							 | 
						||
| 
								 | 
							
								    } = request.params;
							 | 
						||
| 
								 | 
							
								    const h = new _viewHelpers.default(this._admin);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const resource = this._admin.findResource(resourceId);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const action = resource.decorate().actions[actionName];
							 | 
						||
| 
								 | 
							
								    return {
							 | 
						||
| 
								 | 
							
								      resource,
							 | 
						||
| 
								 | 
							
								      action,
							 | 
						||
| 
								 | 
							
								      h,
							 | 
						||
| 
								 | 
							
								      currentAdmin: this.currentAdmin,
							 | 
						||
| 
								 | 
							
								      _admin: this._admin,
							 | 
						||
| 
								 | 
							
								      ...this._admin.translateFunctions
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Search records by query string.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * Handler function responsible for a _.../api/resources/{resourceId}/search/{query}_ route
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param   {ActionRequest}  request with __params.query__ set
							 | 
						||
| 
								 | 
							
								   * @param   {any}            response
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return  {Promise<SearchActionResponse>}    found records
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async search(request, response) {
							 | 
						||
| 
								 | 
							
								    request.params.action = 'search'; // eslint-disable-next-line no-console
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    console.log(['Using ApiController#search is deprecated in favour of resourceAction', 'It will be removed in the next version'].join('\n'));
							 | 
						||
| 
								 | 
							
								    return this.resourceAction(request, response);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Performs a customized {@link Action resource action}.
							 | 
						||
| 
								 | 
							
								   * To call it use {@link ApiClient#resourceAction} method.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * Handler function responsible for a _.../api/resources/{resourceId}/actions/{action}_
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param   {ActionRequest}  originalRequest
							 | 
						||
| 
								 | 
							
								   * @param   {any}            response object from the plugin (i.e. adminjs-expressjs)
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return  {Promise<ActionResponse>}  action response
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async resourceAction(originalRequest, response) {
							 | 
						||
| 
								 | 
							
								    const actionContext = await this.getActionContext(originalRequest);
							 | 
						||
| 
								 | 
							
								    const request = (0, _requestParser.requestParser)(originalRequest, actionContext.resource);
							 | 
						||
| 
								 | 
							
								    return actionContext.action.handler(request, response, actionContext);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Performs a customized {@link Action record action}.
							 | 
						||
| 
								 | 
							
								   * To call it use {@link ApiClient#recordAction} method.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * Handler function responsible for a _.../api/resources/{resourceId}/records/{recordId}/{action}_
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param   {ActionRequest}  originalRequest
							 | 
						||
| 
								 | 
							
								   * @param   {any}  response
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return  {Promise<RecordActionResponse>}  action response
							 | 
						||
| 
								 | 
							
								   * @throws  ConfigurationError      When given record action doesn't return {@link RecordJSON}
							 | 
						||
| 
								 | 
							
								   * @throws  ConfigurationError      when action handler doesn't return Promise<{@link RecordActionResponse}>
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async recordAction(originalRequest, response) {
							 | 
						||
| 
								 | 
							
								    const {
							 | 
						||
| 
								 | 
							
								      recordId,
							 | 
						||
| 
								 | 
							
								      resourceId
							 | 
						||
| 
								 | 
							
								    } = originalRequest.params;
							 | 
						||
| 
								 | 
							
								    const actionContext = await this.getActionContext(originalRequest);
							 | 
						||
| 
								 | 
							
								    const request = (0, _requestParser.requestParser)(originalRequest, actionContext.resource);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!recordId) {
							 | 
						||
| 
								 | 
							
								      throw new _notFoundError.default(['You have to pass recordId to the recordAction'].join('\n'), 'Action#handler');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const idProperty = Object.values(actionContext.resource.decorate().properties ?? {}).find(p => p.isId());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!idProperty || !(0, _validateParam.validateParam)(recordId, idProperty)) {
							 | 
						||
| 
								 | 
							
								      const invalidRecordError = (0, _actionErrorHandler.default)(new _forbiddenError.default(['You have to pass a valid recordId to the recordAction'].join('\n')), actionContext);
							 | 
						||
| 
								 | 
							
								      return invalidRecordError;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let record = await actionContext.resource.findOne(recordId);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!record) {
							 | 
						||
| 
								 | 
							
								      const missingRecordError = (0, _actionErrorHandler.default)(new _notFoundError.default([`Record with given id: "${recordId}" cannot be found in resource "${resourceId}"`].join('\n'), 'Action#handler'), actionContext);
							 | 
						||
| 
								 | 
							
								      return missingRecordError;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    [record] = await (0, _populator.default)([record]);
							 | 
						||
| 
								 | 
							
								    actionContext.record = record;
							 | 
						||
| 
								 | 
							
								    const jsonWithRecord = await actionContext.action.handler(request, response, actionContext);
							 | 
						||
| 
								 | 
							
								    const isValidRecord = !!(jsonWithRecord && jsonWithRecord.record && jsonWithRecord.record.recordActions);
							 | 
						||
| 
								 | 
							
								    const anErrorWasHandled = jsonWithRecord && jsonWithRecord.notice && jsonWithRecord.notice.type === 'error';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (isValidRecord || anErrorWasHandled) {
							 | 
						||
| 
								 | 
							
								      return jsonWithRecord;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    throw new _configurationError.default('handler of a recordAction should return a RecordJSON object', 'Action#handler');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Performs a customized {@link Action bulk action}.
							 | 
						||
| 
								 | 
							
								   * To call it use {@link ApiClient#bulkAction} method.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * Handler function responsible for a _.../api/resources/{resourceId}/bulk/{action}?recordIds={recordIds}_
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param   {ActionRequest}  request
							 | 
						||
| 
								 | 
							
								   * @param   {any}  response
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return  {Promise<BulkActionResponse>}  action response
							 | 
						||
| 
								 | 
							
								   * @throws  NotFoundError           when recordIds are missing in query or they don't exists in
							 | 
						||
| 
								 | 
							
								   *                                  the database
							 | 
						||
| 
								 | 
							
								   * @throws  ConfigurationError      when action handler doesn't return Promise<{@link BulkActionResponse}>
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async bulkAction(originalRequest, response) {
							 | 
						||
| 
								 | 
							
								    const {
							 | 
						||
| 
								 | 
							
								      resourceId
							 | 
						||
| 
								 | 
							
								    } = originalRequest.params;
							 | 
						||
| 
								 | 
							
								    const {
							 | 
						||
| 
								 | 
							
								      recordIds
							 | 
						||
| 
								 | 
							
								    } = originalRequest.query || {};
							 | 
						||
| 
								 | 
							
								    const actionContext = await this.getActionContext(originalRequest);
							 | 
						||
| 
								 | 
							
								    const request = (0, _requestParser.requestParser)(originalRequest, actionContext.resource);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!recordIds) {
							 | 
						||
| 
								 | 
							
								      throw new _notFoundError.default(['You have to pass "recordIds" to the bulkAction via search params: ?recordIds=...'].join('\n'), 'Action#handler');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let records = await actionContext.resource.findMany(recordIds.split(','));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!records || !records.length) {
							 | 
						||
| 
								 | 
							
								      throw new _notFoundError.default([`record with given id: "${recordIds}" cannot be found in resource "${resourceId}"`].join('\n'), 'Action#handler');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    records = await (0, _populator.default)(records);
							 | 
						||
| 
								 | 
							
								    const jsonWithRecord = await actionContext.action.handler(request, response, { ...actionContext,
							 | 
						||
| 
								 | 
							
								      records
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (jsonWithRecord && jsonWithRecord.records) {
							 | 
						||
| 
								 | 
							
								      return jsonWithRecord;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    throw new _configurationError.default('handler of a bulkAction should return an Array of RecordJSON object', 'Action#handler');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Gets optional data needed by the dashboard.
							 | 
						||
| 
								 | 
							
								   * To call it use {@link ApiClient#getDashboard} method.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * Handler function responsible for a _.../api/dashboard_
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param   {ActionRequest}  request
							 | 
						||
| 
								 | 
							
								   * @param   {any}  response
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return  {Promise<any>}  action response
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async dashboard(request, response) {
							 | 
						||
| 
								 | 
							
								    const h = new _viewHelpers.default(this._admin);
							 | 
						||
| 
								 | 
							
								    const handler = this._admin.options.dashboard && this._admin.options.dashboard.handler;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (handler) {
							 | 
						||
| 
								 | 
							
								      return handler(request, response, {
							 | 
						||
| 
								 | 
							
								        h,
							 | 
						||
| 
								 | 
							
								        currentAdmin: this.currentAdmin,
							 | 
						||
| 
								 | 
							
								        _admin: this._admin
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return {
							 | 
						||
| 
								 | 
							
								      message: ['You can override this method by setting up dashboard.handler', 'function in AdminJS options'].join('\n')
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Gets optional data needed by the page.
							 | 
						||
| 
								 | 
							
								   * To call it use {@link ApiClient#getPage} method.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * Handler function responsible for a _.../api/pages/{pageName}_
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param   {ActionRequest}  request
							 | 
						||
| 
								 | 
							
								   * @param   {any}  response
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return  {Promise<any>}  action response
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async page(request, response) {
							 | 
						||
| 
								 | 
							
								    const h = new _viewHelpers.default(this._admin);
							 | 
						||
| 
								 | 
							
								    const {
							 | 
						||
| 
								 | 
							
								      pages = {}
							 | 
						||
| 
								 | 
							
								    } = this._admin.options;
							 | 
						||
| 
								 | 
							
								    const {
							 | 
						||
| 
								 | 
							
								      pageName
							 | 
						||
| 
								 | 
							
								    } = request.params;
							 | 
						||
| 
								 | 
							
								    const {
							 | 
						||
| 
								 | 
							
								      handler
							 | 
						||
| 
								 | 
							
								    } = pages[pageName] || {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (handler) {
							 | 
						||
| 
								 | 
							
								      return handler(request, response, {
							 | 
						||
| 
								 | 
							
								        h,
							 | 
						||
| 
								 | 
							
								        currentAdmin: this.currentAdmin,
							 | 
						||
| 
								 | 
							
								        _admin: this._admin
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return {
							 | 
						||
| 
								 | 
							
								      message: ['You can override this method by setting up pages[pageName].handler', 'function in AdminJS options'].join('\n')
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _default = ApiController;
							 | 
						||
| 
								 | 
							
								exports.default = _default;
							 |