Seditio Source
Root |
./othercms/croogo-4.0.7/vendor/friendsofcake/crud-json-api/src/Error/JsonApiExceptionRenderer.php
<?php
namespace CrudJsonApi\Error;

use
Cake\Controller\Controller;
use
Cake\Core\Configure;
use
Cake\Core\Exception\Exception;
use
Cake\Error\Debugger;
use
Cake\Utility\Inflector;
use
Crud\Error\ExceptionRenderer;
use
Crud\Listener\ApiQueryLogListener;
use
Neomerx\JsonApi\Encoder\Encoder;
use
Neomerx\JsonApi\Schema\Error;
use
Neomerx\JsonApi\Schema\ErrorCollection;
use
Zend\Diactoros\Stream;

/**
 * Exception renderer for the JsonApiListener
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 */
class JsonApiExceptionRenderer extends ExceptionRenderer
{

   
/**
     * Method used for all non-validation errors.
     *
     * @param string $template Name of template to use (ignored for jsonapi)
     * @return \Cake\Http\Response
     */
   
protected function _outputMessage($template)
    {
        if (!
$this->controller->request->accepts('application/vnd.api+json')) {
            return
parent::_outputMessage($template);
        }

       
$viewVars = $this->controller->viewVars;
       
$status = (string)$this->controller->response->getStatusCode(); // e.g. 404
       
$title = $this->controller->response->getReasonPhrase(); // e,g. Not Found

        // Only set JSON API `detail` field if `message` viewVar field is not
        // identical to the CakePHP HTTP Status Code description.
       
$detail = null;
        if (!empty(
$viewVars['message']) && (strcasecmp($viewVars['message'], $title) !== 0)) {
           
$detail = $viewVars['message'];
        }

       
$errorCollection = new ErrorCollection();
       
$errorCollection->add(new Error(
           
$idx = null,
           
$aboutLink = null,
           
$typeLinks = null,
           
$status,
           
$code = null,
           
$title,
           
$detail
       
));

       
$encoder = Encoder::instance();
       
$json = $encoder->encodeErrors($errorCollection);

        if (
Configure::read('debug')) {
           
$json = $this->_addDebugNode($json);
           
$json = $this->_addQueryLogsNode($json);
        }

       
# create stream as required by `response->withBody()`
       
$stream = new Stream('php://memory', 'wb+');
       
$stream->write($json);

       
// set up the response
       
$this->controller->response = $this->controller->response
           
->withType('jsonapi')
            ->
withBody($stream);

        return
$this->controller->response;
    }

   
/**
     * Method used for rendering 422 validation used for both CakePHP entity
     * validation errors and JSON API (request data) documents.
     *
     * @param \Crud\Error\Exception\ValidationException $exception Exception
     * @return \Cake\Http\Response
     */
   
public function validation($exception)
    {
        if (!
$this->controller->request->accepts('application/vnd.api+json')) {
            return
parent::validation($exception);
        }

       
$status = $exception->getCode();

        try {
           
$this->controller->response = $this->controller->response->withStatus($status);
        } catch (
Exception $e) {
           
$status = 422;
           
$this->controller->response = $this->controller->response->withStatus($status);
        }

       
$errorCollection = $this->_getNeoMerxErrorCollection($exception->getValidationErrors());

       
$encoder = Encoder::instance();
       
$json = $encoder->encodeErrors($errorCollection);

        if (
Configure::read('debug')) {
           
$json = $this->_addDebugNode($json);
           
$json = $this->_addQueryLogsNode($json);
        }

       
# create stream as required by `response->withBody()`
       
$stream = new Stream('php://memory', 'wb+');
       
$stream->write($json);

       
// set up the response
       
$this->controller->response = $this->controller->response
           
->withType('jsonapi')
            ->
withBody($stream);

        return
$this->controller->response;
    }

   
/**
     * Returns a NeoMerx ErrorCollection with validation errors by either:
     *
     * - returning cloaked collection as passed down from the Listener
     * - creating a new collection from CakePHP validation errors
     *
     * @param array $validationErrors CakePHP validation errors
     * @return \Neomerx\JsonApi\Schema\ErrorCollection
     */
   
protected function _getNeoMerxErrorCollection($validationErrors)
    {
        if (isset(
$validationErrors['CrudJsonApiListener']['NeoMerxErrorCollection']) &&
           
$validationErrors['CrudJsonApiListener']['NeoMerxErrorCollection'] instanceof ErrorCollection) {
            return
$validationErrors['CrudJsonApiListener']['NeoMerxErrorCollection'];
        }

       
// Create new NeoMerx ErrorCollection from CakePHP validation errors
       
$errorCollection = new ErrorCollection();

       
$validationErrors = $this->_standardizeValidationErrors($validationErrors);

        foreach (
$validationErrors as $validationError) {
           
$errorCollection->addDataAttributeError(
               
$name = $validationError['fields'][0],
               
$title = $validationError['name'],
               
$detail = $validationError['message'],
               
$status = null,
               
$idx = null,
               
$aboutLink = null,
               
$code = null,
               
$meta = null
           
);
        }

        return
$errorCollection;
    }

   
/**
     * Adds top-level `debug` node to a json encoded string
     *
     * @param string $json Json encoded string
     * @return string Json encoded string with added debug node
     */
   
protected function _addDebugNode($json)
    {
       
$viewVars = $this->controller->viewVars;

        if (empty(
$viewVars['error'])) {
            return
$json;
        }

       
$debug = [];
       
$debug['class'] = get_class($viewVars['error']);

        if (!isset(
$debug['trace'])) {
           
$debug['trace'] = Debugger::formatTrace($viewVars['error']->getTrace(), [
               
'format' => 'array',
               
'args' => false
           
]);
        }

       
$result = json_decode($json, true);
       
$result['debug'] = $debug;

        try {
            return
json_encode($result, JSON_PRETTY_PRINT);
        } catch (\
Exception $e) {
           
$result['debug']['message'] = $e->getMessage();
           
$result['debug']['trace'] = [
               
'error' => 'Unable to encode stack trace',
            ];

            return
json_encode($result, JSON_PRETTY_PRINT);
        }
    }

   
/**
     * Add top-level `query` node if ApiQueryLogListener is loaded.
     *
     * @param string $json Json encoded string
     * @return string Json encoded string
     */
   
protected function _addQueryLogsNode($json)
    {
       
$listener = $this->_getApiQueryLogListenerObject();
       
$logs = $listener->getQueryLogs();

        if (empty(
$logs)) {
            return
$json;
        }

       
$result = json_decode($json, true);
       
$result['query'] = $logs;

        return
json_encode($result, JSON_PRETTY_PRINT);
    }

   
/**
     * Returns a plain ApiQueryLogListener instance for e.g. unit testing purposes.
     *
     * @return \Crud\Listener\ApiQueryLogListener
     */
   
protected function _getApiQueryLogListenerObject()
    {
        return new
ApiQueryLogListener(new Controller());
    }

   
/**
     * Creates a uniform array with all information required to generate
     * NeoMerx DataAttributeErrors by parsing (differently structured) built-in
     * and user-defined CakePHP rules feedback.
     *
     * Note: we need this function because Cake's built-in rules don't pass
     * through `_processRules()` function in the Validator.
     *
     * @param array $errors CakePHP validation errors
     * @return array Standardized array
     */
   
protected function _standardizeValidationErrors($errors = [])
    {
       
$result = [];

        foreach (
$errors as $field => $validationFeedback) {
           
// must be a user defined rule
           
if (is_int(key($validationFeedback))) {
               
$result[] = $validationFeedback[0];
                continue;
            }

           
// stil here so array key must be a string (and thus a built-in rule)
           
$rule = key($validationFeedback);
           
$message = $validationFeedback[$rule];
           
$field = Inflector::dasherize($field);

           
$result[] = [
               
'fields' => [$field],
               
'name' => $rule,
               
'message' => $message
           
];
        }

        return
$result;
    }
}