kamikat / moshi-jsonapi

JSON API v1.0 Specification in Moshi.
MIT License
156 stars 34 forks source link

How does the "included" calculate resource dependency? #66

Closed skyred closed 7 years ago

skyred commented 7 years ago

I have Resource Point and PointMovement. They reference each other. When I tries to update the Point. Below is actually the request body generated:

{
  "data": {
    "type": "point--default",
    "id": "4c2a6ce3-fbda-41b4-8f65-ca3ef29df853",
    "attributes": {
      "log": "Bank Card RECHARGE",
      "points": 829
    },
    "relationships": {
      "mid": {
        "data": {
          "type": "point_movement--point_movement",
          "id": "74701917-7aed-4b9c-a9b8-009fce3c4dc1"
        },
        "links": {
          "self": "http:\/\/192.168.2.34:456\/jsonapi\/point\/default\/4c2a6ce3-fbda-41b4-8f65-ca3ef29df853\/relationships\/mid",
          "related": "http:\/\/192.168.2.34:456\/jsonapi\/point\/default\/4c2a6ce3-fbda-41b4-8f65-ca3ef29df853\/mid"
        }
      }
    },
    "links": {
      "self": "http:\/\/192.168.2.34:456\/jsonapi\/point\/default\/4c2a6ce3-fbda-41b4-8f65-ca3ef29df853"
    }
  },
  "included": [
    {
      "type": "user--user",
      "id": "c4c6ce44-9022-4ad4-b452-96f50e477739",
      "attributes": {
        "created": 1490352447,
        "field_given_name": "First name",
        "field_mobile_phone": "+8618611111111",
        "field_surname": "Last name",
        "mail": "xxxxxx@gmail.com",
        "name": "xxxxxxx@gmail.com",
        "status": true,
        "uid": 4
      },
      "relationships": {
        "field_balance": {
          "data": {
            "type": "point--default",
            "id": "4c2a6ce3-fbda-41b4-8f65-ca3ef29df853"
          },
          "links": {
            "self": "http:\/\/192.168.2.34:456\/jsonapi\/user\/user\/c4c6ce44-9022-4ad4-b452-96f50e477739\/relationships\/field_balance",
            "related": "http:\/\/192.168.2.34:456\/jsonapi\/user\/user\/c4c6ce44-9022-4ad4-b452-96f50e477739\/field_balance"
          }
        },
        "roles": {
          "data": [
            {
              "type": "user_role--user_role",
              "id": "f1701197-51c4-4f1f-8317-fe4006ba02bd"
            }
          ],
          "links": {
            "self": "http:\/\/192.168.2.34:456\/jsonapi\/user\/user\/c4c6ce44-9022-4ad4-b452-96f50e477739\/relationships\/roles",
            "related": "http:\/\/192.168.2.34:456\/jsonapi\/user\/user\/c4c6ce44-9022-4ad4-b452-96f50e477739\/roles"
          }
        },
        "user_picture": {
          "data": {
            "type": "file--file",
            "id": "a03b5e6c-c54a-4a77-a3f3-102a3c3f0a0f",
            "meta": {
              "alt": "",
              "title": "",
              "width": "531",
              "height": "600"
            }
          },
          "links": {
            "self": "http:\/\/192.168.2.34:456\/jsonapi\/user\/user\/c4c6ce44-9022-4ad4-b452-96f50e477739\/relationships\/user_picture",
            "related": "http:\/\/192.168.2.34:456\/jsonapi\/user\/user\/c4c6ce44-9022-4ad4-b452-96f50e477739\/user_picture"
          }
        }
      },
      "links": {
        "self": "http:\/\/192.168.2.34:456\/jsonapi\/user\/user\/c4c6ce44-9022-4ad4-b452-96f50e477739"
      }
    },
    {
      "type": "point_movement--point_movement",
      "id": "74701917-7aed-4b9c-a9b8-009fce3c4dc1",
      "attributes": {
        "changed": 1501502953,
        "description": "Cash RECHARGE",
        "mid": 121,
        "points": 12
      },
      "links": {
        "self": "http:\/\/192.168.2.34:456\/jsonapi\/point_movement\/point_movement\/74701917-7aed-4b9c-a9b8-009fce3c4dc1"
      }
    },
    {
      "type": "file--file",
      "id": "a03b5e6c-c54a-4a77-a3f3-102a3c3f0a0f",
      "attributes": {
        "uri": "public:\/\/pictures\/2017-03\/7.JPG"
      },
      "links": {
        "self": "http:\/\/192.168.2.34:456\/jsonapi\/file\/file\/a03b5e6c-c54a-4a77-a3f3-102a3c3f0a0f"
      }
    },
    {
      "type": "point--default",
      "id": "4c2a6ce3-fbda-41b4-8f65-ca3ef29df853",
      "attributes": {
        "log": "Bank Card RECHARGE",
        "points": 829
      },
      "relationships": {
        "mid": {
          "data": {
            "type": "point_movement--point_movement",
            "id": "74701917-7aed-4b9c-a9b8-009fce3c4dc1"
          },
          "links": {
            "self": "http:\/\/192.168.2.34:456\/jsonapi\/point\/default\/4c2a6ce3-fbda-41b4-8f65-ca3ef29df853\/relationships\/mid",
            "related": "http:\/\/192.168.2.34:456\/jsonapi\/point\/default\/4c2a6ce3-fbda-41b4-8f65-ca3ef29df853\/mid"
          }
        }
      },
      "links": {
        "self": "http:\/\/192.168.2.34:456\/jsonapi\/point\/default\/4c2a6ce3-fbda-41b4-8f65-ca3ef29df853"
      }
    }
  ],
  "meta": {
    "errors": [
      {
        "title": "Forbidden",
        "status": 403,
        "detail": "The current user is not allowed to GET the selected resource. The 'administer permissions' permission is required.",
        "links": {
          "info": "http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec10.html#sec10.4.4"
        },
        "code": 0,
        "id": "\/user_role--user_role\/f1701197-51c4-4f1f-8317-fe4006ba02bd",
        "source": {
          "pointer": "\/data"
        }
      }
    ]
  },
  "links": {
    "self": "http:\/\/192.168.2.34:456\/jsonapi\/node\/card?include=field_owner%2Cfield_owner.user_picture%2Cfield_owner.field_balance%2Cfield_owner.roles%2Cfield_owner.field_balance.mid&filter%5Btitle%5D%5Bvalue%5D=199660241&filter%5Bfield_state%5D%5Bvalue%5D=disabled&filter%5Bfield_state%5D%5Boperator%5D=%21%3D"
  }
}

