A REST export view should be configurable to declare which formats should be allowed. Example: the view should allow XML and JSON-LD, but it should forbid HTML, JSON and Drupal JSON-LD.

The new routing system can handle that config for us: routes can be restricted to only support certain formats given in the HTTP Accept headers. So we need to move the registration of routes from views_menu_alter() to a proper route subscriber in Views module. I think we should open an issue to convert Views to the new Routing system first.

You can find an example of restricting routes to specific formats in #1850734: Make serialization formats configurable.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

dawehner’s picture

The new routing system can handle that config for us: routes can be restricted to only support certain formats given in the HTTP Accept headers. So we need to move the registration of routes from views_menu_alter() to a proper route subscriber in Views module. I think we should open an issue to convert Views to the new Routing system first.

We should certainly wait until the the CMF routing is in, though didn't it had the problem that you needed yml files which are kind of static?

klausi’s picture

Status: Postponed » Active

Of course you can also add dynamic routes, REST module does that already: http://drupalcode.org/project/drupal.git/blob/refs/heads/8.x:/core/modul...

Would be a pretty bad routing system for drupal if you could not add run-time/config dependent routes ...

dawehner’s picture

Thank you for that information!

xjm’s picture

Issue tags: +VDC
damiankloip’s picture

FileSize
3.63 KB

Just tracking some work here, This patch just adds options for the style plugin pretty much.

klausi’s picture

