Support from Acquia helps fund testing for Drupal Acquia logo

Comments

andypost created an issue. See original summary.

Wim Leers’s picture

Wim Leers’s picture

Category: Bug report » Task
Wim Leers’s picture

Issue tags: +Novice, +php-novice

I think this is actually a good novice issue.

andypost’s picture

Status: Active » Needs review
FileSize
1.69 KB

Suppose something like that

Wim Leers’s picture

Status: Needs review » Needs work
+++ b/core/tests/Drupal/Tests/Core/Cache/Context/HeadersCacheContextTest.php
@@ -0,0 +1,49 @@
+    $this->assertSame($cache_context->getContext($header_name), $context);
...
+      [NULL, NULL, NULL],
+      ['x-custom-header', NULL, NULL],
+      ['x-custom-header', '123', '123'],
+      ['x-custom-header', ['123'], '123'],

This is only testing the case of looking at one particular header, rather than many. It's also not testing the case of headers.foo, where the foo header does not exist.

Overall, I'd like to see this match the thinking in \Drupal\Tests\Core\Cache\Context\QueryArgsCacheContextTest more closely.

andypost’s picture

@Wim so that means we need another test (functional) cos that's what the class does

Wim Leers’s picture

No, that can happen in a unit test just fine.

andypost’s picture

Category: Task » Bug report
Status: Needs work » Needs review
FileSize
4 KB
2.42 KB
3.71 KB

That's really a bug because headers->all() returns array but context should be a string
Also I'm sure we need to ksort() headers to minimize cache variations

The last submitted patch, 9: 2738563-9-testonly.patch, failed testing.

Status: Needs review » Needs work

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

andypost’s picture

Removed debug code (

The last submitted patch, 12: 2738563-11-testonly.patch, failed testing.

Wim Leers’s picture

Status: Needs review » Needs work
Issue tags: -Novice, -php-novice

Much, much better! :)

Just a few nits and then this is ready:

  1. +++ b/core/lib/Drupal/Core/Cache/Context/HeadersCacheContext.php
    @@ -25,11 +25,29 @@ public static function getLabel() {
    +      $result = NULL;
    +      foreach ($headers as $name => $value) {
    +        if ($result) {
    +          $result .= '&';
    +        }
    +        else {
    +          $result = '';
    +        }
    

    I'd change this to:

    $result = '';
    foreach (…) {
      if ($result) {
        $result .= '&';
      }
    
  2. +++ b/core/lib/Drupal/Core/Cache/Context/HeadersCacheContext.php
    @@ -25,11 +25,29 @@ public static function getLabel() {
    +      }
    +      return '?valueless?';
    

    Let's use an else here, just like in QueryArgsCacheContext.

andypost’s picture

Status: Needs work » Needs review

I disagree

1) context should return NULL if no headers, not empty string
2) no reason to wrap in unconditional else here

I'd better clean-up QueryArgsCacheContext cos this unconditional else {return ..} just makes harder to read code

Wim Leers’s picture

Status: Needs review » Needs work

1) context should return NULL if no headers, not empty string

It must be a string per the interface:

  /**
   * Returns the string representation of the cache context.
   *
   * A cache context service's name is used as a token (placeholder) cache key,
   * and is then replaced with the string returned by this method.
   *
   * @return string
   *   The string representation of the cache context.
   */
  public function getContext();

Remember, it's going to be used to create a cache ID, which is by definition a string.

andypost’s picture

