Problem/Motivation

We validate the entities we submit, but well, we don't make this information easily parsable, as its a long blob of a string, and APIs can properly use it.

Proposed resolution

Make the information easily accessible by leveraging the normalizer framework.

Remaining tasks

User interface changes

API changes

Data model changes

Comments

dawehner created an issue. See original summary.

dawehner’s picture

StatusFileSize
new3.67 KB
wim leers’s picture

Title: Expose entity validation errors in a machine readable REST api » Expose entity validation errors in a machine readable REST API:
Component: rest.module » serialization.module
Issue tags: +DX (Developer Experience), +API-First Initiative, +Needs tests

What a great start! Thanks, @dawehner!

--- /dev/null
+++ b/core/modules/rest/src/Exception/EntityValidationException.php

@@ -0,0 +1,34 @@
+class EntityValidationExceptionNormalizer extends NormalizerBase {

I'm pretty sure this belongs in the serialization module.

dawehner’s picture

I'm pretty sure this belongs in the serialization module.

Fair point. I struggled a bit with the decision, but then I went with rest module, not really for any particular reason.

One question is: How do we deal with the potential BC break?

wim leers’s picture

One question is: How do we deal with the potential BC break?

I doubt any of the existing clients are parsing the current error response bodies. Which is exactly why you're doing this. Not even https://github.com/acquia/waterwheel.js is doing this.

So… yes this is a BC break technically. But it's also just to make something even remotely usable. The current responses are not usable.

Therefore I think the same BC strategy as we used in #2751325: All serialized values are strings, should be integers/booleans when appropriate seems appropriate.

dawehner’s picture

Status: Active » Needs review
StatusFileSize
new8.7 KB
new8.7 KB

Some progress:

* Register the normalizer
* Move to the serialization module
* Encode the actual errors

Status: Needs review » Needs work

The last submitted patch, 7: 2856110-7.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new8.8 KB
new796 bytes

This should fix a good bunch of failures.

Status: Needs review » Needs work

The last submitted patch, 9: 2856110-9.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new11.69 KB
new4.01 KB

Fixing a couple of more issues.

Status: Needs review » Needs work

The last submitted patch, 11: 2856110-11.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new11.68 KB
new2.13 KB

More fixed ...

Status: Needs review » Needs work

The last submitted patch, 13: 2856110-13.patch, failed testing.

br0ken’s picture

Status: Needs work » Needs review
StatusFileSize
new11.48 KB
new1.18 KB

Let's see whether this helps to get rid of couple errors.

Status: Needs review » Needs work

The last submitted patch, 15: 2856110-15.patch, failed testing.

br0ken’s picture

Issue tags: +DevDaysSeville
StatusFileSize
new11.56 KB
new4.43 KB
br0ken’s picture

Status: Needs work » Needs review
br0ken’s picture

StatusFileSize
new11.56 KB
new864 bytes

Gosh...

The last submitted patch, 17: 2856110-17.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 19: 2856110-18.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new12.79 KB
new2.79 KB

Some test fixes ...

Status: Needs review » Needs work

The last submitted patch, 22: 2856110-22.patch, failed testing.

br0ken’s picture

Status: Needs work » Needs review
StatusFileSize
new12.75 KB
new1.34 KB

Status: Needs review » Needs work

The last submitted patch, 24: 2856110-24.patch, failed testing.

dawehner’s picture

@BR0kEN
Can you explain your recent change? I think we cannot just drop the instanceof checking?

br0ken’s picture

@dawehner, I was wrongly thought that supportsNormalization method called somewhere. But now I'm seeing it's not.

By the way, how are you running this locally? In case when I don't want to run whole rest group. Single test (e.g. core/modules/rest/tests/src/Functional/EntityResource/Node/NodeJsonAnonTest.php) fails every time with something like:

No route found that matches &quot;Content-Type: &quot; in <em class="placeholder">Drupal\Core\Routing\ContentTypeHeaderMatcher-&gt;filter()</em> (line <em class="placeholder">49</em> of <em class="placeholder">core/lib/Drupal/Core/Routing/ContentTypeHeaderMatcher.php</em>). <pre class="backtrace">Drupal\Core\Routing\LazyRouteFilter-&gt;filter(Object, Object) (Line: 337)
br0ken’s picture

Status: Needs work » Needs review
StatusFileSize
new12.84 KB
new686 bytes

Status: Needs review » Needs work

The last submitted patch, 28: 2856110-28.patch, failed testing.

br0ken’s picture

Hm, but nevertheless we shouldn't call supportsNormalization() since this happens in \Symfony\Component\Serializer\Serializer::getNormalizer().

br0ken’s picture

Status: Needs work » Needs review
StatusFileSize
new12.68 KB
new2.51 KB

Status: Needs review » Needs work

The last submitted patch, 31: 2856110-31.patch, failed testing.

br0ken’s picture

Status: Needs work » Needs review
StatusFileSize
new12.74 KB
new1.38 KB

Status: Needs review » Needs work

The last submitted patch, 33: 2856110-33.patch, failed testing.

br0ken’s picture

Status: Needs work » Needs review
StatusFileSize
new13.35 KB
new476 bytes

The last submitted patch, 35: 2856110-35.patch, failed testing.

br0ken’s picture

StatusFileSize
new15.56 KB
new2.79 KB

Status: Needs review » Needs work

The last submitted patch, 37: 2856110-37.patch, failed testing.

br0ken’s picture

Status: Needs work » Needs review
StatusFileSize
new16.7 KB
new2.42 KB

Okay, this will solve couple errors.

Status: Needs review » Needs work

The last submitted patch, 39: 2856110-39.patch, failed testing.

damiankloip’s picture

A few comments:

  1. +++ b/core/modules/serialization/src/Normalizer/EntityValidationExceptionNormalizer.php
    @@ -0,0 +1,38 @@
    +      $result['errors'] = array_reduce(iterator_to_array($object->getViolations()), function (array $carry, ConstraintViolation $violation) {
    

    I don't think we can do this at all, when we support \Exception, so this normalizer would catch anything. I am also not 100% convinced about having a normalizer for this exception handling. This should only support EntityValidationExceptions I think, in any case. Which means possibly an exception normalizer for regular exceptions too. That wouild also require things to be handled a bit differently in \Drupal\serialization\EventSubscriber\DefaultExceptionSubscriber::on4xx.

  2. +++ b/core/modules/serialization/src/Normalizer/NormalizerBase.php
    @@ -21,39 +21,57 @@
    -    return (bool) array_filter($supported, function($name) use ($data) {
    -      return $data instanceof $name;
    -    });
    

    Does having the single code in checkSupport() behave the same as the instanceof check we have now? I'm not sure..

  3. +++ b/core/modules/serialization/src/Normalizer/NormalizerBase.php
    @@ -21,39 +21,57 @@
    +   * @param string|object $object
    +   *   Fully-qualified object name or object itself.
    ...
    +  protected function checkSupport($object, $format = NULL) {
    

    Maybe this parameter should be named $type and have the casting from object still?

  4. +++ b/core/modules/serialization/src/Normalizer/NormalizerBase.php
    @@ -21,39 +21,57 @@
    +    if (is_object($object)) {
    +      $object = get_class($object);
    +    }
    +
    +    // Handle immediate match.
    +    if (in_array($object, $supported)) {
    +      return TRUE;
    +    }
    +
    +    // Handle inheritance.
    +    foreach ($supported as $type) {
    +      if ((class_exists($type) || interface_exists($type)) && is_subclass_of($object, $type, TRUE)) {
    +        return TRUE;
    +      }
    +    }
    ...
    -    return in_array($type, $supported) || array_filter($supported, $subclass_check);
    

    Refactoring this piece of the logic seems possibly out of scope here?

  5. +++ b/core/modules/serialization/tests/src/Unit/EventSubscriber/DefaultExceptionSubscriberTest.php
    @@ -28,7 +29,7 @@ public function testOn4xx() {
    +    $subscriber = new DefaultExceptionSubscriber(new Serializer([new EntityValidationExceptionNormalizer()], [new JsonEncoder()]), []);
    

    Why is this change needed for this test?

br0ken’s picture

@damiankloip

  1. +++ b/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php
    @@ -62,13 +62,12 @@ protected static function getPriority() {
    +    $encoded_content = $this->serializer->serialize($exception, $format);
    

    Now, instead of an array, serializes exception object.

  2. instanceof is not good approach, because for instance \RuntimeException is not an instance of \Exception. That's why these changes were made.
  3. I didn't understand what you meant.
  4. Otherwise this logic will be duplicated to supportsNormalization method
  5. It follows from your first question. Now whole exception object going to be serialized and it fails if no normalizer will be found.
br0ken’s picture

@damiankloip, visual answer on your 5 question available here: https://www.drupal.org/pift-ci-job/630951. The Serialization.Serialization section.

dawehner’s picture

Title: Expose entity validation errors in a machine readable REST API: » [PP-1] Expose entity validation errors in a machine readable REST API:
Status: Needs work » Postponed
wim leers’s picture

Related issues:

Quoting #1916302-16: RFC 7807: "Problem Details for HTTP APIs" — serve REST error responses as application/problem+json:

Also, I wonder if #2856110: [PP-1] Expose entity validation errors in a machine readable REST API: is basically a duplicate of this? It focuses on entity validation errors. But if we're going to do that, we should just adopt this RFC right away, rather than first creating our own format, and then causing disruption for REST clients by moving from HEAD's validation error responses (which are basically unparseable plain text) to the custom JSON format there and then finally to the format defined by RFC 7807.

Please share your thoughts here or on that issue. I think this issue still makes a ton of sense, but I think it'd be good to adopt RFC 7807 for this, instead of inventing our own approach.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

smustgrave’s picture

Status: Postponed » Needs work

Seems postponed issue has since been closed.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.