hello all,
i have a problem with restws. I explain me better.

I my drupal 7 install, I have created a new content type (named Pippo)
with title, body and a user reference field. Now, when i try to create
a new content of Pippo type with this json:

{"type":"pippo","title":"Pippo creato","status":1}

all works fine. Instead if i use this json:

{"type":"pippo","title":"Pippo creato","status":1,"field_ref_user":{"uri":"http:\/\/drupal7.local\/user\/4","id":"4","resource":"user"}}

restws responds with a 406 code. A json with compounds fields, like
user reference, must be created in some special way?

the user with id 4 exist.

Any help it's really appreciated

Comments

klausi’s picture

Version: 7.x-1.0-beta1 » 7.x-1.x-dev
Category: support » bug

Unfortunately RESTWS is currently not ready to import references to other resources like this. I know this is really ugly because RESTWS does not accept the format that comes from its own output. As a work-around you can experiment with passing in "4" as you would do it for any other field value.

klausi’s picture

Title: to Create a content, fails with a 406 code respond » Accept resource references on input
Issue tags: +GSoC 2012

Tagging, assigning to Sebastian.

sepgil’s picture

Version: 7.x-1.x-dev » 7.x-2.x-dev
Assigned: Unassigned » sepgil
Status: Active » Needs review
StatusFileSize
new2.88 KB

This patch should fix the problem.

klausi’s picture