Status: Active » Needs work
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/style/Serializer.php
@@ -46,13 +46,47 @@ class Serializer extends StylePluginBase {
+    $form['formats'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Accepted request formats'),
+      '#description' => t('Request formats that will be allowed in responses. If none are selected all formats will be allowed.'),
+      '#options' => array_map('check_plain', $this->formats),
+      '#default_value' => $this->options['formats'],

#options are automatically protected against XSS, see http://api.drupal.org/api/drupal/core!includes!form.inc/function/theme_f...

damiankloip’s picture

Good point, I will bear that in mind when I work on this next. Lucky it's just a wip ;)

klausi’s picture

Thanks, I was just curious about the patch and thought I can leave feedback while I'm reading over it anyway :-)

dawehner’s picture

+++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/style/Serializer.phpundefined
@@ -60,6 +94,13 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o
+    $formats = array_filter($this->options['formats']);

Also just providing feedback :) We could filter that on a validateOptionsForm method.

damiankloip’s picture

Status: Needs work » Needs review
FileSize
2.5 KB
4.04 KB

Rerolled to include the feedback so far. I think this should just wait on #1800998: Use route system instead of hook_menu() in Views for the time being.

damiankloip’s picture

ok, now that #1800998: Use route system instead of hook_menu() in Views is in we can use routes and finish this issue. A 406 response will be returned by the routing system for invalid formats.

klausi’s picture

Status: Needs review » Needs work
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php
@@ -200,7 +195,33 @@ public function optionsSummary(&$categories, &$options) {
 
+    // Only add requirements on formats if some have been configured, otherwise
+    // Use all available formats.

"use" should be lower case

+++ b/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.php
@@ -65,7 +65,7 @@ protected function setUp() {
   /**
    * Checks the behavior of the Serializer callback paths and row plugins.
    */
-  public function testSerializerResponses() {
+  public function _testSerializerResponses() {

So you disabled this test method? Leftover from development? ;-)

+++ b/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.php
@@ -125,17 +125,46 @@ public function testSerializerResponses() {
+    // Should not return a 406.
+    $this->drupalGet('test/serialize/field', array(), array('Accept: application/json'));
+    $this->assertResponse(406, 'A 406 response was returned when JSON was requested.');

Comment does not match implementation? 406 should be indeed returned?

+++ b/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.php
@@ -125,17 +125,46 @@ public function testSerializerResponses() {
+    // Should not return a 406.
+    $this->drupalGet('test/serialize/field', array(), array('Accept: application/html'));
+    $this->assertResponse(406, 'A 406 response was returned when HTML was requested.');
   }

Same here

Anonymous’s picture

I tested this manually. After enabling both formats, I could access both. Turning one off resulted in a 406. Turning unchecking all formats made it possible to access all of them again.

This matches the behavior we talked about, so once the code review is done I think this can be RTBCed.

damiankloip’s picture

Status: Needs work » Needs review
FileSize
3.36 KB
7.82 KB

Thanks for the reviews/testing. Much appreciated. Have made those changes.

klausi’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
866 bytes
7.82 KB

Looks RTBC to me, I only fixed a typo in the comment.

dawehner’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.phpundefined
@@ -59,7 +60,7 @@ class RestExport extends PathPluginBase {
+  protected $contentType = NULL;

nothing is also NULL :)

+++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.phpundefined
@@ -200,7 +195,33 @@ public function optionsSummary(&$categories, &$options) {
+      $formats = $style_plugin->getFormats();

It feels wrong that getFormats() doesn't return the formats

+++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.phpundefined
@@ -200,7 +195,33 @@ public function optionsSummary(&$categories, &$options) {
+    // Format as a string using pipes as a delimeter.
+    $requirements['_format'] = implode('|', $formats);

Should we check for formats?

+++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/style/Serializer.phpundefined
@@ -46,13 +46,56 @@ class Serializer extends StylePluginBase {
+    $container = \Drupal::getContainer();
...
+    $this->serializer = $container->get('serializer');
+    $this->formats = $container->getParameter('serializer.formats');

You know we can inject stuff now?

+++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/style/Serializer.phpundefined
@@ -46,13 +46,56 @@ class Serializer extends StylePluginBase {
+   * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::defineOptions().
...
+   * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::buildOptionsForm().
...
+   * Overrides \Drupal\views\Plugin\views\PluginBase::submitOptionsForm().

@inheritdoc all the things.

+++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/style/Serializer.phpundefined
@@ -46,13 +46,56 @@ class Serializer extends StylePluginBase {
+      '#options' => drupal_map_assoc($this->formats),

Please let's us open a follow up to convert that function.

+++ b/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.phpundefined
@@ -125,11 +125,40 @@ public function testSerializerResponses() {
+  public function testReponseFormatConfiguration() {
+    $this->drupalLogin($this->adminUser);
+
+    $style_options = 'admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/style_options';
+
+    // Select only 'xml' as an accepted format.
+    $this->drupalPost($style_options, array('style_options[formats][xml]' => 'xml'), t('Apply'));
+    $this->drupalPost(NULL, array(), t('Save'));
+
+    // Should return a 406.
+    $this->drupalGet('test/serialize/field', array(), array('Accept: application/json'));

We agreed to split the tests into UI and the rest

damiankloip’s picture

Done everything except splitting up the tests. Just running out of time.

damiankloip’s picture

Status: Needs work » Needs review

I think we should have a follow up now for the decoupling of tests. Seems slightly too far for this issues scope. Created #2004670: Split Views style serializer tests in to UI and functional tests

klausi’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
1.45 KB
10.94 KB
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/style/Serializer.php
@@ -46,13 +48,68 @@ class Serializer extends StylePluginBase {
+  /**
+   * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::defineOptions().

@inheritdoc here too, so I fixed that as well.

Everything else looks good, so RTBC again?

damiankloip’s picture

damiankloip’s picture

FileSize
2.47 KB
11.28 KB

Me and Alex had a chat on IRC about this. As we cannot properly do content negotiation yet, we need to keep the 'fallback' format selection for html as we had before. When we improve the content negotiation, we can fix this.

I have added some @todo's in the code, as well as some additional test assertions for browser headers.

damiankloip’s picture

Status: Reviewed & tested by the community » Needs review

Status: Needs review » Needs work

The last submitted patch, 1891202-21.patch, failed testing.

dawehner’s picture

+++ b/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.phpundefined
@@ -151,6 +151,15 @@ public function testReponseFormatConfiguration() {
+    // Should return a 200. Emulates a sample firefow header.

What is firefow ;)

damiankloip’s picture

Status: Needs work » Needs review
FileSize
932 bytes
11.28 KB

You are seriously behind the times if you aren't using the firefow browser yet!

dawehner’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
2.17 KB

Manual testing worked fine.

Just in case someone is interested, here is a try to write a proper unit test ... still not possible.

Status: Reviewed & tested by the community » Needs work
Issue tags: -VDC

The last submitted patch, 1891202-25.patch, failed testing.

damiankloip’s picture

Status: Needs work » Needs review
Issue tags: +VDC

#25: 1891202-25.patch queued for re-testing.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

ReRTBC

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/rest/lib/Drupal/rest/Tests/Views/StyleSerializerTest.phpundefined
@@ -125,11 +125,49 @@ public function testSerializerResponses() {
+    // Should return a 200.
+    // @todo This should be fixed when we have better content negotiation.
+    $this->drupalGet('test/serialize/field', array(), array('Accept: */*'));
+    $this->assertResponse(200, 'A 200 response was returned when HTML was requested.');
+
+    // Should return a 200. Emulates a sample Firefox header.
+    $this->drupalGet('test/serialize/field', array(), array('Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'));
+    $this->assertResponse(200, 'A 200 response was returned when HTML was requested.');

The assertion messages here are incorrect. We're not requesting HTML - we're requesting something and saying we'll take anything :)

damiankloip’s picture

Assigned: Unassigned » alexpott
Status: Needs work » Needs review
FileSize
1.27 KB
11.31 KB

OK, how about this? As it's just a comment change, assigning to you Alex.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Seems fine for me.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 1c61a22 and pushed to 8.x. Thanks!

alexpott’s picture

Assigned: alexpott » Unassigned
alexpott’s picture

Unassigning...

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

Anonymous’s picture

Issue summary: View changes

not postponed anymore