I'll take another round today
We using this cache variation for rest endpoint to vary custom key (key in set vs wrong key
@Wim looks variation should be configurable, any other idea of usage?

andypost’s picture

I mean there's 2 ways

1 presence of key
2 variation by key value

Is this different context like headers.x-custom
When value is headed:x-custom

andypost’s picture

Status: Needs work » Needs review
FileSize
2.39 KB
3.47 KB

I mixed @return and @param so now fixed
Looks QueryArgsCacheContext and test should be updated accordingly

EDIT filed #2744421: QueryArgsCacheContext::getContext() may return NULL, violates the interface

andypost’s picture

+++ b/core/lib/Drupal/Core/Cache/Context/HeadersCacheContext.php
@@ -25,11 +25,26 @@ public static function getLabel() {
+      ksort($headers);
...
+      foreach ($headers as $name => $value) {
...
+        $result .= $name . '=' . implode(',', $value);

Probably it makes sense to sort values within same name to minimize cache variations

Wim Leers’s picture

Status: Needs review » Needs work

Looking great! Just one small thing:

+++ b/core/lib/Drupal/Core/Cache/Context/HeadersCacheContext.php
@@ -25,11 +25,26 @@ public static function getLabel() {
+    elseif ($this->requestStack->getCurrentRequest()->headers->has($header)) {
+      $value = $this->requestStack->getCurrentRequest()->headers->get($header);
+      if ($value !== '') {
+        return $value;
+      }
+      return '?valueless?';
     }
+    return '';

I think this is a bit confusing.

I think this would be clearer:

else {
  if (has header) {
    …
  }
  else {
    return '';
  }
}

Because that leaves you with an overall structure of:

if ($header === NULL) {
  …
}
else {
  …
}

Which clearly reflects there are two possible high-level cases: either you list all headers, or only the specific header that you care about.

That's less clear in the current code.


#20: Probably it makes sense to sort values within same name to minimize cache variations That'd be even better :) Please add test coverage for that then.

Thanks for helping to make this much better! :)

Wim Leers’s picture

This is what I'm asking for in #21:

  public function getContext($header = NULL) {
    if ($header === NULL) {
      $headers = $this->requestStack->getCurrentRequest()->headers->all();
      // Order headers by name to have less cache variations.
      ksort($headers);
      $result = '';
      foreach ($headers as $name => $value) {
        if ($result) {
          $result .= '&';
        }
        $result .= $name . '=' . implode(',', $value);
      }
      return $result;
    }
    else {
      if ($this->requestStack->getCurrentRequest()->headers->has($header)) {
        $value = $this->requestStack->getCurrentRequest()->headers->get($header);
        if ($value !== '') {
          return $value;
        }
        return '?valueless?';
      }
      else {
        return '';
      }
    }
  }
Wim Leers’s picture

Discussed with @andypost in IRC. He doesn't think this is clearer. I know it's a personal preference, so never mind that part. That just leaves the #20 remark to be addressed, then this is RTBC.

andypost’s picture

andypost’s picture

@Wim please check the patch, it looks still applicable

andypost’s picture

Version: 8.1.x-dev » 8.2.x-dev
Wim Leers’s picture

Version: 8.2.x-dev » 8.3.x-dev
Status: Needs review » Needs work

Sorry, this completely fell off my radar!

+++ b/core/tests/Drupal/Tests/Core/Cache/Context/HeadersCacheContextTest.php
@@ -0,0 +1,53 @@
+      // Values are sorted to minimize cache variations.
+      [['z' => ['1', '0'], 'a' => []], NULL, 'a=&z=0,1'],

I think you should provide this by adding another line that has 'z' => ['0', '1'], and show that it results in the same 'a=&z=0,1'. That'd make it conclusive :)

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

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

andypost’s picture

Status: Needs work » Needs review
FileSize
644 bytes
3.67 KB

Here it goes!

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.

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community

Perfect!

andypost’s picture

Bugfixes could go to betas

  • catch committed aefb4a2 on 8.5.x
    Issue #2738563 by andypost, Wim Leers: HeadersCacheContext needs test...

catch credited catch.

catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 8.5.x and cherry-picked to 8.4.x. Thanks!

  • catch committed fdd0f7a on 8.4.x
    Issue #2738563 by andypost, Wim Leers: HeadersCacheContext needs test...
Wim Leers’s picture

Yay, a more reliable & more thoroughly tested cache system!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

jp.stacey’s picture

This is great to see this go in for 8.4+, so thanks for all your work: but can I confirm for the sake of 8.3 users that this is actually both increasing test coverage, *and* changing running code?

I've had people ask about headers cache context in 8.3 and wanted to be able to give them a coherent answer. The patch in #29 does seem to change the behaviour of HeadersCacheContext.php , not just increase test coverage.

If so it might be useful to change the title of this issue, so that it more clearly reflects what was done.

andypost’s picture

@jp.stacey as 8.3 user I'm using the patch because without it the cache context is broken

jp.stacey’s picture

Title: HeadersCacheContext needs test coverage » Headers cache context does not work, also needs test coverage

Thanks, @andypost, and thanks again for your work on this issue. I've changed the title now so it's clearer to people looking for this problem.