I was wondering why does the "included" member include User, and other resources? It doesn't seem to be relevant to this PATCH request

This request body was generated by 'com.squareup.retrofit2:retrofit:2.3.0' 'moe.banana:moshi-jsonapi:3.2.2'

kamikat commented 7 years ago

included is not "calculated" automatically but with explicit call to Document.include(ResourceIdentifier). And edit a resource without detaching it's associated document can have the document attached to that resource being serialized as well when serializing the resource object.

Therefore, a new document object should be attach to updated resource object. https://github.com/kamikat/moshi-jsonapi#document

(It should be less confusing to treat resource as immutable object here)

skyred commented 7 years ago

Thanks for your reply and the explanation. I am still having trouble to understand this statement:

Document is wrap/unwrapped automatically by the converter.

I can do this no problem:

Article article = new Article()
article.title = ....
article.otherProperty = ...
...

// create the article with retrofit
Call<Article> call =  retrofit.createArticle(article);

However, if I try to do:

User author = retrofit.getAuthor(...); // This can also be new Author();
article.setAuthor(author); // This throw exception, as the Document has not been initialized 

// create the article with retrofit
Call<Article> call =  retrofit.createArticle(article);

Above would throw exception as the Document is null.

I was wondering if I initialize a new Resource, how can I set another resource as the Relationship. Or how can I trigger auto wrapping Document around the new Resource?

The documentation shows another way: it first initializes the Document<Article>, then includes Author, but I don't know how to pass Document<Article> to retrofit?

skyred commented 7 years ago

When I do Document<Article> document = new Document<>();, I get the error: "Document is abstract, cannot be instantiated.". hmm...

kamikat commented 7 years ago

I'm sorry for the misguide of documentation. Document object is made abstract since 3.2.0. The replacing constructor is one of ArrayDocument and ObjectDocument.

kamikat commented 7 years ago

Explicitly create a document to handle resources:

Document<Article> document = new ObjectDocument<>();
Document<User> author = retrofit.getAuthor(...);
document.set(article); // FIXME the _doc of article is set to document implicitly
document.include(author); // FIXME so does the author
retrofit.createArticle(article);

The abstraction is not yet perfect but I'll try to eliminate the ambiguity later.