Status: Needs review » Needs work
+++ b/restws.entity.inc
@@ -143,7 +144,17 @@ class RestWSEntityResourceController implements RestWSResourceControllerInterfac
       foreach ($values as $name => $value) {
-        $wrapper->{$name}->set($value);
+        if (get_class($wrapper->{$name}) == "EntityDrupalWrapper" && is_array($value)) {
+          if (isset($value['resource']) && $value['resource'] == $wrapper->{$name}->type() && isset($value['id'])) {
+            $wrapper->{$name}->set($value['id']);
+          }
+        }
+        else if (get_class($wrapper->{$name}) == "EntityListWrapper") {
+          $this->listFiller($wrapper->{$name}, $value);
+        }
+        else {
+          $wrapper->{$name}->set($value);
+        }

@@ -163,4 +174,52 @@ class RestWSEntityResourceController implements RestWSResourceControllerInterfac
+          $items[] = entity_load_single($item['resource'], $item['id']);
...
+        $this->listFiller($sub_list, $sub_values);

Inline comments missing. Please describe which case is covered in which branch.

+++ b/restws.entity.inc
@@ -143,7 +144,17 @@ class RestWSEntityResourceController implements RestWSResourceControllerInterfac
+        else if (get_class($wrapper->{$name}) == "EntityListWrapper") {

"else if" should be one word, i.e. "elseif"

+++ b/restws.entity.inc
@@ -163,4 +174,52 @@ class RestWSEntityResourceController implements RestWSResourceControllerInterfac
+   * @param array $values Array which contains resource arrays.

Description should eb on a new line. There should be an empty line before @param documentation. Also elsewhere.

entity_load_single() should not be necessary.

And we need new test cases for create/update with the reference array.

sepgil’s picture

StatusFileSize
new2.53 KB

I made a new patch, which uses the same functionality for create and update and therefore simplifies the code.

sepgil’s picture

Status: Needs work » Needs review
sepgil’s picture

StatusFileSize
new9.14 KB

Here a new patch that now works on the formating level, since the results of xml and json can be different. It isn't ready, since the new update test doesn't work correctly. But since I currently can't why, I figured that I could in the meantime post the patch so someone(klausi ? :P) could review it...

Status: Needs review » Needs work

The last submitted patch, 1355220-accept_output_format3.patch, failed testing.

sepgil’s picture

Status: Needs work » Needs review
StatusFileSize
new9.1 KB

At last I've managed to fix the simpletest.

klausi’s picture

Status: Needs review » Needs work
+++ b/restws.formats.inc
@@ -102,13 +102,27 @@ abstract class RestWSBaseFormat implements RestWSFormatInterface {
+    if (is_array($resourceArray) && array_key_exists('id', $resourceArray) && array_key_exists('resource', $resourceArray) && entity_get_info($type) != NULL && $resourceArray['resource'] == $type) {

comment please

+++ b/restws.formats.inc
@@ -102,13 +102,27 @@ abstract class RestWSBaseFormat implements RestWSFormatInterface {
+    } else {

needs to be on a new line

+++ b/restws.formats.inc
@@ -228,18 +289,54 @@ class RestWSFormatXML extends RestWSBaseFormat {
+          $tmp = &$result[];

not a good variable name

+++ b/restws.formats.inc
@@ -228,18 +289,54 @@ class RestWSFormatXML extends RestWSBaseFormat {
+          $tmp = self::xmlToArray($properties, $element, entity_property_list_extract_type($type));

mixed use of static and dynamic method xmlToarray()?

+++ b/restws.formats.inc
@@ -228,18 +289,54 @@ class RestWSFormatXML extends RestWSBaseFormat {
+        $res[$attribute_name] = $attribute_value;

what is $res?

+++ b/restws.test
@@ -99,6 +99,50 @@ class RestWSTestCase extends DrupalWebTestCase {
   /**
+   * Test ResourceArray.

longer description what we do here please

sepgil’s picture

Status: Needs work » Needs review
StatusFileSize
new9.3 KB

Corrected all issues mentioned above.

klausi’s picture

Status: Needs review » Needs work
+++ b/restws.formats.inc
@@ -102,13 +102,27 @@ abstract class RestWSBaseFormat implements RestWSFormatInterface {
   /**
+   * Extracts the id of a resource array.
+   */
+  protected function extractResourceId($resourceArray, $type) {

I think that function should return FALSE if the type is not a reference to an entity. And add @return documentation for that.

+++ b/restws.formats.inc
@@ -102,13 +102,27 @@ abstract class RestWSBaseFormat implements RestWSFormatInterface {
+    if (is_array($resourceArray) && array_key_exists('id', $resourceArray) && array_key_exists('resource', $resourceArray) && entity_get_info($type) != NULL && $resourceArray['resource'] == $type) {

The most important check here is entity_get_info(), so this should come first.

+++ b/restws.formats.inc
@@ -187,8 +201,56 @@ class RestWSFormatJSON extends RestWSBaseFormat {
+  /**
+   * Transforms the input of json to format that entity api can understand.
+   */
+  protected function normalizeValues(array &$values, $properties) {

@param documentation would be helpful here.

+++ b/restws.formats.inc
@@ -187,8 +201,56 @@ class RestWSFormatJSON extends RestWSBaseFormat {
+              $id = $this->extractResourceId($field_values, $type);
+              if (!empty($id)) {
+                $field_values = $id;
+              }

The condition should be $id !== FALSE here, after you updated extractResourceId().

+++ b/restws.formats.inc
@@ -228,18 +290,54 @@ class RestWSFormatXML extends RestWSBaseFormat {
+        // the results into a an numeric array.

should be "a numeric array".

+++ b/restws.formats.inc
@@ -228,18 +290,54 @@ class RestWSFormatXML extends RestWSBaseFormat {
+          if (!empty($id)) {
+            $result_pointer = $id;
+          }

Should use the same FALSE condition as in JSON.

+++ b/restws.formats.inc
@@ -228,18 +290,54 @@ class RestWSFormatXML extends RestWSBaseFormat {
+          else {
+            $result_pointer = $values['uri'];
+          }

This could use a comment, that there is no URI and we use the standard XML to array converted data.

+++ b/restws.test
@@ -99,6 +99,50 @@ class RestWSTestCase extends DrupalWebTestCase {
+  function testResourceArray() {

you should create the taxonmy terms first.

+++ b/restws.test
@@ -99,6 +99,50 @@ class RestWSTestCase extends DrupalWebTestCase {
+    $this->assertEqual($node->field_tags['und'][0]['tid'], 2, 'Taxonomy term 1 was correctly added.');

don't use 'und', use LANGUAGE_NONE instead.

+++ b/restws.test
@@ -192,6 +236,13 @@ class RestWSTestCase extends DrupalWebTestCase {
+  protected function createTerm($term) {

This method is never used?

sepgil’s picture

Status: Needs work » Needs review
StatusFileSize
new10.06 KB

I think all issues should be fixed now...

fago’s picture

Status: Needs review » Needs work
+++ b/restws.formats.inc
@@ -102,13 +102,38 @@ abstract class RestWSBaseFormat implements RestWSFormatInterface {
+   * Extracts the id of a resource array.
+   * @param $resourceArray
+   *   An array containing id and type of an entity.
+   * @param $type
+   *   The type of the entity for which the id should be extracted.
+   * @return
+   *   Returns the extracted id or FALSE if type is not an entity.

Misses a new line before @param and after all @param statements. Also, I think it would be cleaner to use array $resource instead of $resourceArray.

Over at http://drupal.org/node/1682920#comment-6238976 I'd have called that variable array $reference, it's not really a resource, it's a reference to an resource.

+++ b/restws.formats.inc
@@ -187,8 +212,62 @@ class RestWSFormatJSON extends RestWSBaseFormat {
+        // Check if there is a resource array and if the property has a type.

same here, reference array would make more sense to me.

I really like seeing the data-normalizaition separated out more, that's really is what the "base-format" is about. Maybe we should even cleanly separate it out? See #1687836: Leverage Symfony serialization component ?

fago’s picture

StatusFileSize
new11.31 KB

ok, it turned out that #1682920: Add support for referencing entities by UUID can't be clearly separated out on top of this, so I've improved this one and incorporated the UUID bits here.

I've updated the patch by:

  • renaming extractResourceID() to getResourceReferenceValue() and normalizeValues to getPropertyValues().
  • moving the new functions into the baseformat + improved docs for them. They really are the counter-parts of getData() and getResourceReference() - so they should sit in the same class at least. If we go for #1687836: Leverage Symfony serialization component they would probably belong into a Normalizer class.
  • incoroporated my getResourceReferenceValue() implementation from the UUID issue, as it is more powerful, e.g. already handles generic-entity references (untested). Still, URI parsing is todo here as well
  • UUID integration is in the normalized data array and so in JSON only. I've not added or test it in XML - it hurts to have two separate code paths for that. (The normalizer approach would help here).
  • I've tested the UUID module integration in read-mode, as well as updating a single reference by UUID and a multiple-reference by two.

E.g. the following snipped works fine for setting a multiple term-reference field by UUIDs:

{
    "title": "industry",
    "field_industry": [

    {
        "uuid": "6c252962-bd85-5c44-25e0-93ca127020d0"
    },
    {
        "uuid": "e1badf1e-bb0b-ab24-8965-4d3df646899d"
    }

]
}
fago’s picture

Status: Needs work » Needs review

Note: Currently getPropertyValues() ignores that existing properties. Probably we want to improve that to throw errors if not existing properties are set while ignoring read-only properties. But that's another issue, so I've not touched that.

klausi’s picture

Status: Needs review » Fixed

Committed patch from #15, thanks!

I think it is ok to just ignore read-only properties right now. See also #1607662: Don't try to set non-metadata properties on update for unknown properties.

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