From 0a94072a90222b06fff85eb5261fd0278af9488a Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Mon, 20 Aug 2018 15:07:31 -0400 Subject: [PATCH 1/8] JsonApi -> JSONAPI prefix for pagination classes --- CHANGELOG.md | 3 +- README.rst | 2 +- docs/usage.md | 16 +- example/1045.discuss.jsonapi.org.json | 1632 +++++++++++++++++++++++++ example/1353.discuss.jsonapi.org.json | 1499 +++++++++++++++++++++++ example/tests/unit/test_pagination.py | 6 +- example/views.py | 6 +- rest_framework_json_api/pagination.py | 43 +- 8 files changed, 3185 insertions(+), 22 deletions(-) create mode 100644 example/1045.discuss.jsonapi.org.json create mode 100644 example/1353.discuss.jsonapi.org.json diff --git a/CHANGELOG.md b/CHANGELOG.md index fc160067..be003793 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ * Add sorting configuration to `REST_FRAMEWORK` as defined in [json api spec](http://jsonapi.org/format/#fetching-sorting) * Add `HyperlinkedRelatedField` and `SerializerMethodHyperlinkedRelatedField`. See [usage docs](docs/usage.md#related-fields) * Add related urls support. See [usage docs](docs/usage.md#related-urls) - +* For naming consistency, renamed new `JsonApi`-prefix pagination classes to `JSONAPI`-prefix. + * Deprecates `JsonApiPageNumberPagination` and `JsonApiLimitOffsetPagination` v2.5.0 - Released July 11, 2018 diff --git a/README.rst b/README.rst index 46813188..283ff640 100644 --- a/README.rst +++ b/README.rst @@ -161,7 +161,7 @@ override ``settings.REST_FRAMEWORK`` 'PAGE_SIZE': 10, 'EXCEPTION_HANDLER': 'rest_framework_json_api.exceptions.exception_handler', 'DEFAULT_PAGINATION_CLASS': - 'rest_framework_json_api.pagination.JsonApiPageNumberPagination', + 'rest_framework_json_api.pagination.JSONAPIPageNumberPagination', 'DEFAULT_PARSER_CLASSES': ( 'rest_framework_json_api.parsers.JSONParser', 'rest_framework.parsers.FormParser', diff --git a/docs/usage.md b/docs/usage.md index 25bb7310..61ac4fc3 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -15,7 +15,7 @@ REST_FRAMEWORK = { 'PAGE_SIZE': 10, 'EXCEPTION_HANDLER': 'rest_framework_json_api.exceptions.exception_handler', 'DEFAULT_PAGINATION_CLASS': - 'rest_framework_json_api.pagination.JsonApiPageNumberPagination', + 'rest_framework_json_api.pagination.JSONAPIPageNumberPagination', 'DEFAULT_PARSER_CLASSES': ( 'rest_framework_json_api.parsers.JSONParser', 'rest_framework.parsers.FormParser', @@ -58,15 +58,15 @@ You can configure fixed values for the page size or limit -- or allow the client via query parameters. Two pagination classes are available: -- `JsonApiPageNumberPagination` breaks a response up into pages that start at a given page number with a given size - (number of items per page). It can be configured with the following attributes: +- `JSONAPIPageNumberPagination` breaks a response up into pages that start at a given page number + with a given size (number of items per page). It can be configured with the following attributes: - `page_query_param` (default `page[number]`) - `page_size_query_param` (default `page[size]`) Set this to `None` if you don't want to allow the client to specify the size. - `max_page_size` (default `100`) enforces an upper bound on the `page_size_query_param`. Set it to `None` if you don't want to enforce an upper bound. -- `JsonApiLimitOffsetPagination` breaks a response up into pages that start from an item's offset in the viewset for - a given number of items (the limit). +- `JSONAPILimitOffsetPagination` breaks a response up into pages that start from an item's offset + in the viewset for a given number of items (the limit). It can be configured with the following attributes: - `offset_query_param` (default `page[offset]`). - `limit_query_param` (default `page[limit]`). @@ -77,14 +77,14 @@ Two pagination classes are available: These examples show how to configure the parameters to use non-standard names and different limits: ```python -from rest_framework_json_api.pagination import JsonApiPageNumberPagination, JsonApiLimitOffsetPagination +from rest_framework_json_api.pagination import JSONAPIPageNumberPagination, JSONAPILimitOffsetPagination -class MyPagePagination(JsonApiPageNumberPagination): +class MyPagePagination(JSONAPIPageNumberPagination): page_query_param = 'page_number' page_size_query_param = 'page_size' max_page_size = 1000 -class MyLimitPagination(JsonApiLimitOffsetPagination): +class MyLimitPagination(JSONAPILimitOffsetPagination): offset_query_param = 'offset' limit_query_param = 'limit' max_limit = None diff --git a/example/1045.discuss.jsonapi.org.json b/example/1045.discuss.jsonapi.org.json new file mode 100644 index 00000000..79614a85 --- /dev/null +++ b/example/1045.discuss.jsonapi.org.json @@ -0,0 +1,1632 @@ +{ + "post_stream": { + "posts": [ + { + "id": 1045, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2016-04-13T14:25:18.023Z", + "cooked": "

@Sazzer I can’t imagine a situation where you would have so many links from a resource that it would become unmanageable.

\n

For your specific example, when you want to specify something like pagination links to specific pages you should use URL templates

", + "post_number": 11, + "post_type": 1, + "updated_at": "2016-04-13T14:25:18.023Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 14, + "incoming_link_count": 2, + "reads": 15, + "score": 18.7, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "https://tools.ietf.org/html/rfc6570", + "internal": false, + "reflection": false, + "title": "RFC 6570 - URI Template", + "clicks": 11 + } + ], + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1046, + "name": "Graham", + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", + "created_at": "2016-04-13T14:43:34.171Z", + "cooked": "

@A-Helberg Ok - I’d not come across that before. Are they valid in JSON API Links?

\n

Take, for example, faceted searching. Your search results might want to specify a number of facets and values that you can filter on to restrict down what you are seeing. If you are providing a link for every single value of every single facet then this can get to be very big very quickly. If you instead simply provide the facets and values then the payload size is much smaller, and if the client knows how to then it can build the URLs from those input values. If using these URL Templates in JSONAPI is valid then this becomes possible to do.

", + "post_number": 12, + "post_type": 1, + "updated_at": "2016-04-13T14:43:34.171Z", + "reply_count": 1, + "reply_to_post_number": 11, + "quote_count": 0, + "avg_time": 14, + "incoming_link_count": 1, + "reads": 14, + "score": 13.5, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Graham", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 259, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1047, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T14:50:28.351Z", + "cooked": "\n

JSON API wants you to use a filter parameter for this.

", + "post_number": 13, + "post_type": 1, + "updated_at": "2016-04-13T14:50:28.351Z", + "reply_count": 1, + "reply_to_post_number": 12, + "quote_count": 1, + "avg_time": 17, + "incoming_link_count": 1, + "reads": 13, + "score": 13.45, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "http://jsonapi.org/format/#fetching-filtering", + "internal": false, + "reflection": false, + "title": "JSON API — Latest Specification (v1.0)", + "clicks": 9 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1048, + "name": "Graham", + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", + "created_at": "2016-04-13T14:53:46.405Z", + "cooked": "

Yes it does. But something has to provide a list of possibilities for these parameters. That will either be external documentation combined with some data from the payload, or a large number of links in the payload. (Or just external documentation when the possibilities aren’t dependant on the previous payload)

", + "post_number": 14, + "post_type": 1, + "updated_at": "2016-04-13T14:53:46.405Z", + "reply_count": 2, + "reply_to_post_number": 13, + "quote_count": 0, + "avg_time": 27, + "incoming_link_count": 0, + "reads": 13, + "score": 13.95, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Graham", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 259, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1050, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T14:54:31.325Z", + "cooked": "\n

I disagree.
\nThe links say what you can do right now, i.e. based on the current state of resources held by the server. The client cannot know that in advance.
\nHowever, the client can be told in advance that when a certain action is available, the client must supply certain information (i.e. the POST / PATCH body) in order for the server to carry out the action.

", + "post_number": 15, + "post_type": 1, + "updated_at": "2016-04-13T14:54:31.325Z", + "reply_count": 1, + "reply_to_post_number": 9, + "quote_count": 1, + "avg_time": 16, + "incoming_link_count": 0, + "reads": 13, + "score": 8.4, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1051, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T15:00:28.099Z", + "cooked": "\n

Well, a client could assume that it is entitled to filter on any of the resource’s attributes (and then the server could return error responses for requests it does not support).

\n

But I agree that is pretty unfriendly to the client developer, so personally I would put a list of filterable attributes in the API documentation. As you suggest, providing links for every option and every combination of options could significantly bloat the server response, and probably would be hard for a client to parse and present sensibly back to the user.

\n

I can easily imagine different clients offering different subsets of filters appropriate to their particular users. That feels like a client design time consideration, rather than a runtime consideration, so fits with my current rule of thumb about how different aspects of the API get communicated.

", + "post_number": 16, + "post_type": 1, + "updated_at": "2016-04-13T15:01:08.887Z", + "reply_count": 2, + "reply_to_post_number": 14, + "quote_count": 1, + "avg_time": 10, + "incoming_link_count": 1, + "reads": 14, + "score": 18.3, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1052, + "name": "Graham", + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", + "created_at": "2016-04-13T15:04:50.744Z", + "cooked": "

I have to confess, the more I think about it the more I’m starting to wonder against the idea of hyperlinks in the payloads that tell you what to do next. It works fantastically for simple cases, and it seems like a brilliant idea, but it starts to feel like it’s just making life harder for some situations where you end up breaking out of the resource payload anyway.

\n

And I know the big argument in favour of hyperlinks is that the server can do large-scale reorganisations and the client doesn’t need to know or care, because the new links provided will just work. But unfortunately it just doesn’t work that way in real life.

", + "post_number": 17, + "post_type": 1, + "updated_at": "2016-04-13T15:04:50.744Z", + "reply_count": 1, + "reply_to_post_number": 16, + "quote_count": 0, + "avg_time": 9, + "incoming_link_count": 0, + "reads": 13, + "score": 8.05, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Graham", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 259, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1054, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T15:40:58.402Z", + "cooked": "\n

I disagree (as outlined above in reply to @A-Helberg).
\nJust because we can’t use them for everything, that doesn’t mean we shouldn’t use them for anything.

", + "post_number": 18, + "post_type": 1, + "updated_at": "2016-04-13T15:40:58.402Z", + "reply_count": 1, + "reply_to_post_number": 17, + "quote_count": 1, + "avg_time": 10, + "incoming_link_count": 1, + "reads": 10, + "score": 12.5, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1056, + "name": "Graham", + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", + "created_at": "2016-04-13T16:08:47.849Z", + "cooked": "

That’s true. I’m just a huge fan of consistency is all. It gets awkward using an API when some things work one way and other things work in a completely different way. If everything is the same then you know what you’re dealing with.

", + "post_number": 19, + "post_type": 1, + "updated_at": "2016-04-13T16:08:47.849Z", + "reply_count": 0, + "reply_to_post_number": 18, + "quote_count": 0, + "avg_time": 7, + "incoming_link_count": 0, + "reads": 11, + "score": 2.55, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Graham", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 259, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1057, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2016-04-13T19:54:15.126Z", + "cooked": "

@Sazzer as @jlangley said you would use a filter parameter for “faceted searching”. And I agree that having a link to each possible value for search value is not the way to go, and hence the reason for starting this thread, which I’ll explain in a bit.

\n\n

You are right a client can know in advance when a certain action will be available, and the way you do that, is by making your client privy to business rules and logic. This is a very bad type of coupling and exactly the reason HATEOAS was introduced.

\n\n

Correct but what about the case where the filter parater is not an attribute on the resource?

\n\n

And any of a multitude of API updates would, invalidate your client.
\nConsider the case, mentioned above, where a client only cares about a certain filterable attribute, the only breaking change I can think of right now, is a name change. If the client was able to get the filterable parameters from the API. The absence of the parameter it cares about, means it’s no longer available and the client and degrade gracefully.

", + "post_number": 20, + "post_type": 1, + "updated_at": "2016-04-13T19:54:15.126Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 2, + "avg_time": 9, + "incoming_link_count": 0, + "reads": 12, + "score": 7.85, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1058, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2016-04-13T20:02:57.727Z", + "cooked": "\n

This quote is the crux, of why I want to get some discussion around this thread’s topic.

\n
\n

The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI. This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.
\n…
\nIf the Request-URI is not an asterisk, the OPTIONS request applies only to the options that are available when communicating with that resource.
\n…
\nThe response body, if any, SHOULD also include information about the communication options

\n
\n

The problem we are trying to solve, is getting our available “communication options”, from the server. This is exactly what the OPTIONS method tries to solve.

\n

OPTIONS requests may include a body, and we can use this body to get exactly the type of “options” we care about a particular point in time. The OPTIONS body would include, all allowed methods, the attributes and relationships for the resource, and all query parameters.

\n

In my opinion such a response should include validation information, for each of the attributes, such that the client can reasonably validate the content it is sending to the server, without making a round trip. This would be validations for length and for example format.

", + "post_number": 21, + "post_type": 1, + "updated_at": "2016-04-13T20:02:57.727Z", + "reply_count": 2, + "reply_to_post_number": null, + "quote_count": 1, + "avg_time": 11, + "incoming_link_count": 1, + "reads": 12, + "score": 17.95, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1059, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2016-04-13T20:07:12.905Z", + "cooked": "

As an aside, my motivation for HATEOAS as a driving princaple in API design, is as follows.

\n

Consider a regular website, that supports user registration. As a first pass the site requires, username and password, when you submit the form, you are registered. Later in the application’s life, we change the form to be: email and password. And later on we add extra fields, and we can do whatever!

\n

None of this requires you to update your browser (the client), because the the server’s response is self contained. The client can always infer what actions it should take, by following links, or submitting forms.

", + "post_number": 22, + "post_type": 1, + "updated_at": "2016-04-13T20:07:12.905Z", + "reply_count": 2, + "reply_to_post_number": 21, + "quote_count": 0, + "avg_time": 11, + "incoming_link_count": 0, + "reads": 11, + "score": 12.75, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1060, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T22:34:05.990Z", + "cooked": "\n

You’re thinking of filtering on related resources then I presume? There might be a good case for that, but I suspect you would be better served by doing either of the following:

\n
    \n
  1. \n

    Use a different representation model

    \n
  2. \n
  3. \n

    Use a different technical solution (e.g. GraphQL)

    \n
  4. \n
\n\n

Not that many - you would need to remove or rename a previously allowed filter to break clients, adding a new filter wouldn’t be a breaking change.

\n\n

Where a query parameter or attribute element is only allowed to have a restricted set of values (e.g. an enum, where a client might typically use a drop-down selector) I do like to have the API provide (via related resources) the list of allowed values rather than just put this in the API documentation (this way even removing an allowed value doesn’t break the client \":slight_smile:\"). But currently I don’t believe this approach applies generally to filters though, due to the potential explosion of combinations when an API allows multiple filters.

", + "post_number": 23, + "post_type": 1, + "updated_at": "2016-04-13T22:34:05.990Z", + "reply_count": 0, + "reply_to_post_number": 20, + "quote_count": 1, + "avg_time": 9, + "incoming_link_count": 1, + "reads": 12, + "score": 7.85, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html", + "internal": false, + "reflection": false, + "title": "GraphQL Introduction | React", + "clicks": 1 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1061, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T22:40:09.638Z", + "cooked": "\n

I don’t think you need OPTIONS to do this though. Couldn’t you just use related resources (as per the IANA create-form and edit-form approach?

\n\n

You’re still going to have to validate that data on the server anyway though. I can see that doing client-side validation might be attractive if the network is slow / not reliable, but I suspect you would have just as many problems downloading the validation information before submission as you would retrying after the server rejects the submission.

", + "post_number": 24, + "post_type": 1, + "updated_at": "2016-04-13T22:40:09.638Z", + "reply_count": 1, + "reply_to_post_number": 21, + "quote_count": 1, + "avg_time": 7, + "incoming_link_count": 0, + "reads": 10, + "score": 7.35, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "http://www.iana.org/assignments/link-relations/link-relations.xhtml", + "internal": false, + "reflection": false, + "title": "Link Relations", + "clicks": 5 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1062, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2016-04-13T22:51:57.619Z", + "cooked": "

@jlangley Agreed, I originally looked at the create and edit form links. And this is another solution. But this requires adding a new top-level key in jsonapi, that would represent something analogous to a form, which I am not opposed to, and similar work will be needed for responses to an OPTIONS request, but…

\n

My idea behind using OPTIONS, is that they can be conceptually separated from other requests/responses. If you make anything other than an OPTIONS request, you can safely assume your getting a resource back, and not a specification for a resource.

\n

This would also be a non breaking change to the spec, ie. a jsonapi 1.0 implementation would still work exactly the same with a 1.x version that supports OPTIONS

\n

With regards to round trip for validation, definitely it is possible that this does not improve roundtrip, but this is not the goal. Currently you can get a list of errors back, but those errors are only human readable, and a client can’t really do much with them. I believe having some basic data validation, will improve a client’s user experience A great deal

", + "post_number": 25, + "post_type": 1, + "updated_at": "2016-04-13T22:54:55.644Z", + "reply_count": 1, + "reply_to_post_number": 24, + "quote_count": 0, + "avg_time": 5, + "incoming_link_count": 0, + "reads": 10, + "score": 7.25, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1063, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T22:54:47.141Z", + "cooked": "\n

In a (transactional) “regular website” the browser isn’t the client - the V in the MVC / MVVM web framework running on the server is the client for the API (and that will need updating to cater for the changes you describe). The browser is just rendering the UI (HTML) generated by the client, and the UI hasn’t had a breaking change because changing the API hasn’t caused the semantics of HTML to change.

\n

This doesn’t mean that hypermedia and HATEOAS have no value, but rather that we should recognise that they aren’t “silver bullets” either.

", + "post_number": 26, + "post_type": 1, + "updated_at": "2016-04-13T22:54:47.141Z", + "reply_count": 1, + "reply_to_post_number": 22, + "quote_count": 1, + "avg_time": 3, + "incoming_link_count": 0, + "reads": 12, + "score": 7.55, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1064, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2016-04-13T22:59:34.195Z", + "cooked": "\n

I wasn’t implying anything about API’s here, sorry for the confusion. I meant client, in the case of client-server applications.

", + "post_number": 27, + "post_type": 1, + "updated_at": "2016-04-13T22:59:34.195Z", + "reply_count": 0, + "reply_to_post_number": 26, + "quote_count": 1, + "avg_time": 4, + "incoming_link_count": 0, + "reads": 11, + "score": 2.4, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1065, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T23:09:13.586Z", + "cooked": "\n

No need for that - you can just define the forms under relationships.

\n\n

That’s a nice distinction and I quite like that as an idea. I’m still not sold on the idea that this meta information is best provided at runtime though; and even if it is, should we be using directly in-band data to convey this, or should we link to somewhere else (e.g. API documentation \":wink:\") where this can be found?

\n\n

I disagree. Another reason why I chose JSON API was the structured error information it provides.

\n\n

Perhaps this would be more valuable where you expect to have a high proportion of novice users (e.g. a public facing API), and so you might expect the server to respond with a high proportion of rejections. The things I’m working on right now will only be available internally / to selected partners so this is less of a worry for me.

", + "post_number": 28, + "post_type": 1, + "updated_at": "2016-04-13T23:09:13.586Z", + "reply_count": 0, + "reply_to_post_number": 25, + "quote_count": 1, + "avg_time": 14, + "incoming_link_count": 9, + "reads": 12, + "score": 48.1, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "http://jsonapi.org/format/#error-objects", + "internal": false, + "reflection": false, + "title": "JSON API — Latest Specification (v1.0)", + "clicks": 2 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2786, + "name": "Alan Crosswell", + "username": "n2ygk", + "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png", + "created_at": "2018-03-26T15:47:20.859Z", + "cooked": "

I’m wondering if it’s time to revisit this idea now that OAS 3.0 is out there and it seems to be where the world of API modeling and documentation is converging. Unlike Swagger 2.0, OAS 3.0 is able to represent jsonapi thanks to addition of some missing json-schema constructs.

\n

I’ve been working a bit with django-restframework-json-api and it seems the DRF OPTIONS metadata would be a good place to plug this in as a proof of concept.

", + "post_number": 29, + "post_type": 1, + "updated_at": "2018-03-26T15:47:20.859Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 220, + "incoming_link_count": 0, + "reads": 4, + "score": 16.8, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Alan Crosswell", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "https://www.openapis.org/", + "internal": false, + "reflection": false, + "title": "Home - OpenAPI Initiative", + "clicks": 1 + }, + { + "url": "http://www.django-rest-framework.org/api-guide/metadata/", + "internal": false, + "reflection": false, + "title": "Metadata - Django REST framework", + "clicks": 1 + }, + { + "url": "https://blog.readme.io/an-example-filled-guide-to-swagger-3-2/", + "internal": false, + "reflection": false, + "title": "A Visual Guide to What's New in Swagger 3.0", + "clicks": 1 + } + ], + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 563, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2787, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2018-03-27T06:05:35.616Z", + "cooked": "

I still think this is a marvellous idea. Back when I started this thread I was hoping to get some community feedback on what a proper setup would be, but I have since realised, you don’t need it (the feedback I mean).

\n

If you are working on a proof of concept congratulations \":tada:\"
\nYou now have an amazing project to give a talk about (at almost any tech conference since APIs are language agnostic)

\n

If you write a blog post to share what you’ve done, please link it, I’d love to read it!

\n

Some hidden benefits of this include:
\nAutomatic rendering of resource “forms”
\nOTA(over the air) updates of resources without the need to update clients
\nClient side validation + single source of truth for these validations

", + "post_number": 30, + "post_type": 1, + "updated_at": "2018-03-27T06:05:35.616Z", + "reply_count": 0, + "reply_to_post_number": 29, + "quote_count": 0, + "avg_time": 165, + "incoming_link_count": 0, + "reads": 3, + "score": 8.85, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "n2ygk", + "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + } + ], + "stream": [ + 939, + 1006, + 1018, + 1020, + 1033, + 1035, + 1039, + 1042, + 1043, + 1044, + 1045, + 1046, + 1047, + 1048, + 1050, + 1051, + 1052, + 1054, + 1056, + 1057, + 1058, + 1059, + 1060, + 1061, + 1062, + 1063, + 1064, + 1065, + 2786, + 2787 + ] + }, + "timeline_lookup": [ + [ + 1, + 868 + ], + [ + 2, + 854 + ], + [ + 3, + 853 + ], + [ + 7, + 852 + ], + [ + 10, + 851 + ], + [ + 29, + 139 + ] + ], + "suggested_topics": [ + { + "id": 1158, + "title": "Relationships within abstract relationship", + "fancy_title": "Relationships within abstract relationship", + "slug": "relationships-within-abstract-relationship", + "posts_count": 14, + "reply_count": 6, + "highest_post_number": 14, + "image_url": null, + "created_at": "2017-11-20T23:01:24.465Z", + "last_posted_at": "2018-03-15T05:04:58.419Z", + "bumped": true, + "bumped_at": "2018-03-15T05:04:58.419Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 2, + "views": 1106, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 513, + "username": "a-komarev", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-komarev/{size}/440_1.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + } + ] + }, + { + "id": 1164, + "title": "Complex Nested Arrays", + "fancy_title": "Complex Nested Arrays", + "slug": "complex-nested-arrays", + "posts_count": 1, + "reply_count": 0, + "highest_post_number": 1, + "image_url": null, + "created_at": "2017-12-05T18:40:58.012Z", + "last_posted_at": "2017-12-05T18:40:58.106Z", + "bumped": true, + "bumped_at": "2017-12-06T20:47:19.587Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 0, + "views": 532, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": "latest single", + "description": "Original Poster, Most Recent Poster", + "user": { + "id": 581, + "username": "Wesley", + "avatar_template": "https://avatars.discourse.org/v2/letter/w/eb9ed0/{size}.png" + } + } + ] + }, + { + "id": 1193, + "title": "Is it allowed to have an attribute called \"links\" inside \"attributes\"?", + "fancy_title": "Is it allowed to have an attribute called “links” inside “attributes”?", + "slug": "is-it-allowed-to-have-an-attribute-called-links-inside-attributes", + "posts_count": 3, + "reply_count": 1, + "highest_post_number": 3, + "image_url": null, + "created_at": "2018-01-25T16:59:15.093Z", + "last_posted_at": "2018-01-25T17:42:40.319Z", + "bumped": true, + "bumped_at": "2018-01-25T17:42:40.319Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 0, + "views": 378, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": "latest", + "description": "Original Poster, Most Recent Poster", + "user": { + "id": 341, + "username": "Buju", + "avatar_template": "/user_avatar/discuss.jsonapi.org/buju/{size}/321_1.png" + } + }, + { + "extras": null, + "description": "Frequent Poster", + "user": { + "id": 15, + "username": "dgeb", + "avatar_template": "/user_avatar/discuss.jsonapi.org/dgeb/{size}/17_1.png" + } + } + ] + }, + { + "id": 1194, + "title": "Auth Header Question", + "fancy_title": "Auth Header Question", + "slug": "auth-header-question", + "posts_count": 2, + "reply_count": 0, + "highest_post_number": 2, + "image_url": null, + "created_at": "2018-01-30T19:32:48.514Z", + "last_posted_at": "2018-01-31T05:48:56.600Z", + "bumped": true, + "bumped_at": "2018-01-31T05:48:56.600Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 0, + "views": 373, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 581, + "username": "Wesley", + "avatar_template": "https://avatars.discourse.org/v2/letter/w/eb9ed0/{size}.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + } + ] + }, + { + "id": 1137, + "title": "303 response on POSTing resource without conflicts", + "fancy_title": "303 response on POSTing resource without conflicts", + "slug": "303-response-on-posting-resource-without-conflicts", + "posts_count": 8, + "reply_count": 3, + "highest_post_number": 8, + "image_url": null, + "created_at": "2017-10-23T01:07:28.611Z", + "last_posted_at": "2017-10-26T13:29:35.909Z", + "bumped": true, + "bumped_at": "2017-10-26T13:29:35.909Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 0, + "views": 773, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 513, + "username": "a-komarev", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-komarev/{size}/440_1.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + } + ] + } + ], + "id": 407, + "title": "[RFC] Standardising an OPTIONS response on a server that supports jsonapi", + "fancy_title": "[RFC] Standardising an OPTIONS response on a server that supports jsonapi", + "posts_count": 30, + "created_at": "2016-03-28T07:43:44.543Z", + "views": 3393, + "reply_count": 23, + "like_count": 0, + "last_posted_at": "2018-03-27T06:05:35.616Z", + "visible": true, + "closed": false, + "archived": false, + "has_summary": false, + "archetype": "regular", + "slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "category_id": 1, + "word_count": 3807, + "deleted_at": null, + "user_id": 252, + "featured_link": null, + "pinned_globally": false, + "pinned_at": null, + "pinned_until": null, + "draft": null, + "draft_key": "topic_407", + "draft_sequence": null, + "unpinned": null, + "pinned": false, + "details": { + "created_by": { + "id": 252, + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "last_poster": { + "id": 252, + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "participants": [ + { + "id": 252, + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "post_count": 12, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 251, + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "post_count": 11, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 259, + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", + "post_count": 5, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 49, + "username": "lukasoppermann", + "avatar_template": "/user_avatar/discuss.jsonapi.org/lukasoppermann/{size}/178_1.png", + "post_count": 1, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 563, + "username": "n2ygk", + "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png", + "post_count": 1, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + } + ], + "links": [ + { + "url": "https://tools.ietf.org/html/rfc6570", + "title": "RFC 6570 - URI Template", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 11, + "user_id": 252, + "domain": "tools.ietf.org", + "root_domain": "ietf.org" + }, + { + "url": "http://jsonapi.org/format/#fetching-filtering", + "title": "JSON API — Latest Specification (v1.0)", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 9, + "user_id": 251, + "domain": "jsonapi.org", + "root_domain": "jsonapi.org" + }, + { + "url": "http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven", + "title": "REST APIs must be hypertext-driven » Untangled", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 8, + "user_id": 252, + "domain": "roy.gbiv.com", + "root_domain": "gbiv.com" + }, + { + "url": "http://www.iana.org/assignments/link-relations/link-relations.xhtml", + "title": "Link Relations", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 5, + "user_id": 251, + "domain": "www.iana.org", + "root_domain": "iana.org" + }, + { + "url": "https://github.com/apiaryio/dredd", + "title": "GitHub - apiaryio/dredd: Language-agnostic HTTP API Testing Framework", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 2, + "user_id": 251, + "domain": "github.com", + "root_domain": "github.com" + }, + { + "url": "http://jsonapi.org/format/#error-objects", + "title": "JSON API — Latest Specification (v1.0)", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 2, + "user_id": 251, + "domain": "jsonapi.org", + "root_domain": "jsonapi.org" + }, + { + "url": "https://www.ietf.org/rfc/rfc2616.txt", + "title": null, + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 2, + "user_id": 252, + "domain": "www.ietf.org", + "root_domain": "ietf.org" + }, + { + "url": "https://blog.readme.io/an-example-filled-guide-to-swagger-3-2/", + "title": "A Visual Guide to What's New in Swagger 3.0", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 1, + "user_id": 563, + "domain": "blog.readme.io", + "root_domain": "readme.io" + }, + { + "url": "https://www.openapis.org/", + "title": "Home - OpenAPI Initiative", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 1, + "user_id": 563, + "domain": "www.openapis.org", + "root_domain": "openapis.org" + }, + { + "url": "http://www.django-rest-framework.org/api-guide/metadata/", + "title": "Metadata - Django REST framework", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 1, + "user_id": 563, + "domain": "www.django-rest-framework.org", + "root_domain": "django-rest-framework.org" + }, + { + "url": "https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html", + "title": "GraphQL Introduction | React", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 1, + "user_id": 251, + "domain": "facebook.github.io", + "root_domain": "facebook.github.io" + } + ], + "notification_level": 1, + "can_flag_topic": false + }, + "current_post_number": 24, + "highest_post_number": 30, + "deleted_by": null, + "actions_summary": [ + { + "id": 4, + "count": 0, + "hidden": false, + "can_act": false + }, + { + "id": 8, + "count": 0, + "hidden": false, + "can_act": false + }, + { + "id": 7, + "count": 0, + "hidden": false, + "can_act": false + } + ], + "chunk_size": 20, + "bookmarked": null, + "topic_timer": null, + "message_bus_last_id": 0, + "participant_count": 5, + "can_vote": false, + "vote_count": null, + "user_voted": false +} diff --git a/example/1353.discuss.jsonapi.org.json b/example/1353.discuss.jsonapi.org.json new file mode 100644 index 00000000..94b2cf9a --- /dev/null +++ b/example/1353.discuss.jsonapi.org.json @@ -0,0 +1,1499 @@ +{ + "post_stream": { + "posts": [ + { + "id": 2150, + "name": "", + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", + "created_at": "2017-07-06T01:56:25.763Z", + "cooked": "

I’ve been reading the specs over and over again.

\n

If an article has many comments,

\n

1.) are you supposed to POST to /comments where in body, you have a relationship to article?

\n

or

\n

2.) are you supposed to POST to /articles/33/comments?

\n

and even if you do the 2nd method, do you also put the relationship to the article even if it is in the url?

", + "post_number": 1, + "post_type": 1, + "updated_at": "2017-07-06T01:56:25.763Z", + "reply_count": 0, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 43, + "incoming_link_count": 162, + "reads": 26, + "score": 822.35, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [ + { + "id": 2, + "count": 1 + } + ], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 502, + "hidden": false, + "trust_level": 0, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2154, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-06T19:14:50.492Z", + "cooked": "

Currently, the spec is pretty specific about what is supposed to happen in this case.

\n

If you scroll down to the POST request portion at Update to-many relationships, you’ll notice the /resource/{id}/relationships/{rel-name} link requires a resource identifier object to be POSTed to add itself to the set of resources related to the primary resource through the rel-name.

\n

This unfortunately, seems to be a 2 step process based on the current spec, while some contributors claim you can use the convention of /resource/{id}/{rel-name} to directly post a member of the relationship, this is unintuitive and hackey and most certainly not in the spec as far as I can see. It also breaks the hypermedia driven paradigm by requiring you to implement stateful information in the URL.

\n

The ideal solution would be for them to allow you to post a ‘resource object’ to the resource relationship link directly, and require the server to create the resource if it is of the correct type and then add the resource to the relationship set, following the conventional error handling process described in the spec.

", + "post_number": 2, + "post_type": 1, + "updated_at": "2017-07-06T19:15:23.157Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 55, + "incoming_link_count": 0, + "reads": 26, + "score": 12.95, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "http://jsonapi.org/format/#crud-updating-to-many-relationships", + "internal": false, + "reflection": false, + "title": "JSON API — Latest Specification (v1.0)", + "clicks": 25 + } + ], + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2156, + "name": "", + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "created_at": "2017-07-06T20:07:36.449Z", + "cooked": "

Nested relationships are a common feature of RESTful APIs. I don’t see anything un-intuitive about posting to /resource/{id}/{rel-name}, or even having an entire set of RESTful endpoints there.

", + "post_number": 3, + "post_type": 1, + "updated_at": "2017-07-06T20:07:36.449Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 47, + "incoming_link_count": 0, + "reads": 27, + "score": 12.75, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 503, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2158, + "name": "", + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", + "created_at": "2017-07-06T21:20:23.566Z", + "cooked": "

Thanks,

\n

Not sure if I want to follow the specs to the dot, or do what is “familiar” to non json-api people.

", + "post_number": 4, + "post_type": 1, + "updated_at": "2017-07-06T21:20:23.566Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 28, + "incoming_link_count": 3, + "reads": 27, + "score": 26.8, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 502, + "hidden": false, + "trust_level": 0, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2159, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-06T21:52:48.297Z", + "cooked": "\n

That’s understandable, but those aren’t RESTful APIs. Those are likely CRUD APIs, and json-api is about enhancing and pushing CRUD api users into a more hypermedia driven space.

\n

URL resource partitioning is easy in the short term and handicaps the API designer and consumer in the long term.

\n\n

/giphy Do or do not, there is no try.

\n

Having said that, I did actually just put up a suggestion on GH about solving this issue here.

\n

If you are going to follow the spec, follow the spec because ultimately the people who benefit from the use of the spec are your users. Snowflake or partial implementations which go outside the bounds of ‘unconditionally compliant’ and ‘conditionally compliant’ to the specification actually hurt the consumer in the long run, as their abstractions for the structure aren’t complete, and they can’t use nice tools like Katharsis to operate against your service.

\n

Back on the initial question, as I posted in github issue, I believe there is a better path than your backreference approach which is currently supported by the specification. Namely you POST a compound document with the user defined ID ‘resource identifier object’ to the relationships link /resource/{id}/relationships/{rel-name} with the new resource in the included section.

\n

However, I think the direct POST / PATCH would be an easier way to implement a client long term.

", + "post_number": 5, + "post_type": 1, + "updated_at": "2017-07-06T21:57:26.587Z", + "reply_count": 1, + "reply_to_post_number": 3, + "quote_count": 2, + "avg_time": 35, + "incoming_link_count": 2, + "reads": 28, + "score": 37.35, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "https://github.com/json-api/json-api/issues/1158#issuecomment-313527090", + "internal": false, + "reflection": false, + "title": "Creating new relationship · Issue #1158 · json-api/json-api · GitHub", + "clicks": 21 + }, + { + "url": "https://github.com/katharsis-project/katharsis-framework", + "internal": false, + "reflection": false, + "title": "GitHub - katharsis-project/katharsis-framework: Katharsis adds powerful layer for RESTful endpoints providing implementenation of JSON:API standard", + "clicks": 8 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" + }, + "actions_summary": [ + { + "id": 2, + "count": 1 + } + ], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2160, + "name": "", + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", + "created_at": "2017-07-06T22:00:07.189Z", + "cooked": "

Thanks, do you guys know of any public apis that implement json api spec perfectly? one with “creating” one to many relationships ?

", + "post_number": 6, + "post_type": 1, + "updated_at": "2017-07-06T22:00:07.189Z", + "reply_count": 0, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 22, + "incoming_link_count": 0, + "reads": 24, + "score": 5.9, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 502, + "hidden": false, + "trust_level": 0, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2162, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-06T22:16:26.863Z", + "cooked": "

That might be a better question for @steveklabnik or @dgeb. However, is there a reason you are implementing this all yourself, and not using a library which has done a lot of this heavy lifting for you in your language of choice?

", + "post_number": 7, + "post_type": 1, + "updated_at": "2017-07-06T22:16:26.863Z", + "reply_count": 0, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 21, + "incoming_link_count": 0, + "reads": 24, + "score": 5.85, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2163, + "name": "", + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "created_at": "2017-07-06T22:18:03.058Z", + "cooked": "\n

This is only an option if you use UUIDs for the id field, which not everyone does nor should they.

\n

AFAICT using backreferences in the URL is a valid interpretation of the spec, and as long as it is properly linked, completely valid HATEOAS.

", + "post_number": 8, + "post_type": 1, + "updated_at": "2017-07-06T22:18:03.058Z", + "reply_count": 1, + "reply_to_post_number": 5, + "quote_count": 1, + "avg_time": 25, + "incoming_link_count": 0, + "reads": 24, + "score": 11.05, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 503, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2164, + "name": "", + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", + "created_at": "2017-07-06T22:32:52.281Z", + "cooked": "

I’m using phoenix and elixir and ja_serializer,

\n

it does basics of converting JSON attributes/relationships to Ecto friendly data, but you still have to setup and design the Urls yourself

", + "post_number": 9, + "post_type": 1, + "updated_at": "2017-07-06T22:32:52.281Z", + "reply_count": 0, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 27, + "incoming_link_count": 1, + "reads": 23, + "score": 10.95, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 502, + "hidden": false, + "trust_level": 0, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2166, + "name": "", + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", + "created_at": "2017-07-07T00:34:50.422Z", + "cooked": "

do you guys know of any api documentation that shows a proper json spec implementation?

\n

for example this is for angellist
\nhttps://market.mashape.com/community/angellist

\n

but it is doing the CRUD method

", + "post_number": 10, + "post_type": 1, + "updated_at": "2017-07-07T00:34:50.422Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 32, + "incoming_link_count": 2, + "reads": 23, + "score": 21.2, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "https://market.mashape.com/community/angellist", + "internal": false, + "reflection": false, + "title": "AngelList API Documentation", + "clicks": 17 + } + ], + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 502, + "hidden": false, + "trust_level": 0, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2167, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-07T14:30:15.674Z", + "cooked": "\n

I’m not sure I understand why you make this assertion at all, and identifier is meant to be a key value, why would the format of the identifier matter in any way where a user should not use it? I’ll let you make your arguments for this statement if you chose before I comment.

\n

As far as the back references are concerned, from what I see you are correct. I previously stated exactly that, the backreference method was a valid approach under the specification, but I did make the claim it required a bit of mental gymnastics. I believe this claim is both true and a valid criticism of its usability. The compound document approach is also currently valid within the specification, and far more straightforward.

\n

For the sake of concisely defining the behavior of the specification it isn’t in our collective best interests to dilute the means operating against a server running a json-api service, however I do think as far as semantic sugar is concerned the best approach would be to simply allow the inclusion of a ‘resource object’ in the POST and PATCH collections. My previous paragraph, would of course enumerate the way you COULD do the same now albeit in a more verbose, and constrained fashion.

", + "post_number": 11, + "post_type": 1, + "updated_at": "2017-07-07T14:30:15.674Z", + "reply_count": 0, + "reply_to_post_number": 8, + "quote_count": 1, + "avg_time": 31, + "incoming_link_count": 3, + "reads": 20, + "score": 20.55, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2176, + "name": "", + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "created_at": "2017-07-13T03:15:05.076Z", + "cooked": "

You’ll find they’re quite similar, except unlike CRUD, we’re abstracting things a bit more. You’ll use links, relationships, and sub-resources to help abstract away things like pivot tables and foreign keys. The hardest part is not getting hung up on your data representation and coming up with more elegant ways to represent your data.

\n

There isn’t a “wrong” or “right” way to do it as long as the spec is followed. JSONAPI is more of a standardized set of tools to accomplish these tasks. Welcome to the real difficult part of programming: systems design.

", + "post_number": 12, + "post_type": 1, + "updated_at": "2017-07-13T03:15:05.076Z", + "reply_count": 1, + "reply_to_post_number": 10, + "quote_count": 0, + "avg_time": 29, + "incoming_link_count": 0, + "reads": 20, + "score": 10.45, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 503, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2181, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-13T15:45:23.717Z", + "cooked": "

While I’m very aware of the issues you bring up when designing a hypermedia driven service and agree with the overall premise that system design is the most important and complicated part of the implementation, you still haven’t answered my question. Why “shouldn’t” people use UUID’s? You made no mention of any negative property of their use to recommend against it.

\n

The traditional incremented integer ‘best practice’ is always a worse decision for security and cohesion concerns.

\n

As far as the other methods go, I used the term ‘semantic sugar’ to reference ‘syntactic sugar’ meaning it was functionally equivalent but a more intuitive process.

\n

The proposed modification approach is slightly better than the compound document approach which is markedly better than the back reference approach, when comparing their relative intuitiveness.

", + "post_number": 13, + "post_type": 1, + "updated_at": "2017-07-13T15:45:23.717Z", + "reply_count": 0, + "reply_to_post_number": 12, + "quote_count": 0, + "avg_time": 25, + "incoming_link_count": 0, + "reads": 21, + "score": 5.45, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2248, + "name": "", + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "created_at": "2017-07-28T04:53:59.584Z", + "cooked": "

@michaelhibay My last post referring to system design was directed at @mikeni and designed to re-align this thread on topic. I’m not going to wreck this poor guys inquiry with pointless bickering.

\n

I say UUIDs are a dumb decision and I don’t like the compound doc approach. You say incremented integers are flawed and don’t like backreferences. Ultimately, he must make the design decision best for his project.

", + "post_number": 14, + "post_type": 1, + "updated_at": "2017-07-28T04:53:59.584Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 30, + "incoming_link_count": 0, + "reads": 21, + "score": 10.7, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 503, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2251, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-28T14:21:24.820Z", + "cooked": "

I apologize if my response came off as bikeshedding in your mind. While I feel I am well versed in many subjects, I heartily welcome every opportunity to learn and was very interested to know your thoughts. I was challenging the assertion of UUID usage being a mistake in situations as a chance to learn why you would say this.

\n

The back-reference solution is approaching a bike-shedding discussion which would be better served as a specification level discussion rather than an implementation one to be sure. However, the point is certainly somewhat moot if and when the use of hypermedia can obviate the URL, but hypermedia is unfortunately not a requirement and my statement was the spec should offer limited solutions to the requirement to keep the interaction and documents as uniform as possible.

", + "post_number": 15, + "post_type": 1, + "updated_at": "2017-07-28T14:21:24.820Z", + "reply_count": 1, + "reply_to_post_number": 14, + "quote_count": 0, + "avg_time": 25, + "incoming_link_count": 0, + "reads": 22, + "score": 10.65, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2252, + "name": "", + "username": "cam", + "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png", + "created_at": "2017-07-28T14:49:56.646Z", + "cooked": "

Michael, in the “Update to-many relationships” of the spec, can you please put an example of a POST with a NEW comment (meaning how the URL and json would look like)? From your statement above, it sounds like adding a NEW comment is a two step process. Are you indicating that you would have to first do a POST to /comments and then a POST to /articles/1/relationships/comments? I started a new thread yesterday about this issue with foreign keys and this does not work in that situation. I’m trying to understand the correct way to deal with foreign keys and updating to many relationships.

\n

The more examples, the easier it is to understand what one should do in different scenarios. That would be very helpful. Thank you.
\n–Cam–

", + "post_number": 16, + "post_type": 1, + "updated_at": "2017-07-28T14:56:42.102Z", + "reply_count": 2, + "reply_to_post_number": 2, + "quote_count": 0, + "avg_time": 29, + "incoming_link_count": 2, + "reads": 21, + "score": 25.65, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 2, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 499, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2254, + "name": "", + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "created_at": "2017-07-28T17:01:02.162Z", + "cooked": "

You can do this with one request using a compound document posted to /comments. I agree, though, there needs to be more use cases of this in action. You would POST the following, and the relationships would be created along with the object.

\n

http://jsonapi.org/format/#document-compound-documents

", + "post_number": 17, + "post_type": 1, + "updated_at": "2017-07-28T17:04:00.964Z", + "reply_count": 0, + "reply_to_post_number": 16, + "quote_count": 0, + "avg_time": 31, + "incoming_link_count": 0, + "reads": 20, + "score": 5.55, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "http://jsonapi.org/format/#document-compound-documents", + "internal": false, + "reflection": false, + "clicks": 25 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "cam", + "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 503, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2255, + "name": "", + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "created_at": "2017-07-28T17:10:57.711Z", + "cooked": "

Until I have a measurably better suggestion, I won’t complain about it anymore. I don’t have numbers to back up any claim that I’d make, and all my complaints would be performance related.

\n

I’ve been researching alternatives, and if I get around to benchmarking them I’ll post the results in a new thread.

", + "post_number": 18, + "post_type": 1, + "updated_at": "2017-07-28T17:10:57.711Z", + "reply_count": 1, + "reply_to_post_number": 15, + "quote_count": 0, + "avg_time": 26, + "incoming_link_count": 1, + "reads": 20, + "score": 15.3, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 503, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2256, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-28T17:32:34.478Z", + "cooked": "

There’s no need to carry this discussion in multiple threads. I responded to the thread you created, and we can continue there.

\n\n

I’d be interested in seeing those results. Despite what they reinforce in college, performance related issues which are outside the bounds of algorithmic complexity are generally best addressed when they show signs of being a problem. I regularly have to fight the urge of premature optimization, so I understand where you are coming from.

", + "post_number": 19, + "post_type": 1, + "updated_at": "2017-07-28T17:32:34.478Z", + "reply_count": 0, + "reply_to_post_number": 16, + "quote_count": 1, + "avg_time": 34, + "incoming_link_count": 4, + "reads": 21, + "score": 25.9, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "http://discuss.jsonapi.org/t/posting-related-data/1054/3", + "internal": true, + "reflection": false, + "title": "POSTing related data", + "clicks": 30 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "cam", + "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + } + ], + "stream": [ + 2150, + 2154, + 2156, + 2158, + 2159, + 2160, + 2162, + 2163, + 2164, + 2166, + 2167, + 2176, + 2181, + 2248, + 2251, + 2252, + 2254, + 2255, + 2256 + ] + }, + "timeline_lookup": [ + [ + 1, + 403 + ], + [ + 2, + 402 + ], + [ + 11, + 401 + ], + [ + 12, + 396 + ], + [ + 13, + 395 + ], + [ + 14, + 381 + ], + [ + 15, + 380 + ] + ], + "suggested_topics": [ + { + "id": 1121, + "title": "Is it valid for a server to return a response with no body?", + "fancy_title": "Is it valid for a server to return a response with no body?", + "slug": "is-it-valid-for-a-server-to-return-a-response-with-no-body", + "posts_count": 3, + "reply_count": 0, + "highest_post_number": 3, + "image_url": null, + "created_at": "2017-10-06T15:09:32.969Z", + "last_posted_at": "2017-10-06T18:49:15.183Z", + "bumped": true, + "bumped_at": "2017-10-06T18:49:15.183Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 3, + "views": 569, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 565, + "username": "bundacia", + "avatar_template": "/user_avatar/discuss.jsonapi.org/bundacia/{size}/496_1.png" + } + }, + { + "extras": null, + "description": "Frequent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 563, + "username": "n2ygk", + "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png" + } + } + ] + }, + { + "id": 1150, + "title": "Status of Versioning - What's the silver bullet?", + "fancy_title": "Status of Versioning - What’s the silver bullet?", + "slug": "status-of-versioning-whats-the-silver-bullet", + "posts_count": 1, + "reply_count": 0, + "highest_post_number": 1, + "image_url": null, + "created_at": "2017-11-03T10:15:37.624Z", + "last_posted_at": "2017-11-03T10:15:37.685Z", + "bumped": true, + "bumped_at": "2017-11-03T10:15:37.685Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 0, + "views": 113, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": "latest single", + "description": "Original Poster, Most Recent Poster", + "user": { + "id": 572, + "username": "mblackritter", + "avatar_template": "/user_avatar/discuss.jsonapi.org/mblackritter/{size}/498_1.png" + } + } + ] + }, + { + "id": 1192, + "title": "Returning page meta data", + "fancy_title": "Returning page meta data", + "slug": "returning-page-meta-data", + "posts_count": 2, + "reply_count": 0, + "highest_post_number": 2, + "image_url": null, + "created_at": "2018-01-25T15:34:35.664Z", + "last_posted_at": "2018-03-08T13:37:46.651Z", + "bumped": true, + "bumped_at": "2018-03-08T13:37:46.651Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 1, + "views": 416, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 599, + "username": "marceloverdijk", + "avatar_template": "/user_avatar/discuss.jsonapi.org/marceloverdijk/{size}/521_1.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + } + ] + }, + { + "id": 1194, + "title": "Auth Header Question", + "fancy_title": "Auth Header Question", + "slug": "auth-header-question", + "posts_count": 2, + "reply_count": 0, + "highest_post_number": 2, + "image_url": null, + "created_at": "2018-01-30T19:32:48.514Z", + "last_posted_at": "2018-01-31T05:48:56.600Z", + "bumped": true, + "bumped_at": "2018-01-31T05:48:56.600Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 0, + "views": 373, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 581, + "username": "Wesley", + "avatar_template": "https://avatars.discourse.org/v2/letter/w/eb9ed0/{size}.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + } + ] + }, + { + "id": 1065, + "title": "Idea: Request Cycle Library", + "fancy_title": "Idea: Request Cycle Library", + "slug": "idea-request-cycle-library", + "posts_count": 7, + "reply_count": 3, + "highest_post_number": 7, + "image_url": null, + "created_at": "2017-08-13T19:03:54.180Z", + "last_posted_at": "2017-09-02T15:38:20.661Z", + "bumped": true, + "bumped_at": "2017-09-02T15:38:20.661Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 1, + "views": 764, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 503, + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" + } + }, + { + "extras": null, + "description": "Frequent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 61, + "username": "beauby", + "avatar_template": "https://avatars.discourse.org/v2/letter/b/f475e1/{size}.png" + } + } + ] + } + ], + "id": 1020, + "title": "POST /comments with relationship or POST /articles/33/comments", + "fancy_title": "POST /comments with relationship or POST /articles/33/comments", + "posts_count": 19, + "created_at": "2017-07-06T01:56:25.691Z", + "views": 2026, + "reply_count": 10, + "like_count": 2, + "last_posted_at": "2017-07-28T17:32:34.478Z", + "visible": true, + "closed": false, + "archived": false, + "has_summary": false, + "archetype": "regular", + "slug": "post-comments-with-relationship-or-post-articles-33-comments", + "category_id": 1, + "word_count": 2562, + "deleted_at": null, + "user_id": 502, + "featured_link": null, + "pinned_globally": false, + "pinned_at": null, + "pinned_until": null, + "draft": null, + "draft_key": "topic_1020", + "draft_sequence": null, + "unpinned": null, + "pinned": false, + "details": { + "created_by": { + "id": 502, + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png" + }, + "last_poster": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + }, + "participants": [ + { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "post_count": 7, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 503, + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "post_count": 6, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 502, + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", + "post_count": 5, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 499, + "username": "cam", + "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png", + "post_count": 1, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + } + ], + "links": [ + { + "url": "http://discuss.jsonapi.org/t/posting-related-data/1054/3", + "title": "POSTing related data", + "internal": true, + "attachment": false, + "reflection": false, + "clicks": 30, + "user_id": 415, + "domain": "discuss.jsonapi.org", + "root_domain": "jsonapi.org" + }, + { + "url": "http://jsonapi.org/format/#crud-updating-to-many-relationships", + "title": "JSON API — Latest Specification (v1.0)", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 25, + "user_id": 415, + "domain": "jsonapi.org", + "root_domain": "jsonapi.org" + }, + { + "url": "http://jsonapi.org/format/#document-compound-documents", + "title": null, + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 25, + "user_id": 503, + "domain": "jsonapi.org", + "root_domain": "jsonapi.org" + }, + { + "url": "https://github.com/json-api/json-api/issues/1158#issuecomment-313527090", + "title": "Creating new relationship · Issue #1158 · json-api/json-api · GitHub", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 21, + "user_id": 415, + "domain": "github.com", + "root_domain": "github.com" + }, + { + "url": "https://market.mashape.com/community/angellist", + "title": "AngelList API Documentation", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 17, + "user_id": 502, + "domain": "market.mashape.com", + "root_domain": "mashape.com" + }, + { + "url": "https://github.com/katharsis-project/katharsis-framework", + "title": "GitHub - katharsis-project/katharsis-framework: Katharsis adds powerful layer for RESTful endpoints providing implementenation of JSON:API standard", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 8, + "user_id": 415, + "domain": "github.com", + "root_domain": "github.com" + } + ], + "notification_level": 1, + "can_flag_topic": false + }, + "current_post_number": 1, + "highest_post_number": 19, + "deleted_by": null, + "actions_summary": [ + { + "id": 4, + "count": 0, + "hidden": false, + "can_act": false + }, + { + "id": 8, + "count": 0, + "hidden": false, + "can_act": false + }, + { + "id": 7, + "count": 0, + "hidden": false, + "can_act": false + } + ], + "chunk_size": 20, + "bookmarked": null, + "topic_timer": null, + "message_bus_last_id": 6, + "participant_count": 4, + "can_vote": false, + "vote_count": null, + "user_voted": false +} diff --git a/example/tests/unit/test_pagination.py b/example/tests/unit/test_pagination.py index f6e95db0..b2cd1621 100644 --- a/example/tests/unit/test_pagination.py +++ b/example/tests/unit/test_pagination.py @@ -13,11 +13,11 @@ class TestLimitOffset: """ - Unit tests for `pagination.JsonApiLimitOffsetPagination`. + Unit tests for `pagination.JSONAPILimitOffsetPagination`. """ def setup(self): - class ExamplePagination(pagination.JsonApiLimitOffsetPagination): + class ExamplePagination(pagination.JSONAPILimitOffsetPagination): default_limit = 10 max_limit = 15 @@ -91,7 +91,7 @@ def test_limit_offset_deprecation(self): reason="python2.7 fails for unknown reason") class TestPageNumber: """ - Unit tests for `pagination.JsonApiPageNumberPagination`. + Unit tests for `pagination.JSONAPIPageNumberPagination`. TODO: add unit tests for changing query parameter names, limits, etc. """ def test_page_number_deprecation(self): diff --git a/example/views.py b/example/views.py index 5dfc3341..a42a80ae 100644 --- a/example/views.py +++ b/example/views.py @@ -34,7 +34,7 @@ def get_object(self): return super(BlogViewSet, self).get_object() -class JsonApiViewSet(ModelViewSet): +class JSONAPIViewSet(ModelViewSet): """ This is an example on how to configure DRF-jsonapi from within a class. It allows using DRF-jsonapi alongside @@ -58,12 +58,12 @@ def handle_exception(self, exc): exc.status_code = HTTP_422_UNPROCESSABLE_ENTITY # exception handler can't be set on class so you have to # override the error response in this method - response = super(JsonApiViewSet, self).handle_exception(exc) + response = super(JSONAPIViewSet, self).handle_exception(exc) context = self.get_exception_handler_context() return format_drf_errors(response, context, exc) -class BlogCustomViewSet(JsonApiViewSet): +class BlogCustomViewSet(JSONAPIViewSet): queryset = Blog.objects.all() serializer_class = BlogSerializer diff --git a/rest_framework_json_api/pagination.py b/rest_framework_json_api/pagination.py index 00873c99..6b1f0fc5 100644 --- a/rest_framework_json_api/pagination.py +++ b/rest_framework_json_api/pagination.py @@ -9,7 +9,7 @@ from rest_framework.views import Response -class JsonApiPageNumberPagination(PageNumberPagination): +class JSONAPIPageNumberPagination(PageNumberPagination): """ A json-api compatible pagination format """ @@ -50,7 +50,7 @@ def get_paginated_response(self, data): }) -class JsonApiLimitOffsetPagination(LimitOffsetPagination): +class JSONAPILimitOffsetPagination(LimitOffsetPagination): """ A limit/offset based style. For example: http://api.example.org/accounts/?page[limit]=100 @@ -100,7 +100,23 @@ def get_paginated_response(self, data): }) -class PageNumberPagination(JsonApiPageNumberPagination): +class JsonApiPageNumberPagination(JSONAPIPageNumberPagination): + """ + Deprecated due to desire to use `JSONAPI` prefix for all classes. + """ + page_query_param = 'page' + page_size_query_param = 'page_size' + + def __init__(self): + warnings.warn( + 'JsonApiPageNumberPagination is deprecated. Use JSONAPIPageNumberPagination ' + 'or create custom pagination. See ' + 'https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#pagination', + DeprecationWarning) + super(PageNumberPagination, self).__init__() + + +class PageNumberPagination(JSONAPIPageNumberPagination): """ Deprecated paginator that uses different query parameters """ @@ -109,14 +125,29 @@ class PageNumberPagination(JsonApiPageNumberPagination): def __init__(self): warnings.warn( - 'PageNumberPagination is deprecated. Use JsonApiPageNumberPagination ' + 'PageNumberPagination is deprecated. Use JSONAPIPageNumberPagination ' 'or create custom pagination. See ' 'https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#pagination', DeprecationWarning) super(PageNumberPagination, self).__init__() -class LimitOffsetPagination(JsonApiLimitOffsetPagination): +class JsonApiLimitOffsetPagination(JSONAPILimitOffsetPagination): + """ + Deprecated due to desire to use `JSONAPI` prefix for all classes. + """ + max_limit = None + + def __init__(self): + warnings.warn( + 'JsonApiLimitOffsetPagination is deprecated. Use JSONAPILimitOffsetPagination ' + 'or create custom pagination. See ' + 'https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#pagination', + DeprecationWarning) + super(LimitOffsetPagination, self).__init__() + + +class LimitOffsetPagination(JSONAPILimitOffsetPagination): """ Deprecated paginator that uses a different max_limit """ @@ -124,7 +155,7 @@ class LimitOffsetPagination(JsonApiLimitOffsetPagination): def __init__(self): warnings.warn( - 'LimitOffsetPagination is deprecated. Use JsonApiLimitOffsetPagination ' + 'LimitOffsetPagination is deprecated. Use JSONAPILimitOffsetPagination ' 'or create custom pagination. See ' 'https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#pagination', DeprecationWarning) From ae145d2192ebd40e0f0e1f2795ab71095a0ba8c7 Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Mon, 20 Aug 2018 15:07:59 -0400 Subject: [PATCH 2/8] fix tox -e flake8 to only check our code --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index d5cca046..24df43e4 100644 --- a/tox.ini +++ b/tox.ini @@ -20,7 +20,7 @@ commands = [testenv:flake8] deps = flake8 -commands = flake8 +commands = flake8 --exclude=migrations rest_framework_json_api example skip_install = true [testenv:isort] From 551edd2a481282a754b46e831b9a2df7e734d4aa Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Mon, 20 Aug 2018 15:16:32 -0400 Subject: [PATCH 3/8] remove inadvertently added files --- example/1045.discuss.jsonapi.org.json | 1632 ------------------------- example/1353.discuss.jsonapi.org.json | 1499 ----------------------- 2 files changed, 3131 deletions(-) delete mode 100644 example/1045.discuss.jsonapi.org.json delete mode 100644 example/1353.discuss.jsonapi.org.json diff --git a/example/1045.discuss.jsonapi.org.json b/example/1045.discuss.jsonapi.org.json deleted file mode 100644 index 79614a85..00000000 --- a/example/1045.discuss.jsonapi.org.json +++ /dev/null @@ -1,1632 +0,0 @@ -{ - "post_stream": { - "posts": [ - { - "id": 1045, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2016-04-13T14:25:18.023Z", - "cooked": "

@Sazzer I can’t imagine a situation where you would have so many links from a resource that it would become unmanageable.

\n

For your specific example, when you want to specify something like pagination links to specific pages you should use URL templates

", - "post_number": 11, - "post_type": 1, - "updated_at": "2016-04-13T14:25:18.023Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 14, - "incoming_link_count": 2, - "reads": 15, - "score": 18.7, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "https://tools.ietf.org/html/rfc6570", - "internal": false, - "reflection": false, - "title": "RFC 6570 - URI Template", - "clicks": 11 - } - ], - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1046, - "name": "Graham", - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", - "created_at": "2016-04-13T14:43:34.171Z", - "cooked": "

@A-Helberg Ok - I’d not come across that before. Are they valid in JSON API Links?

\n

Take, for example, faceted searching. Your search results might want to specify a number of facets and values that you can filter on to restrict down what you are seeing. If you are providing a link for every single value of every single facet then this can get to be very big very quickly. If you instead simply provide the facets and values then the payload size is much smaller, and if the client knows how to then it can build the URLs from those input values. If using these URL Templates in JSONAPI is valid then this becomes possible to do.

", - "post_number": 12, - "post_type": 1, - "updated_at": "2016-04-13T14:43:34.171Z", - "reply_count": 1, - "reply_to_post_number": 11, - "quote_count": 0, - "avg_time": 14, - "incoming_link_count": 1, - "reads": 14, - "score": 13.5, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Graham", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 259, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1047, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T14:50:28.351Z", - "cooked": "\n

JSON API wants you to use a filter parameter for this.

", - "post_number": 13, - "post_type": 1, - "updated_at": "2016-04-13T14:50:28.351Z", - "reply_count": 1, - "reply_to_post_number": 12, - "quote_count": 1, - "avg_time": 17, - "incoming_link_count": 1, - "reads": 13, - "score": 13.45, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "http://jsonapi.org/format/#fetching-filtering", - "internal": false, - "reflection": false, - "title": "JSON API — Latest Specification (v1.0)", - "clicks": 9 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1048, - "name": "Graham", - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", - "created_at": "2016-04-13T14:53:46.405Z", - "cooked": "

Yes it does. But something has to provide a list of possibilities for these parameters. That will either be external documentation combined with some data from the payload, or a large number of links in the payload. (Or just external documentation when the possibilities aren’t dependant on the previous payload)

", - "post_number": 14, - "post_type": 1, - "updated_at": "2016-04-13T14:53:46.405Z", - "reply_count": 2, - "reply_to_post_number": 13, - "quote_count": 0, - "avg_time": 27, - "incoming_link_count": 0, - "reads": 13, - "score": 13.95, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Graham", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 259, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1050, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T14:54:31.325Z", - "cooked": "\n

I disagree.
\nThe links say what you can do right now, i.e. based on the current state of resources held by the server. The client cannot know that in advance.
\nHowever, the client can be told in advance that when a certain action is available, the client must supply certain information (i.e. the POST / PATCH body) in order for the server to carry out the action.

", - "post_number": 15, - "post_type": 1, - "updated_at": "2016-04-13T14:54:31.325Z", - "reply_count": 1, - "reply_to_post_number": 9, - "quote_count": 1, - "avg_time": 16, - "incoming_link_count": 0, - "reads": 13, - "score": 8.4, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1051, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T15:00:28.099Z", - "cooked": "\n

Well, a client could assume that it is entitled to filter on any of the resource’s attributes (and then the server could return error responses for requests it does not support).

\n

But I agree that is pretty unfriendly to the client developer, so personally I would put a list of filterable attributes in the API documentation. As you suggest, providing links for every option and every combination of options could significantly bloat the server response, and probably would be hard for a client to parse and present sensibly back to the user.

\n

I can easily imagine different clients offering different subsets of filters appropriate to their particular users. That feels like a client design time consideration, rather than a runtime consideration, so fits with my current rule of thumb about how different aspects of the API get communicated.

", - "post_number": 16, - "post_type": 1, - "updated_at": "2016-04-13T15:01:08.887Z", - "reply_count": 2, - "reply_to_post_number": 14, - "quote_count": 1, - "avg_time": 10, - "incoming_link_count": 1, - "reads": 14, - "score": 18.3, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1052, - "name": "Graham", - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", - "created_at": "2016-04-13T15:04:50.744Z", - "cooked": "

I have to confess, the more I think about it the more I’m starting to wonder against the idea of hyperlinks in the payloads that tell you what to do next. It works fantastically for simple cases, and it seems like a brilliant idea, but it starts to feel like it’s just making life harder for some situations where you end up breaking out of the resource payload anyway.

\n

And I know the big argument in favour of hyperlinks is that the server can do large-scale reorganisations and the client doesn’t need to know or care, because the new links provided will just work. But unfortunately it just doesn’t work that way in real life.

", - "post_number": 17, - "post_type": 1, - "updated_at": "2016-04-13T15:04:50.744Z", - "reply_count": 1, - "reply_to_post_number": 16, - "quote_count": 0, - "avg_time": 9, - "incoming_link_count": 0, - "reads": 13, - "score": 8.05, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Graham", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 259, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1054, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T15:40:58.402Z", - "cooked": "\n

I disagree (as outlined above in reply to @A-Helberg).
\nJust because we can’t use them for everything, that doesn’t mean we shouldn’t use them for anything.

", - "post_number": 18, - "post_type": 1, - "updated_at": "2016-04-13T15:40:58.402Z", - "reply_count": 1, - "reply_to_post_number": 17, - "quote_count": 1, - "avg_time": 10, - "incoming_link_count": 1, - "reads": 10, - "score": 12.5, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1056, - "name": "Graham", - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", - "created_at": "2016-04-13T16:08:47.849Z", - "cooked": "

That’s true. I’m just a huge fan of consistency is all. It gets awkward using an API when some things work one way and other things work in a completely different way. If everything is the same then you know what you’re dealing with.

", - "post_number": 19, - "post_type": 1, - "updated_at": "2016-04-13T16:08:47.849Z", - "reply_count": 0, - "reply_to_post_number": 18, - "quote_count": 0, - "avg_time": 7, - "incoming_link_count": 0, - "reads": 11, - "score": 2.55, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Graham", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 259, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1057, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2016-04-13T19:54:15.126Z", - "cooked": "

@Sazzer as @jlangley said you would use a filter parameter for “faceted searching”. And I agree that having a link to each possible value for search value is not the way to go, and hence the reason for starting this thread, which I’ll explain in a bit.

\n\n

You are right a client can know in advance when a certain action will be available, and the way you do that, is by making your client privy to business rules and logic. This is a very bad type of coupling and exactly the reason HATEOAS was introduced.

\n\n

Correct but what about the case where the filter parater is not an attribute on the resource?

\n\n

And any of a multitude of API updates would, invalidate your client.
\nConsider the case, mentioned above, where a client only cares about a certain filterable attribute, the only breaking change I can think of right now, is a name change. If the client was able to get the filterable parameters from the API. The absence of the parameter it cares about, means it’s no longer available and the client and degrade gracefully.

", - "post_number": 20, - "post_type": 1, - "updated_at": "2016-04-13T19:54:15.126Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 2, - "avg_time": 9, - "incoming_link_count": 0, - "reads": 12, - "score": 7.85, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1058, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2016-04-13T20:02:57.727Z", - "cooked": "\n

This quote is the crux, of why I want to get some discussion around this thread’s topic.

\n
\n

The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI. This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.
\n…
\nIf the Request-URI is not an asterisk, the OPTIONS request applies only to the options that are available when communicating with that resource.
\n…
\nThe response body, if any, SHOULD also include information about the communication options

\n
\n

The problem we are trying to solve, is getting our available “communication options”, from the server. This is exactly what the OPTIONS method tries to solve.

\n

OPTIONS requests may include a body, and we can use this body to get exactly the type of “options” we care about a particular point in time. The OPTIONS body would include, all allowed methods, the attributes and relationships for the resource, and all query parameters.

\n

In my opinion such a response should include validation information, for each of the attributes, such that the client can reasonably validate the content it is sending to the server, without making a round trip. This would be validations for length and for example format.

", - "post_number": 21, - "post_type": 1, - "updated_at": "2016-04-13T20:02:57.727Z", - "reply_count": 2, - "reply_to_post_number": null, - "quote_count": 1, - "avg_time": 11, - "incoming_link_count": 1, - "reads": 12, - "score": 17.95, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1059, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2016-04-13T20:07:12.905Z", - "cooked": "

As an aside, my motivation for HATEOAS as a driving princaple in API design, is as follows.

\n

Consider a regular website, that supports user registration. As a first pass the site requires, username and password, when you submit the form, you are registered. Later in the application’s life, we change the form to be: email and password. And later on we add extra fields, and we can do whatever!

\n

None of this requires you to update your browser (the client), because the the server’s response is self contained. The client can always infer what actions it should take, by following links, or submitting forms.

", - "post_number": 22, - "post_type": 1, - "updated_at": "2016-04-13T20:07:12.905Z", - "reply_count": 2, - "reply_to_post_number": 21, - "quote_count": 0, - "avg_time": 11, - "incoming_link_count": 0, - "reads": 11, - "score": 12.75, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1060, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T22:34:05.990Z", - "cooked": "\n

You’re thinking of filtering on related resources then I presume? There might be a good case for that, but I suspect you would be better served by doing either of the following:

\n
    \n
  1. \n

    Use a different representation model

    \n
  2. \n
  3. \n

    Use a different technical solution (e.g. GraphQL)

    \n
  4. \n
\n\n

Not that many - you would need to remove or rename a previously allowed filter to break clients, adding a new filter wouldn’t be a breaking change.

\n\n

Where a query parameter or attribute element is only allowed to have a restricted set of values (e.g. an enum, where a client might typically use a drop-down selector) I do like to have the API provide (via related resources) the list of allowed values rather than just put this in the API documentation (this way even removing an allowed value doesn’t break the client \":slight_smile:\"). But currently I don’t believe this approach applies generally to filters though, due to the potential explosion of combinations when an API allows multiple filters.

", - "post_number": 23, - "post_type": 1, - "updated_at": "2016-04-13T22:34:05.990Z", - "reply_count": 0, - "reply_to_post_number": 20, - "quote_count": 1, - "avg_time": 9, - "incoming_link_count": 1, - "reads": 12, - "score": 7.85, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html", - "internal": false, - "reflection": false, - "title": "GraphQL Introduction | React", - "clicks": 1 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1061, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T22:40:09.638Z", - "cooked": "\n

I don’t think you need OPTIONS to do this though. Couldn’t you just use related resources (as per the IANA create-form and edit-form approach?

\n\n

You’re still going to have to validate that data on the server anyway though. I can see that doing client-side validation might be attractive if the network is slow / not reliable, but I suspect you would have just as many problems downloading the validation information before submission as you would retrying after the server rejects the submission.

", - "post_number": 24, - "post_type": 1, - "updated_at": "2016-04-13T22:40:09.638Z", - "reply_count": 1, - "reply_to_post_number": 21, - "quote_count": 1, - "avg_time": 7, - "incoming_link_count": 0, - "reads": 10, - "score": 7.35, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "http://www.iana.org/assignments/link-relations/link-relations.xhtml", - "internal": false, - "reflection": false, - "title": "Link Relations", - "clicks": 5 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1062, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2016-04-13T22:51:57.619Z", - "cooked": "

@jlangley Agreed, I originally looked at the create and edit form links. And this is another solution. But this requires adding a new top-level key in jsonapi, that would represent something analogous to a form, which I am not opposed to, and similar work will be needed for responses to an OPTIONS request, but…

\n

My idea behind using OPTIONS, is that they can be conceptually separated from other requests/responses. If you make anything other than an OPTIONS request, you can safely assume your getting a resource back, and not a specification for a resource.

\n

This would also be a non breaking change to the spec, ie. a jsonapi 1.0 implementation would still work exactly the same with a 1.x version that supports OPTIONS

\n

With regards to round trip for validation, definitely it is possible that this does not improve roundtrip, but this is not the goal. Currently you can get a list of errors back, but those errors are only human readable, and a client can’t really do much with them. I believe having some basic data validation, will improve a client’s user experience A great deal

", - "post_number": 25, - "post_type": 1, - "updated_at": "2016-04-13T22:54:55.644Z", - "reply_count": 1, - "reply_to_post_number": 24, - "quote_count": 0, - "avg_time": 5, - "incoming_link_count": 0, - "reads": 10, - "score": 7.25, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1063, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T22:54:47.141Z", - "cooked": "\n

In a (transactional) “regular website” the browser isn’t the client - the V in the MVC / MVVM web framework running on the server is the client for the API (and that will need updating to cater for the changes you describe). The browser is just rendering the UI (HTML) generated by the client, and the UI hasn’t had a breaking change because changing the API hasn’t caused the semantics of HTML to change.

\n

This doesn’t mean that hypermedia and HATEOAS have no value, but rather that we should recognise that they aren’t “silver bullets” either.

", - "post_number": 26, - "post_type": 1, - "updated_at": "2016-04-13T22:54:47.141Z", - "reply_count": 1, - "reply_to_post_number": 22, - "quote_count": 1, - "avg_time": 3, - "incoming_link_count": 0, - "reads": 12, - "score": 7.55, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1064, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2016-04-13T22:59:34.195Z", - "cooked": "\n

I wasn’t implying anything about API’s here, sorry for the confusion. I meant client, in the case of client-server applications.

", - "post_number": 27, - "post_type": 1, - "updated_at": "2016-04-13T22:59:34.195Z", - "reply_count": 0, - "reply_to_post_number": 26, - "quote_count": 1, - "avg_time": 4, - "incoming_link_count": 0, - "reads": 11, - "score": 2.4, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1065, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T23:09:13.586Z", - "cooked": "\n

No need for that - you can just define the forms under relationships.

\n\n

That’s a nice distinction and I quite like that as an idea. I’m still not sold on the idea that this meta information is best provided at runtime though; and even if it is, should we be using directly in-band data to convey this, or should we link to somewhere else (e.g. API documentation \":wink:\") where this can be found?

\n\n

I disagree. Another reason why I chose JSON API was the structured error information it provides.

\n\n

Perhaps this would be more valuable where you expect to have a high proportion of novice users (e.g. a public facing API), and so you might expect the server to respond with a high proportion of rejections. The things I’m working on right now will only be available internally / to selected partners so this is less of a worry for me.

", - "post_number": 28, - "post_type": 1, - "updated_at": "2016-04-13T23:09:13.586Z", - "reply_count": 0, - "reply_to_post_number": 25, - "quote_count": 1, - "avg_time": 14, - "incoming_link_count": 9, - "reads": 12, - "score": 48.1, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "http://jsonapi.org/format/#error-objects", - "internal": false, - "reflection": false, - "title": "JSON API — Latest Specification (v1.0)", - "clicks": 2 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2786, - "name": "Alan Crosswell", - "username": "n2ygk", - "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png", - "created_at": "2018-03-26T15:47:20.859Z", - "cooked": "

I’m wondering if it’s time to revisit this idea now that OAS 3.0 is out there and it seems to be where the world of API modeling and documentation is converging. Unlike Swagger 2.0, OAS 3.0 is able to represent jsonapi thanks to addition of some missing json-schema constructs.

\n

I’ve been working a bit with django-restframework-json-api and it seems the DRF OPTIONS metadata would be a good place to plug this in as a proof of concept.

", - "post_number": 29, - "post_type": 1, - "updated_at": "2018-03-26T15:47:20.859Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 220, - "incoming_link_count": 0, - "reads": 4, - "score": 16.8, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Alan Crosswell", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "https://www.openapis.org/", - "internal": false, - "reflection": false, - "title": "Home - OpenAPI Initiative", - "clicks": 1 - }, - { - "url": "http://www.django-rest-framework.org/api-guide/metadata/", - "internal": false, - "reflection": false, - "title": "Metadata - Django REST framework", - "clicks": 1 - }, - { - "url": "https://blog.readme.io/an-example-filled-guide-to-swagger-3-2/", - "internal": false, - "reflection": false, - "title": "A Visual Guide to What's New in Swagger 3.0", - "clicks": 1 - } - ], - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 563, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2787, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2018-03-27T06:05:35.616Z", - "cooked": "

I still think this is a marvellous idea. Back when I started this thread I was hoping to get some community feedback on what a proper setup would be, but I have since realised, you don’t need it (the feedback I mean).

\n

If you are working on a proof of concept congratulations \":tada:\"
\nYou now have an amazing project to give a talk about (at almost any tech conference since APIs are language agnostic)

\n

If you write a blog post to share what you’ve done, please link it, I’d love to read it!

\n

Some hidden benefits of this include:
\nAutomatic rendering of resource “forms”
\nOTA(over the air) updates of resources without the need to update clients
\nClient side validation + single source of truth for these validations

", - "post_number": 30, - "post_type": 1, - "updated_at": "2018-03-27T06:05:35.616Z", - "reply_count": 0, - "reply_to_post_number": 29, - "quote_count": 0, - "avg_time": 165, - "incoming_link_count": 0, - "reads": 3, - "score": 8.85, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "n2ygk", - "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - } - ], - "stream": [ - 939, - 1006, - 1018, - 1020, - 1033, - 1035, - 1039, - 1042, - 1043, - 1044, - 1045, - 1046, - 1047, - 1048, - 1050, - 1051, - 1052, - 1054, - 1056, - 1057, - 1058, - 1059, - 1060, - 1061, - 1062, - 1063, - 1064, - 1065, - 2786, - 2787 - ] - }, - "timeline_lookup": [ - [ - 1, - 868 - ], - [ - 2, - 854 - ], - [ - 3, - 853 - ], - [ - 7, - 852 - ], - [ - 10, - 851 - ], - [ - 29, - 139 - ] - ], - "suggested_topics": [ - { - "id": 1158, - "title": "Relationships within abstract relationship", - "fancy_title": "Relationships within abstract relationship", - "slug": "relationships-within-abstract-relationship", - "posts_count": 14, - "reply_count": 6, - "highest_post_number": 14, - "image_url": null, - "created_at": "2017-11-20T23:01:24.465Z", - "last_posted_at": "2018-03-15T05:04:58.419Z", - "bumped": true, - "bumped_at": "2018-03-15T05:04:58.419Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 2, - "views": 1106, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 513, - "username": "a-komarev", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-komarev/{size}/440_1.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - } - ] - }, - { - "id": 1164, - "title": "Complex Nested Arrays", - "fancy_title": "Complex Nested Arrays", - "slug": "complex-nested-arrays", - "posts_count": 1, - "reply_count": 0, - "highest_post_number": 1, - "image_url": null, - "created_at": "2017-12-05T18:40:58.012Z", - "last_posted_at": "2017-12-05T18:40:58.106Z", - "bumped": true, - "bumped_at": "2017-12-06T20:47:19.587Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 0, - "views": 532, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": "latest single", - "description": "Original Poster, Most Recent Poster", - "user": { - "id": 581, - "username": "Wesley", - "avatar_template": "https://avatars.discourse.org/v2/letter/w/eb9ed0/{size}.png" - } - } - ] - }, - { - "id": 1193, - "title": "Is it allowed to have an attribute called \"links\" inside \"attributes\"?", - "fancy_title": "Is it allowed to have an attribute called “links” inside “attributes”?", - "slug": "is-it-allowed-to-have-an-attribute-called-links-inside-attributes", - "posts_count": 3, - "reply_count": 1, - "highest_post_number": 3, - "image_url": null, - "created_at": "2018-01-25T16:59:15.093Z", - "last_posted_at": "2018-01-25T17:42:40.319Z", - "bumped": true, - "bumped_at": "2018-01-25T17:42:40.319Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 0, - "views": 378, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": "latest", - "description": "Original Poster, Most Recent Poster", - "user": { - "id": 341, - "username": "Buju", - "avatar_template": "/user_avatar/discuss.jsonapi.org/buju/{size}/321_1.png" - } - }, - { - "extras": null, - "description": "Frequent Poster", - "user": { - "id": 15, - "username": "dgeb", - "avatar_template": "/user_avatar/discuss.jsonapi.org/dgeb/{size}/17_1.png" - } - } - ] - }, - { - "id": 1194, - "title": "Auth Header Question", - "fancy_title": "Auth Header Question", - "slug": "auth-header-question", - "posts_count": 2, - "reply_count": 0, - "highest_post_number": 2, - "image_url": null, - "created_at": "2018-01-30T19:32:48.514Z", - "last_posted_at": "2018-01-31T05:48:56.600Z", - "bumped": true, - "bumped_at": "2018-01-31T05:48:56.600Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 0, - "views": 373, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 581, - "username": "Wesley", - "avatar_template": "https://avatars.discourse.org/v2/letter/w/eb9ed0/{size}.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - } - ] - }, - { - "id": 1137, - "title": "303 response on POSTing resource without conflicts", - "fancy_title": "303 response on POSTing resource without conflicts", - "slug": "303-response-on-posting-resource-without-conflicts", - "posts_count": 8, - "reply_count": 3, - "highest_post_number": 8, - "image_url": null, - "created_at": "2017-10-23T01:07:28.611Z", - "last_posted_at": "2017-10-26T13:29:35.909Z", - "bumped": true, - "bumped_at": "2017-10-26T13:29:35.909Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 0, - "views": 773, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 513, - "username": "a-komarev", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-komarev/{size}/440_1.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - } - ] - } - ], - "id": 407, - "title": "[RFC] Standardising an OPTIONS response on a server that supports jsonapi", - "fancy_title": "[RFC] Standardising an OPTIONS response on a server that supports jsonapi", - "posts_count": 30, - "created_at": "2016-03-28T07:43:44.543Z", - "views": 3393, - "reply_count": 23, - "like_count": 0, - "last_posted_at": "2018-03-27T06:05:35.616Z", - "visible": true, - "closed": false, - "archived": false, - "has_summary": false, - "archetype": "regular", - "slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "category_id": 1, - "word_count": 3807, - "deleted_at": null, - "user_id": 252, - "featured_link": null, - "pinned_globally": false, - "pinned_at": null, - "pinned_until": null, - "draft": null, - "draft_key": "topic_407", - "draft_sequence": null, - "unpinned": null, - "pinned": false, - "details": { - "created_by": { - "id": 252, - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "last_poster": { - "id": 252, - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "participants": [ - { - "id": 252, - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "post_count": 12, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 251, - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "post_count": 11, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 259, - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", - "post_count": 5, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 49, - "username": "lukasoppermann", - "avatar_template": "/user_avatar/discuss.jsonapi.org/lukasoppermann/{size}/178_1.png", - "post_count": 1, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 563, - "username": "n2ygk", - "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png", - "post_count": 1, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - } - ], - "links": [ - { - "url": "https://tools.ietf.org/html/rfc6570", - "title": "RFC 6570 - URI Template", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 11, - "user_id": 252, - "domain": "tools.ietf.org", - "root_domain": "ietf.org" - }, - { - "url": "http://jsonapi.org/format/#fetching-filtering", - "title": "JSON API — Latest Specification (v1.0)", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 9, - "user_id": 251, - "domain": "jsonapi.org", - "root_domain": "jsonapi.org" - }, - { - "url": "http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven", - "title": "REST APIs must be hypertext-driven » Untangled", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 8, - "user_id": 252, - "domain": "roy.gbiv.com", - "root_domain": "gbiv.com" - }, - { - "url": "http://www.iana.org/assignments/link-relations/link-relations.xhtml", - "title": "Link Relations", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 5, - "user_id": 251, - "domain": "www.iana.org", - "root_domain": "iana.org" - }, - { - "url": "https://github.com/apiaryio/dredd", - "title": "GitHub - apiaryio/dredd: Language-agnostic HTTP API Testing Framework", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 2, - "user_id": 251, - "domain": "github.com", - "root_domain": "github.com" - }, - { - "url": "http://jsonapi.org/format/#error-objects", - "title": "JSON API — Latest Specification (v1.0)", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 2, - "user_id": 251, - "domain": "jsonapi.org", - "root_domain": "jsonapi.org" - }, - { - "url": "https://www.ietf.org/rfc/rfc2616.txt", - "title": null, - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 2, - "user_id": 252, - "domain": "www.ietf.org", - "root_domain": "ietf.org" - }, - { - "url": "https://blog.readme.io/an-example-filled-guide-to-swagger-3-2/", - "title": "A Visual Guide to What's New in Swagger 3.0", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 1, - "user_id": 563, - "domain": "blog.readme.io", - "root_domain": "readme.io" - }, - { - "url": "https://www.openapis.org/", - "title": "Home - OpenAPI Initiative", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 1, - "user_id": 563, - "domain": "www.openapis.org", - "root_domain": "openapis.org" - }, - { - "url": "http://www.django-rest-framework.org/api-guide/metadata/", - "title": "Metadata - Django REST framework", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 1, - "user_id": 563, - "domain": "www.django-rest-framework.org", - "root_domain": "django-rest-framework.org" - }, - { - "url": "https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html", - "title": "GraphQL Introduction | React", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 1, - "user_id": 251, - "domain": "facebook.github.io", - "root_domain": "facebook.github.io" - } - ], - "notification_level": 1, - "can_flag_topic": false - }, - "current_post_number": 24, - "highest_post_number": 30, - "deleted_by": null, - "actions_summary": [ - { - "id": 4, - "count": 0, - "hidden": false, - "can_act": false - }, - { - "id": 8, - "count": 0, - "hidden": false, - "can_act": false - }, - { - "id": 7, - "count": 0, - "hidden": false, - "can_act": false - } - ], - "chunk_size": 20, - "bookmarked": null, - "topic_timer": null, - "message_bus_last_id": 0, - "participant_count": 5, - "can_vote": false, - "vote_count": null, - "user_voted": false -} diff --git a/example/1353.discuss.jsonapi.org.json b/example/1353.discuss.jsonapi.org.json deleted file mode 100644 index 94b2cf9a..00000000 --- a/example/1353.discuss.jsonapi.org.json +++ /dev/null @@ -1,1499 +0,0 @@ -{ - "post_stream": { - "posts": [ - { - "id": 2150, - "name": "", - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", - "created_at": "2017-07-06T01:56:25.763Z", - "cooked": "

I’ve been reading the specs over and over again.

\n

If an article has many comments,

\n

1.) are you supposed to POST to /comments where in body, you have a relationship to article?

\n

or

\n

2.) are you supposed to POST to /articles/33/comments?

\n

and even if you do the 2nd method, do you also put the relationship to the article even if it is in the url?

", - "post_number": 1, - "post_type": 1, - "updated_at": "2017-07-06T01:56:25.763Z", - "reply_count": 0, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 43, - "incoming_link_count": 162, - "reads": 26, - "score": 822.35, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [ - { - "id": 2, - "count": 1 - } - ], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 502, - "hidden": false, - "trust_level": 0, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2154, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-06T19:14:50.492Z", - "cooked": "

Currently, the spec is pretty specific about what is supposed to happen in this case.

\n

If you scroll down to the POST request portion at Update to-many relationships, you’ll notice the /resource/{id}/relationships/{rel-name} link requires a resource identifier object to be POSTed to add itself to the set of resources related to the primary resource through the rel-name.

\n

This unfortunately, seems to be a 2 step process based on the current spec, while some contributors claim you can use the convention of /resource/{id}/{rel-name} to directly post a member of the relationship, this is unintuitive and hackey and most certainly not in the spec as far as I can see. It also breaks the hypermedia driven paradigm by requiring you to implement stateful information in the URL.

\n

The ideal solution would be for them to allow you to post a ‘resource object’ to the resource relationship link directly, and require the server to create the resource if it is of the correct type and then add the resource to the relationship set, following the conventional error handling process described in the spec.

", - "post_number": 2, - "post_type": 1, - "updated_at": "2017-07-06T19:15:23.157Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 55, - "incoming_link_count": 0, - "reads": 26, - "score": 12.95, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "http://jsonapi.org/format/#crud-updating-to-many-relationships", - "internal": false, - "reflection": false, - "title": "JSON API — Latest Specification (v1.0)", - "clicks": 25 - } - ], - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2156, - "name": "", - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "created_at": "2017-07-06T20:07:36.449Z", - "cooked": "

Nested relationships are a common feature of RESTful APIs. I don’t see anything un-intuitive about posting to /resource/{id}/{rel-name}, or even having an entire set of RESTful endpoints there.

", - "post_number": 3, - "post_type": 1, - "updated_at": "2017-07-06T20:07:36.449Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 47, - "incoming_link_count": 0, - "reads": 27, - "score": 12.75, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 503, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2158, - "name": "", - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", - "created_at": "2017-07-06T21:20:23.566Z", - "cooked": "

Thanks,

\n

Not sure if I want to follow the specs to the dot, or do what is “familiar” to non json-api people.

", - "post_number": 4, - "post_type": 1, - "updated_at": "2017-07-06T21:20:23.566Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 28, - "incoming_link_count": 3, - "reads": 27, - "score": 26.8, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 502, - "hidden": false, - "trust_level": 0, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2159, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-06T21:52:48.297Z", - "cooked": "\n

That’s understandable, but those aren’t RESTful APIs. Those are likely CRUD APIs, and json-api is about enhancing and pushing CRUD api users into a more hypermedia driven space.

\n

URL resource partitioning is easy in the short term and handicaps the API designer and consumer in the long term.

\n\n

/giphy Do or do not, there is no try.

\n

Having said that, I did actually just put up a suggestion on GH about solving this issue here.

\n

If you are going to follow the spec, follow the spec because ultimately the people who benefit from the use of the spec are your users. Snowflake or partial implementations which go outside the bounds of ‘unconditionally compliant’ and ‘conditionally compliant’ to the specification actually hurt the consumer in the long run, as their abstractions for the structure aren’t complete, and they can’t use nice tools like Katharsis to operate against your service.

\n

Back on the initial question, as I posted in github issue, I believe there is a better path than your backreference approach which is currently supported by the specification. Namely you POST a compound document with the user defined ID ‘resource identifier object’ to the relationships link /resource/{id}/relationships/{rel-name} with the new resource in the included section.

\n

However, I think the direct POST / PATCH would be an easier way to implement a client long term.

", - "post_number": 5, - "post_type": 1, - "updated_at": "2017-07-06T21:57:26.587Z", - "reply_count": 1, - "reply_to_post_number": 3, - "quote_count": 2, - "avg_time": 35, - "incoming_link_count": 2, - "reads": 28, - "score": 37.35, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "https://github.com/json-api/json-api/issues/1158#issuecomment-313527090", - "internal": false, - "reflection": false, - "title": "Creating new relationship · Issue #1158 · json-api/json-api · GitHub", - "clicks": 21 - }, - { - "url": "https://github.com/katharsis-project/katharsis-framework", - "internal": false, - "reflection": false, - "title": "GitHub - katharsis-project/katharsis-framework: Katharsis adds powerful layer for RESTful endpoints providing implementenation of JSON:API standard", - "clicks": 8 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" - }, - "actions_summary": [ - { - "id": 2, - "count": 1 - } - ], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2160, - "name": "", - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", - "created_at": "2017-07-06T22:00:07.189Z", - "cooked": "

Thanks, do you guys know of any public apis that implement json api spec perfectly? one with “creating” one to many relationships ?

", - "post_number": 6, - "post_type": 1, - "updated_at": "2017-07-06T22:00:07.189Z", - "reply_count": 0, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 22, - "incoming_link_count": 0, - "reads": 24, - "score": 5.9, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 502, - "hidden": false, - "trust_level": 0, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2162, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-06T22:16:26.863Z", - "cooked": "

That might be a better question for @steveklabnik or @dgeb. However, is there a reason you are implementing this all yourself, and not using a library which has done a lot of this heavy lifting for you in your language of choice?

", - "post_number": 7, - "post_type": 1, - "updated_at": "2017-07-06T22:16:26.863Z", - "reply_count": 0, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 21, - "incoming_link_count": 0, - "reads": 24, - "score": 5.85, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2163, - "name": "", - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "created_at": "2017-07-06T22:18:03.058Z", - "cooked": "\n

This is only an option if you use UUIDs for the id field, which not everyone does nor should they.

\n

AFAICT using backreferences in the URL is a valid interpretation of the spec, and as long as it is properly linked, completely valid HATEOAS.

", - "post_number": 8, - "post_type": 1, - "updated_at": "2017-07-06T22:18:03.058Z", - "reply_count": 1, - "reply_to_post_number": 5, - "quote_count": 1, - "avg_time": 25, - "incoming_link_count": 0, - "reads": 24, - "score": 11.05, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 503, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2164, - "name": "", - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", - "created_at": "2017-07-06T22:32:52.281Z", - "cooked": "

I’m using phoenix and elixir and ja_serializer,

\n

it does basics of converting JSON attributes/relationships to Ecto friendly data, but you still have to setup and design the Urls yourself

", - "post_number": 9, - "post_type": 1, - "updated_at": "2017-07-06T22:32:52.281Z", - "reply_count": 0, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 27, - "incoming_link_count": 1, - "reads": 23, - "score": 10.95, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 502, - "hidden": false, - "trust_level": 0, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2166, - "name": "", - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", - "created_at": "2017-07-07T00:34:50.422Z", - "cooked": "

do you guys know of any api documentation that shows a proper json spec implementation?

\n

for example this is for angellist
\nhttps://market.mashape.com/community/angellist

\n

but it is doing the CRUD method

", - "post_number": 10, - "post_type": 1, - "updated_at": "2017-07-07T00:34:50.422Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 32, - "incoming_link_count": 2, - "reads": 23, - "score": 21.2, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "https://market.mashape.com/community/angellist", - "internal": false, - "reflection": false, - "title": "AngelList API Documentation", - "clicks": 17 - } - ], - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 502, - "hidden": false, - "trust_level": 0, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2167, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-07T14:30:15.674Z", - "cooked": "\n

I’m not sure I understand why you make this assertion at all, and identifier is meant to be a key value, why would the format of the identifier matter in any way where a user should not use it? I’ll let you make your arguments for this statement if you chose before I comment.

\n

As far as the back references are concerned, from what I see you are correct. I previously stated exactly that, the backreference method was a valid approach under the specification, but I did make the claim it required a bit of mental gymnastics. I believe this claim is both true and a valid criticism of its usability. The compound document approach is also currently valid within the specification, and far more straightforward.

\n

For the sake of concisely defining the behavior of the specification it isn’t in our collective best interests to dilute the means operating against a server running a json-api service, however I do think as far as semantic sugar is concerned the best approach would be to simply allow the inclusion of a ‘resource object’ in the POST and PATCH collections. My previous paragraph, would of course enumerate the way you COULD do the same now albeit in a more verbose, and constrained fashion.

", - "post_number": 11, - "post_type": 1, - "updated_at": "2017-07-07T14:30:15.674Z", - "reply_count": 0, - "reply_to_post_number": 8, - "quote_count": 1, - "avg_time": 31, - "incoming_link_count": 3, - "reads": 20, - "score": 20.55, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2176, - "name": "", - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "created_at": "2017-07-13T03:15:05.076Z", - "cooked": "

You’ll find they’re quite similar, except unlike CRUD, we’re abstracting things a bit more. You’ll use links, relationships, and sub-resources to help abstract away things like pivot tables and foreign keys. The hardest part is not getting hung up on your data representation and coming up with more elegant ways to represent your data.

\n

There isn’t a “wrong” or “right” way to do it as long as the spec is followed. JSONAPI is more of a standardized set of tools to accomplish these tasks. Welcome to the real difficult part of programming: systems design.

", - "post_number": 12, - "post_type": 1, - "updated_at": "2017-07-13T03:15:05.076Z", - "reply_count": 1, - "reply_to_post_number": 10, - "quote_count": 0, - "avg_time": 29, - "incoming_link_count": 0, - "reads": 20, - "score": 10.45, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 503, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2181, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-13T15:45:23.717Z", - "cooked": "

While I’m very aware of the issues you bring up when designing a hypermedia driven service and agree with the overall premise that system design is the most important and complicated part of the implementation, you still haven’t answered my question. Why “shouldn’t” people use UUID’s? You made no mention of any negative property of their use to recommend against it.

\n

The traditional incremented integer ‘best practice’ is always a worse decision for security and cohesion concerns.

\n

As far as the other methods go, I used the term ‘semantic sugar’ to reference ‘syntactic sugar’ meaning it was functionally equivalent but a more intuitive process.

\n

The proposed modification approach is slightly better than the compound document approach which is markedly better than the back reference approach, when comparing their relative intuitiveness.

", - "post_number": 13, - "post_type": 1, - "updated_at": "2017-07-13T15:45:23.717Z", - "reply_count": 0, - "reply_to_post_number": 12, - "quote_count": 0, - "avg_time": 25, - "incoming_link_count": 0, - "reads": 21, - "score": 5.45, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2248, - "name": "", - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "created_at": "2017-07-28T04:53:59.584Z", - "cooked": "

@michaelhibay My last post referring to system design was directed at @mikeni and designed to re-align this thread on topic. I’m not going to wreck this poor guys inquiry with pointless bickering.

\n

I say UUIDs are a dumb decision and I don’t like the compound doc approach. You say incremented integers are flawed and don’t like backreferences. Ultimately, he must make the design decision best for his project.

", - "post_number": 14, - "post_type": 1, - "updated_at": "2017-07-28T04:53:59.584Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 30, - "incoming_link_count": 0, - "reads": 21, - "score": 10.7, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 503, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2251, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-28T14:21:24.820Z", - "cooked": "

I apologize if my response came off as bikeshedding in your mind. While I feel I am well versed in many subjects, I heartily welcome every opportunity to learn and was very interested to know your thoughts. I was challenging the assertion of UUID usage being a mistake in situations as a chance to learn why you would say this.

\n

The back-reference solution is approaching a bike-shedding discussion which would be better served as a specification level discussion rather than an implementation one to be sure. However, the point is certainly somewhat moot if and when the use of hypermedia can obviate the URL, but hypermedia is unfortunately not a requirement and my statement was the spec should offer limited solutions to the requirement to keep the interaction and documents as uniform as possible.

", - "post_number": 15, - "post_type": 1, - "updated_at": "2017-07-28T14:21:24.820Z", - "reply_count": 1, - "reply_to_post_number": 14, - "quote_count": 0, - "avg_time": 25, - "incoming_link_count": 0, - "reads": 22, - "score": 10.65, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2252, - "name": "", - "username": "cam", - "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png", - "created_at": "2017-07-28T14:49:56.646Z", - "cooked": "

Michael, in the “Update to-many relationships” of the spec, can you please put an example of a POST with a NEW comment (meaning how the URL and json would look like)? From your statement above, it sounds like adding a NEW comment is a two step process. Are you indicating that you would have to first do a POST to /comments and then a POST to /articles/1/relationships/comments? I started a new thread yesterday about this issue with foreign keys and this does not work in that situation. I’m trying to understand the correct way to deal with foreign keys and updating to many relationships.

\n

The more examples, the easier it is to understand what one should do in different scenarios. That would be very helpful. Thank you.
\n–Cam–

", - "post_number": 16, - "post_type": 1, - "updated_at": "2017-07-28T14:56:42.102Z", - "reply_count": 2, - "reply_to_post_number": 2, - "quote_count": 0, - "avg_time": 29, - "incoming_link_count": 2, - "reads": 21, - "score": 25.65, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 2, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 499, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2254, - "name": "", - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "created_at": "2017-07-28T17:01:02.162Z", - "cooked": "

You can do this with one request using a compound document posted to /comments. I agree, though, there needs to be more use cases of this in action. You would POST the following, and the relationships would be created along with the object.

\n

http://jsonapi.org/format/#document-compound-documents

", - "post_number": 17, - "post_type": 1, - "updated_at": "2017-07-28T17:04:00.964Z", - "reply_count": 0, - "reply_to_post_number": 16, - "quote_count": 0, - "avg_time": 31, - "incoming_link_count": 0, - "reads": 20, - "score": 5.55, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "http://jsonapi.org/format/#document-compound-documents", - "internal": false, - "reflection": false, - "clicks": 25 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "cam", - "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 503, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2255, - "name": "", - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "created_at": "2017-07-28T17:10:57.711Z", - "cooked": "

Until I have a measurably better suggestion, I won’t complain about it anymore. I don’t have numbers to back up any claim that I’d make, and all my complaints would be performance related.

\n

I’ve been researching alternatives, and if I get around to benchmarking them I’ll post the results in a new thread.

", - "post_number": 18, - "post_type": 1, - "updated_at": "2017-07-28T17:10:57.711Z", - "reply_count": 1, - "reply_to_post_number": 15, - "quote_count": 0, - "avg_time": 26, - "incoming_link_count": 1, - "reads": 20, - "score": 15.3, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 503, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2256, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-28T17:32:34.478Z", - "cooked": "

There’s no need to carry this discussion in multiple threads. I responded to the thread you created, and we can continue there.

\n\n

I’d be interested in seeing those results. Despite what they reinforce in college, performance related issues which are outside the bounds of algorithmic complexity are generally best addressed when they show signs of being a problem. I regularly have to fight the urge of premature optimization, so I understand where you are coming from.

", - "post_number": 19, - "post_type": 1, - "updated_at": "2017-07-28T17:32:34.478Z", - "reply_count": 0, - "reply_to_post_number": 16, - "quote_count": 1, - "avg_time": 34, - "incoming_link_count": 4, - "reads": 21, - "score": 25.9, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "http://discuss.jsonapi.org/t/posting-related-data/1054/3", - "internal": true, - "reflection": false, - "title": "POSTing related data", - "clicks": 30 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "cam", - "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - } - ], - "stream": [ - 2150, - 2154, - 2156, - 2158, - 2159, - 2160, - 2162, - 2163, - 2164, - 2166, - 2167, - 2176, - 2181, - 2248, - 2251, - 2252, - 2254, - 2255, - 2256 - ] - }, - "timeline_lookup": [ - [ - 1, - 403 - ], - [ - 2, - 402 - ], - [ - 11, - 401 - ], - [ - 12, - 396 - ], - [ - 13, - 395 - ], - [ - 14, - 381 - ], - [ - 15, - 380 - ] - ], - "suggested_topics": [ - { - "id": 1121, - "title": "Is it valid for a server to return a response with no body?", - "fancy_title": "Is it valid for a server to return a response with no body?", - "slug": "is-it-valid-for-a-server-to-return-a-response-with-no-body", - "posts_count": 3, - "reply_count": 0, - "highest_post_number": 3, - "image_url": null, - "created_at": "2017-10-06T15:09:32.969Z", - "last_posted_at": "2017-10-06T18:49:15.183Z", - "bumped": true, - "bumped_at": "2017-10-06T18:49:15.183Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 3, - "views": 569, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 565, - "username": "bundacia", - "avatar_template": "/user_avatar/discuss.jsonapi.org/bundacia/{size}/496_1.png" - } - }, - { - "extras": null, - "description": "Frequent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 563, - "username": "n2ygk", - "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png" - } - } - ] - }, - { - "id": 1150, - "title": "Status of Versioning - What's the silver bullet?", - "fancy_title": "Status of Versioning - What’s the silver bullet?", - "slug": "status-of-versioning-whats-the-silver-bullet", - "posts_count": 1, - "reply_count": 0, - "highest_post_number": 1, - "image_url": null, - "created_at": "2017-11-03T10:15:37.624Z", - "last_posted_at": "2017-11-03T10:15:37.685Z", - "bumped": true, - "bumped_at": "2017-11-03T10:15:37.685Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 0, - "views": 113, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": "latest single", - "description": "Original Poster, Most Recent Poster", - "user": { - "id": 572, - "username": "mblackritter", - "avatar_template": "/user_avatar/discuss.jsonapi.org/mblackritter/{size}/498_1.png" - } - } - ] - }, - { - "id": 1192, - "title": "Returning page meta data", - "fancy_title": "Returning page meta data", - "slug": "returning-page-meta-data", - "posts_count": 2, - "reply_count": 0, - "highest_post_number": 2, - "image_url": null, - "created_at": "2018-01-25T15:34:35.664Z", - "last_posted_at": "2018-03-08T13:37:46.651Z", - "bumped": true, - "bumped_at": "2018-03-08T13:37:46.651Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 1, - "views": 416, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 599, - "username": "marceloverdijk", - "avatar_template": "/user_avatar/discuss.jsonapi.org/marceloverdijk/{size}/521_1.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - } - ] - }, - { - "id": 1194, - "title": "Auth Header Question", - "fancy_title": "Auth Header Question", - "slug": "auth-header-question", - "posts_count": 2, - "reply_count": 0, - "highest_post_number": 2, - "image_url": null, - "created_at": "2018-01-30T19:32:48.514Z", - "last_posted_at": "2018-01-31T05:48:56.600Z", - "bumped": true, - "bumped_at": "2018-01-31T05:48:56.600Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 0, - "views": 373, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 581, - "username": "Wesley", - "avatar_template": "https://avatars.discourse.org/v2/letter/w/eb9ed0/{size}.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - } - ] - }, - { - "id": 1065, - "title": "Idea: Request Cycle Library", - "fancy_title": "Idea: Request Cycle Library", - "slug": "idea-request-cycle-library", - "posts_count": 7, - "reply_count": 3, - "highest_post_number": 7, - "image_url": null, - "created_at": "2017-08-13T19:03:54.180Z", - "last_posted_at": "2017-09-02T15:38:20.661Z", - "bumped": true, - "bumped_at": "2017-09-02T15:38:20.661Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 1, - "views": 764, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 503, - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" - } - }, - { - "extras": null, - "description": "Frequent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 61, - "username": "beauby", - "avatar_template": "https://avatars.discourse.org/v2/letter/b/f475e1/{size}.png" - } - } - ] - } - ], - "id": 1020, - "title": "POST /comments with relationship or POST /articles/33/comments", - "fancy_title": "POST /comments with relationship or POST /articles/33/comments", - "posts_count": 19, - "created_at": "2017-07-06T01:56:25.691Z", - "views": 2026, - "reply_count": 10, - "like_count": 2, - "last_posted_at": "2017-07-28T17:32:34.478Z", - "visible": true, - "closed": false, - "archived": false, - "has_summary": false, - "archetype": "regular", - "slug": "post-comments-with-relationship-or-post-articles-33-comments", - "category_id": 1, - "word_count": 2562, - "deleted_at": null, - "user_id": 502, - "featured_link": null, - "pinned_globally": false, - "pinned_at": null, - "pinned_until": null, - "draft": null, - "draft_key": "topic_1020", - "draft_sequence": null, - "unpinned": null, - "pinned": false, - "details": { - "created_by": { - "id": 502, - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png" - }, - "last_poster": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - }, - "participants": [ - { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "post_count": 7, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 503, - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "post_count": 6, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 502, - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", - "post_count": 5, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 499, - "username": "cam", - "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png", - "post_count": 1, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - } - ], - "links": [ - { - "url": "http://discuss.jsonapi.org/t/posting-related-data/1054/3", - "title": "POSTing related data", - "internal": true, - "attachment": false, - "reflection": false, - "clicks": 30, - "user_id": 415, - "domain": "discuss.jsonapi.org", - "root_domain": "jsonapi.org" - }, - { - "url": "http://jsonapi.org/format/#crud-updating-to-many-relationships", - "title": "JSON API — Latest Specification (v1.0)", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 25, - "user_id": 415, - "domain": "jsonapi.org", - "root_domain": "jsonapi.org" - }, - { - "url": "http://jsonapi.org/format/#document-compound-documents", - "title": null, - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 25, - "user_id": 503, - "domain": "jsonapi.org", - "root_domain": "jsonapi.org" - }, - { - "url": "https://github.com/json-api/json-api/issues/1158#issuecomment-313527090", - "title": "Creating new relationship · Issue #1158 · json-api/json-api · GitHub", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 21, - "user_id": 415, - "domain": "github.com", - "root_domain": "github.com" - }, - { - "url": "https://market.mashape.com/community/angellist", - "title": "AngelList API Documentation", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 17, - "user_id": 502, - "domain": "market.mashape.com", - "root_domain": "mashape.com" - }, - { - "url": "https://github.com/katharsis-project/katharsis-framework", - "title": "GitHub - katharsis-project/katharsis-framework: Katharsis adds powerful layer for RESTful endpoints providing implementenation of JSON:API standard", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 8, - "user_id": 415, - "domain": "github.com", - "root_domain": "github.com" - } - ], - "notification_level": 1, - "can_flag_topic": false - }, - "current_post_number": 1, - "highest_post_number": 19, - "deleted_by": null, - "actions_summary": [ - { - "id": 4, - "count": 0, - "hidden": false, - "can_act": false - }, - { - "id": 8, - "count": 0, - "hidden": false, - "can_act": false - }, - { - "id": 7, - "count": 0, - "hidden": false, - "can_act": false - } - ], - "chunk_size": 20, - "bookmarked": null, - "topic_timer": null, - "message_bus_last_id": 6, - "participant_count": 4, - "can_vote": false, - "vote_count": null, - "user_voted": false -} From 8821b9908b8fc0d4c1be401f147765b185b0eef5 Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Tue, 21 Aug 2018 07:59:42 -0400 Subject: [PATCH 4/8] correct running example app documentation (#457) --- docs/getting-started.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index d68bbdd3..49bd64b7 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -68,15 +68,15 @@ From Source ## Running the example app - git clone https://github.com/django-json-api/django-rest-framework-json-api.git - cd django-rest-framework-json-api - python -m venv env - source env/bin/activate - pip install -r example/requirements.txt + git clone https://github.com/django-json-api/django-rest-framework-json-api.git + cd django-rest-framework-json-api + python3 -m venv env + source env/bin/activate + pip install -r example/requirements.txt pip install -e . - django-admin.py startproject example . - python manage.py migrate - python manage.py runserver + django-admin migrate --settings=example.settings + django-admin runserver --settings=example.settings + Browse to http://localhost:8000 From 95e6d8db1e072a93122a0df7e5c5f26a8a0a1cdf Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Tue, 21 Aug 2018 15:48:25 -0400 Subject: [PATCH 5/8] Drf example fixtures (#462) * replace binary drf_example sqlite3 db file with fixture --- .gitignore | 3 + CHANGELOG.md | 1 + docs/getting-started.md | 1 + drf_example | Bin 80896 -> 0 bytes example/fixtures/drf_example.json | 124 ++++++++++++++++++++++++++++++ example/requirements.txt | 1 + 6 files changed, 130 insertions(+) delete mode 100644 drf_example create mode 100644 example/fixtures/drf_example.json diff --git a/.gitignore b/.gitignore index 1207cc48..6b952e8f 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,6 @@ pip-delete-this-directory.txt *.sw* manage.py .DS_Store + +# example database +drf_example diff --git a/CHANGELOG.md b/CHANGELOG.md index fc160067..c591958f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Add sorting configuration to `REST_FRAMEWORK` as defined in [json api spec](http://jsonapi.org/format/#fetching-sorting) * Add `HyperlinkedRelatedField` and `SerializerMethodHyperlinkedRelatedField`. See [usage docs](docs/usage.md#related-fields) * Add related urls support. See [usage docs](docs/usage.md#related-urls) +* Replaced binary `drf_example` sqlite3 db with a [fixture](example/fixtures/drf_example.yaml). See [usage docs](docs/usage.md#running-the-example-app). v2.5.0 - Released July 11, 2018 diff --git a/docs/getting-started.md b/docs/getting-started.md index 49bd64b7..26117e0b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -75,6 +75,7 @@ From Source pip install -r example/requirements.txt pip install -e . django-admin migrate --settings=example.settings + django-admin loaddata drf_example --settings=example.settings django-admin runserver --settings=example.settings diff --git a/drf_example b/drf_example deleted file mode 100644 index 2b54190f8873c12784558b18f8aac186b0c613d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80896 zcmeHQdvF}dS)ZO>X|=ZAvb<-@vPY6-c_T~Onc1g(m%Fts`(#`4*=OIK3}iFAJCax4 zhi-S}vz((YD_>pl2agI>yo#!XS5YCns|Zj$0wh$06z@Ptpm>u40s)dh5(+5b>zgSv=_4i^M#CG9dBRuEvf8#IXFXFG^f53l%KZpMo{}KK@{3-mK zMszDNoX!#82zVoK_A=0)B@OlI^AISr5XdPALJZ${I6KXKJl~SuizT~ zxcDXU!%sdAJco}v0{w${vO~Y66;}&-I$JKTYNhpkeK?LxnqI0^*3;Tr?RvRV75jSe zgfmLS8$!Dv^zX6BS*?^;^lWXg2hTK^4GEciS=@v16w0k=rR8#3%N6sbbfLWL>+i-P z7qb{_!>Y-dEthIwOS-ncs`m}F<4Hs$rdRb!F<-6b%Ow?e_TxhVN?oh!75x3eKr{OI z9|?`TsI9BJ6ORR$K~?Cr8$1I8pu3y(K=Gmsr| z7l`i@KO*4Q@z3J#z(0&XFa8ETfUn{kct-ppJ|%t*3*txcuj9{HcJN;ufrpR4Ufr_ces@7Z_K80x;J?FvjDHRP4E`bf?f89o1z*7@@iZP1|4;m9=nMQhu<(l` z@aPa2?DGjjBkVnAwg16h8`*0A{XIY)V%)4f_jlW9to`rr0@@_wbG84zPKevb;*4h8 zzuPH-wEx~+KnpT9+KTrCfO5!@5c(569Y7sqRMP(Y{5~N#>}db}?LJ{}gy^!`e`g!e z1__O{|1KZg1<4(4{{z@33=g^5f0qb?2OI5w2>FEZF?`7D`Kvh zBft@OU7@J4^YA513E zoo8!Wrl6CjX_`JxZNs#xH+A=^%d@E~v(lB+$qTd6s5jH7baXVI8nu90a8 zL0ao=k-j?tRsvH>NbP73o*h9#zLe8%RbMSY&ym!VGQFoYhG8UInGV!@{r=#{2&&(3 zA!+e+v_foKD3!FLJ}TWL-E^&TG!YJ2;9gyryL5Fn^n%zPoSa09)F76Rs{ZPlUdrmO zTkJ5+6?v4XItD32^+n_l&di_u0*3&&Y=Q0VR6So z(n4N`3YpVtdM#hn8=!?q`*(cRdtBNmw}T_V5qRJTkoiB}{vSBZTmeVmu|%K??S|&R zM-cXie3*K3{r;M5elGh}t@$lQa`|1&zqw)<{pZ-@5$Y|-25?9lXg*Oeu# zW+(RTbfZI1J}dWJMhLzs-Sbu7*XIwOPonw>+PSv+%J%U`I;U(*{ZcWrnglEcQZcZs%{y-{ZSw9|KD@# z{EtVrR+aU}--rsK)D8cT+|A@z78wVckE z^0mBH@Jtbp$0MrR)g}x>3ic@!RBoOq5|?9PrL#>K8c(s0GFzsR!)i=E4{Ig|zbQrD zb&w#=@^w=yTL95otq7?wVv{vv0FPcoo5|QURK*m{9 zM#L_I2qLpf>GEpLGb7P>G$BV%`9Z?GDM7VM(4oW&>7sTkUC>L*wd-D4iAL41tUlik zLS8n7m{rF1nxlqnAxVix!|~_a+Jy15DLae=MZmXMzS)46F49R8p?%azsZ`7C*G z#;3xuDLE<0%JHyrJPL0*IjYENv=0(a+7c>hYjuNHGBuKjCSzxM;R*4g`GiQ=pSO)C zw&OwBq+Sij67bd}dA0BA*+$-Z5{>{zU}qvg+P{e3E8w4kfL|N|j=+6I;DXpLq*4Qu zw$XtcJ|0dUkH)8E6|PvTm_+zwIg=@8o@1laX;`xO0!%ljQo)&~G?B!#0;6Ch(JCep zPZjc69Y(2H-7b;8|L<#Yb735T9fbh-{;!Ik7V!Vzuj8-4&iuc}e~teP_5%DK{(0C9 z@DcnY_=EU+@o(Xe<6ptwiN6)Uh2M{F!_L5O#4q6&@Hu=MzZ)m8f{)=6zK%1nU+|yA z&*LLl!u#JZLzZ5?wej1D7|A=4PQP6N>cO(LB7@=O3h6)X35_W}YI77o}6825e z@E8pzN!ar)8ir_il!V}t#MaWy=xD)EGB z|5yW^eJdswo|BgKnq+<#l9npvqC`99l2*z|RULxbb-gIn%F-#VQZ9(^p*{Yq0hhAy zaYdbuD6l|8Z4ncRsdLiGTD2y}m^UYgxl)X|_n z`0Ny_kGV2O9cEOW&O)euBVu1G%w6f+))~<**C4_k!-f%W8=7l?A?2wpY%tTcwt)c( zWjru!$Vp6k2tSWyeH58BwF*Y&h#GS%WLMMR{%}Yik?rK4?PqgZ+l5ms!hR zD&FCxC85UEj2yQ)R3Vnb)DJBAGtWLjEo-gHEcFUc7>>y9|Mg$ZW5)+X5NC7u5x^Z#vR%3l(`h<;rB zC|-9KLw#<Mek+eSo>|&Uac2b+?gTT(gYIbuw*zANHIL0Jh&@y_#YVxwH$rsDH zmeHa5)1&@iRz-E=OdAiw(+w5rcr24sb83dpAbYU-SRS2Ttvwi9$#fKw8=Hn5qwv%) zG8NkJkNL^THg{$%Pu5n;rVkD4cFb7>uGnqCZ@1C;5HycBvOmN;kYHNLgD?x_LxR@P1DsfKsYd@c>2r7~(fs#zZcEarC4Vsyc^xkTI3 z&yq{>>ITf7?@--0?hif#d6@FdgV~f_mh>@m?M~D!rHu#2U7JMD|MB6c1@QOrL0o_z z{Nf031nvrfX)Msu15|KFg0S5jc<(KKEfA18=-hNdE zhse{ShlWn)j(^jaSVW1)VdSZyIR*6>mPm$^ZIDy4{}--{F#pH*0N+)~b0&_!4o4sv zh782^k^a9=_^2R!6#b<52Y3JvJhXg0vu#cFwP}Aa6he2#X-}9rNMxs)I`4X%{a|P0 z_PW6p9Zpe%GgNCH1979bo?n8~7F#knd$v*MoCS53Z;fx1Xv8|_F|;Ag(1R%Nls)nd zr@1j!t^3_^ZdM#hOF6DGBM+TM>(#q49Sb2LAA7gmWk1A9+W@pX5|nt-p#$=3lJ8;hA~$xFW+T z8*)r`ow5;W#KaR_PFTK+%|2^jaj-vX56BFpI zF~>X+EjM$bh~6Cao++Ynk7laqI%{fk(Fn7cr1hoARFOGh1Ph&jyt5hR`-67Yc23 zMf|~d9NjV2vpC9$O(0i2iSOE^5Y2?!Dok1p?mjp6T0>S+Y&LD`g@(c3m@hTP|6dXC zf8np(WBups9088Njzgeb^a*e#i0DH;ArLUe|KR_dfBWBYCCLru2y71lvh@e{{|E?1 zd+_}qeLz4T5dGrs<7K?Or9|}JEm=6jwh^es{K4b|st?j-q0X*oEE0((mQ=+-onReP zwj|UMmo`%v3ql=nqzhq*tQ=@Hs=d4t!DDW_kPV>1z@>E(QmEhE4uBoKXFac@Tn3y@l1R0 zVyM+V9H-@J*?Av%{oVfHi4dwAeukZq>6{*oEW!MplN@zUk2xb7X^eR>XC$3Xv}Gn# zSD*6-$HvgclmqiTZ2F=ro6@k(Mvr@=4KYNX=O!i_x>D-um5Xx=AY*=Z;R;y{0n4PU zLUHX0%ax1Z?%0ZUZ0(VZDb`Rl)gGK1+X`^7&q5n+5_JAwfNlRzqkj;kK;~|8uV>Jw5nCBu#yb6hnaO(RuYaiD+R4uBYuepR%DTi zQ6r6oEBR`=x(4gdh!2)gSX)*uH0mnAzm%^)QfDEkww3`bRQRSGn{C|#0mOouwzSl& z9b$-kpPNtv+=h{Cq*uUSi4OU;@?75?} zEGAMAa5qZ#|B2{LLHsxb{CXq^Y@86==HF~f6(dXQx7Ax&?bVy{gOFJB5TzC5R1Jbmf4h10XIEu6h7Upu`RU07Kt%&%MxFRm2w*G``+ zUORnBo>ykog_YI#Tp@Wq_sZq+E9y(Puf1|^NjrPywsvYRHn(ykJb!i`(%x8KxH5NZ z;dG&}a0TR@eJMYG`^K$>mBsaID|3;BE7|C^m4)KM>0EyC_I&=rsdGzQP!ZDi;qJq*wdkj{l$j-`? zHCV?6fOUMvPc+*3h}H&^)DKK!lWj~j8B^QTl3S_I;9wd}4yKLV?(YST;qf-2$c!dR z$nVh}V#Jh>$)|@s*%Z+G(Qbo>X=v~;74-f<7w{aK@DUvi2BL)C9P5PGnRq+XV)>f1 zsj`U2b{pJGpA|v4nNo{*WEXIU#{XXCr9v7(=7EM*a?`hm7 z1mqJ;Jo6J;TVJ(nVN{8br$x%ol)3nsPCNfm1pJdxqT0nwEUjRP?H@ zRkGJtD`j$ohpqo)#z-tGK4T9_GQ<%}X71-`M#9-`RBM6|NoBNJ3oH`54jfmN-vZp$^J964g}F^x7p zrjb#kajLPKXtXgAji#i-RMKusBk{)J)JP~t#tc47qseD!Bot|sYTV5>`>P3`7sTnLG%wQ}F=PVhm)#rHCzO zY;X(2$zg+;sdF$hl@{~(0brhx14OTbljyeC4i6EwnNEiRP1+CdH@F;{3712y(f|KG z0e_!Yljbyz07u}FAfTg8VLlb;>goyvQbA2uGV)SZ1J8fSNK%jHU=378Q8EcVmyAZ^ zQFHz;tk^z}Dv@w9Zi```{*>vkszhRm6}_TU|9^=1|NHtQ$t73H5#R_sd;~ghFhJWs zdH?%_&j`Y2(6@+xic1gQln0=(9zFvHeZd;5#a3&q*c52vh^Tl*T7ZC-En?k(3_ zwUV{j+ACL@(a^@i96j92wb~B4(xhW+n#FEUdatu%v5#hv-Q7F&cORK(X3pr~GV6(J zE85%Mre~~Bz5kp)cnQweiaP25!-#dSL3mn^B(u>>wz1TXaZGxbd+YSh;u zP`&58KX?(0G7kPTb8ZSXGn`IDaa!Q<9+i!#V~>hfsj*pavxj9-PI>p8 zEe6s7WvSDvwH60b+4pjJd0AxwY@Jz908X#1eJJ+rMWOmD^RVOX1ll<3$cOpy!j%~p z`LuVATwD(*TXa(uxSlTC&S7_EZE3+cwHQ0H=E@Sgo%PO&6V2x1!R)2s-dR^GujaGf zu)`2zwOm*)mMg2*^VxKkth8|sH{3%NwgJ#SdNYd{whdl14kd=YdO7`;S@W3fX)jIb zjGmR%Y$EJpPI>q9?@E-j&u>G{%K+a0-&xN)ZY@XP{vyEp|MwS9E{!9wlMo>Ne-UQ{ z{4)^nizC1hxX%b&5dCnd(3H^FqhoxdhfTQA=yW8ZsA0wSjh>vP)QC}&XDTpgG!Oq7 z85t255RoRM#+IIjG&MRM2Co5O*IEFw+1H3kL@tzX>c*i#WHg#B=p)qsH=qB%&qdC~ zaRhb<0_6SA+y4$JEN&)8;65Y3+y8wAl#AmC>>vbq``YvY_@)i3G|8Si^J6Z%J>&gNo5ixv$M^P4+vV^xY;*Vt-?X!9 zl?p8IH1`2Izi4~8t27O^eJm!6RYUcuOR$U@R;xOfQ8UZa&3e*GOw+QNuxd(Vtj~FW z&%O&m&ac@v%$k+M8fy76xYj(6?ksv0OL}Rowgw)HSuPu={F+v{qMzTi*xbUlRg9)) zPjA6qt4eJ7Cj~^-FBqF^w_nEm{r{vsqBtLIUD-LdZ59Nf8vmFDmhMr`TCQaF*&B8_vX zTO_%#`RMsB6nKuJy@s&1;bdrI_lq6D`oMX#aq6xzW?By!v&5uhnOHQec(5Yza!o6FDUkmECh9ZurmLTPb@e z*iJ6_{$F=<=JyooQ`dPBOB&|zJQFFk2xDzr1XaCKX74E zsu6bF?L)(;;S}4~=R;#iWa-JldTj=Ur6iPgO0j$E_V4kb5W;3E<21w$gJ>o+h=Cp- zI)p6W=3V_hG>&{MCG~LLZID4HM`*le=jw%gC!;2@Ph`0 zB_oulNc-Q9{!kEA@tmkZ`+rmX9`VP-kBNUIeqQ`f+=-8fd+{lJ6|dko@CWdZiU|CH z{vrNl{0I0S&1%8_K1K-8s@2Pi*xJ>s8gy0}EV@GLZ7=mW+1NELAMl|hYT?K=yw``O zP{5Q#4|M=Xm%|3xRBVzD(gtXG(mX_~_%4>3`k|2w5=%)*BebI0J$;T+J9o2m(EBBB zk;V)nOUa0bdwpmG*-o6;H{e4PsMBHaeuL()c4(gusmRNj?6iRgli=D|Jwt7^`#~M2 zW_4S(lCQ!?45DQ#Ax_Y=cCW2RDO(Rq%LX(dgUHe{;=3r(a6X3&`g zi#|oi|9D-%pT!@BKm6hda0DI(0#hou=bcG5KdJ4Gd@D86ee;xJu(OtJJ;3cAeJ%D0 S8Q4Rr>$y&NsViZN@&5r)Myl}u diff --git a/example/fixtures/drf_example.json b/example/fixtures/drf_example.json new file mode 100644 index 00000000..498c0d1c --- /dev/null +++ b/example/fixtures/drf_example.json @@ -0,0 +1,124 @@ +[ +{ + "model": "example.blog", + "pk": 1, + "fields": { + "created_at": "2016-05-02T08:27:16.889", + "modified_at": "2016-05-02T08:27:16.889", + "name": "Personal", + "tagline": "" + } +}, +{ + "model": "example.blog", + "pk": 2, + "fields": { + "created_at": "2016-05-02T08:27:23.871", + "modified_at": "2016-05-02T08:27:23.871", + "name": "Work", + "tagline": "" + } +}, +{ + "model": "example.author", + "pk": 1, + "fields": { + "created_at": "2016-05-02T10:09:48.277", + "modified_at": "2016-05-02T10:09:48.277", + "name": "Alice", + "email": "alice@example.com", + "type": null + } +}, +{ + "model": "example.author", + "pk": 2, + "fields": { + "created_at": "2016-05-02T10:09:57.133", + "modified_at": "2016-05-02T10:09:57.133", + "name": "Bob", + "email": "bob@example.com", + "type": null + } +}, +{ + "model": "example.authorbio", + "pk": 1, + "fields": { + "created_at": "2016-05-02T10:10:23.429", + "modified_at": "2016-05-02T10:10:23.429", + "author": 1, + "body": "I just want to send messages to Bob." + } +}, +{ + "model": "example.authorbio", + "pk": 2, + "fields": { + "created_at": "2016-05-02T10:11:30.327", + "modified_at": "2016-05-02T10:11:30.327", + "author": 2, + "body": "I get messages from Alice and send them to Carol" + } +}, +{ + "model": "example.entry", + "pk": 1, + "fields": { + "created_at": "2016-05-02T10:43:21.271", + "modified_at": "2016-05-02T10:43:21.271", + "blog": 1, + "headline": "This is a test, this is only a test", + "body_text": "And this is the body text for the blog entry. To see comments included in this payload visit: /entries/1?include=comments", + "pub_date": "2015-01-01", + "mod_date": "2015-04-05", + "n_comments": 0, + "n_pingbacks": 0, + "rating": 0, + "authors": [ + 1 + ] + } +}, +{ + "model": "example.entry", + "pk": 2, + "fields": { + "created_at": "2016-05-02T10:44:14.376", + "modified_at": "2016-05-02T10:49:30.150", + "blog": 1, + "headline": "Django, the framework for perfectionists with deadlines", + "body_text": "And this is the body text. Try out includes by using this uri: /entries/2?include=comments,authors,authors.bio", + "pub_date": "2015-05-01", + "mod_date": "2015-09-03", + "n_comments": 0, + "n_pingbacks": 0, + "rating": 0, + "authors": [ + 2 + ] + } +}, +{ + "model": "example.comment", + "pk": 1, + "fields": { + "created_at": "2016-05-02T10:44:35.093", + "modified_at": "2016-05-02T10:44:35.093", + "entry": 1, + "body": "Love this article!", + "author": 2 + } +}, +{ + "model": "example.comment", + "pk": 2, + "fields": { + "created_at": "2016-05-02T10:44:55.482", + "modified_at": "2016-05-02T10:44:55.482", + "entry": 2, + "body": "Frist comment!!!", + "author": null + } +} +] diff --git a/example/requirements.txt b/example/requirements.txt index 0fa77009..fe28eddc 100644 --- a/example/requirements.txt +++ b/example/requirements.txt @@ -11,3 +11,4 @@ pyparsing pytz six sqlparse + From d0c8b317a32cb29cac1bacd74b949e73961e30cf Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Mon, 20 Aug 2018 15:07:31 -0400 Subject: [PATCH 6/8] JsonApi -> JSONAPI prefix for pagination classes --- CHANGELOG.md | 3 +- README.rst | 2 +- docs/usage.md | 16 +- example/1045.discuss.jsonapi.org.json | 1632 +++++++++++++++++++++++++ example/1353.discuss.jsonapi.org.json | 1499 +++++++++++++++++++++++ example/tests/unit/test_pagination.py | 6 +- example/views.py | 6 +- rest_framework_json_api/pagination.py | 43 +- 8 files changed, 3185 insertions(+), 22 deletions(-) create mode 100644 example/1045.discuss.jsonapi.org.json create mode 100644 example/1353.discuss.jsonapi.org.json diff --git a/CHANGELOG.md b/CHANGELOG.md index c591958f..c2e50c21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ * Add `HyperlinkedRelatedField` and `SerializerMethodHyperlinkedRelatedField`. See [usage docs](docs/usage.md#related-fields) * Add related urls support. See [usage docs](docs/usage.md#related-urls) * Replaced binary `drf_example` sqlite3 db with a [fixture](example/fixtures/drf_example.yaml). See [usage docs](docs/usage.md#running-the-example-app). - +* For naming consistency, renamed new `JsonApi`-prefix pagination classes to `JSONAPI`-prefix. + * Deprecates `JsonApiPageNumberPagination` and `JsonApiLimitOffsetPagination` v2.5.0 - Released July 11, 2018 diff --git a/README.rst b/README.rst index 46813188..283ff640 100644 --- a/README.rst +++ b/README.rst @@ -161,7 +161,7 @@ override ``settings.REST_FRAMEWORK`` 'PAGE_SIZE': 10, 'EXCEPTION_HANDLER': 'rest_framework_json_api.exceptions.exception_handler', 'DEFAULT_PAGINATION_CLASS': - 'rest_framework_json_api.pagination.JsonApiPageNumberPagination', + 'rest_framework_json_api.pagination.JSONAPIPageNumberPagination', 'DEFAULT_PARSER_CLASSES': ( 'rest_framework_json_api.parsers.JSONParser', 'rest_framework.parsers.FormParser', diff --git a/docs/usage.md b/docs/usage.md index 25bb7310..61ac4fc3 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -15,7 +15,7 @@ REST_FRAMEWORK = { 'PAGE_SIZE': 10, 'EXCEPTION_HANDLER': 'rest_framework_json_api.exceptions.exception_handler', 'DEFAULT_PAGINATION_CLASS': - 'rest_framework_json_api.pagination.JsonApiPageNumberPagination', + 'rest_framework_json_api.pagination.JSONAPIPageNumberPagination', 'DEFAULT_PARSER_CLASSES': ( 'rest_framework_json_api.parsers.JSONParser', 'rest_framework.parsers.FormParser', @@ -58,15 +58,15 @@ You can configure fixed values for the page size or limit -- or allow the client via query parameters. Two pagination classes are available: -- `JsonApiPageNumberPagination` breaks a response up into pages that start at a given page number with a given size - (number of items per page). It can be configured with the following attributes: +- `JSONAPIPageNumberPagination` breaks a response up into pages that start at a given page number + with a given size (number of items per page). It can be configured with the following attributes: - `page_query_param` (default `page[number]`) - `page_size_query_param` (default `page[size]`) Set this to `None` if you don't want to allow the client to specify the size. - `max_page_size` (default `100`) enforces an upper bound on the `page_size_query_param`. Set it to `None` if you don't want to enforce an upper bound. -- `JsonApiLimitOffsetPagination` breaks a response up into pages that start from an item's offset in the viewset for - a given number of items (the limit). +- `JSONAPILimitOffsetPagination` breaks a response up into pages that start from an item's offset + in the viewset for a given number of items (the limit). It can be configured with the following attributes: - `offset_query_param` (default `page[offset]`). - `limit_query_param` (default `page[limit]`). @@ -77,14 +77,14 @@ Two pagination classes are available: These examples show how to configure the parameters to use non-standard names and different limits: ```python -from rest_framework_json_api.pagination import JsonApiPageNumberPagination, JsonApiLimitOffsetPagination +from rest_framework_json_api.pagination import JSONAPIPageNumberPagination, JSONAPILimitOffsetPagination -class MyPagePagination(JsonApiPageNumberPagination): +class MyPagePagination(JSONAPIPageNumberPagination): page_query_param = 'page_number' page_size_query_param = 'page_size' max_page_size = 1000 -class MyLimitPagination(JsonApiLimitOffsetPagination): +class MyLimitPagination(JSONAPILimitOffsetPagination): offset_query_param = 'offset' limit_query_param = 'limit' max_limit = None diff --git a/example/1045.discuss.jsonapi.org.json b/example/1045.discuss.jsonapi.org.json new file mode 100644 index 00000000..79614a85 --- /dev/null +++ b/example/1045.discuss.jsonapi.org.json @@ -0,0 +1,1632 @@ +{ + "post_stream": { + "posts": [ + { + "id": 1045, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2016-04-13T14:25:18.023Z", + "cooked": "

@Sazzer I can’t imagine a situation where you would have so many links from a resource that it would become unmanageable.

\n

For your specific example, when you want to specify something like pagination links to specific pages you should use URL templates

", + "post_number": 11, + "post_type": 1, + "updated_at": "2016-04-13T14:25:18.023Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 14, + "incoming_link_count": 2, + "reads": 15, + "score": 18.7, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "https://tools.ietf.org/html/rfc6570", + "internal": false, + "reflection": false, + "title": "RFC 6570 - URI Template", + "clicks": 11 + } + ], + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1046, + "name": "Graham", + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", + "created_at": "2016-04-13T14:43:34.171Z", + "cooked": "

@A-Helberg Ok - I’d not come across that before. Are they valid in JSON API Links?

\n

Take, for example, faceted searching. Your search results might want to specify a number of facets and values that you can filter on to restrict down what you are seeing. If you are providing a link for every single value of every single facet then this can get to be very big very quickly. If you instead simply provide the facets and values then the payload size is much smaller, and if the client knows how to then it can build the URLs from those input values. If using these URL Templates in JSONAPI is valid then this becomes possible to do.

", + "post_number": 12, + "post_type": 1, + "updated_at": "2016-04-13T14:43:34.171Z", + "reply_count": 1, + "reply_to_post_number": 11, + "quote_count": 0, + "avg_time": 14, + "incoming_link_count": 1, + "reads": 14, + "score": 13.5, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Graham", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 259, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1047, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T14:50:28.351Z", + "cooked": "\n

JSON API wants you to use a filter parameter for this.

", + "post_number": 13, + "post_type": 1, + "updated_at": "2016-04-13T14:50:28.351Z", + "reply_count": 1, + "reply_to_post_number": 12, + "quote_count": 1, + "avg_time": 17, + "incoming_link_count": 1, + "reads": 13, + "score": 13.45, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "http://jsonapi.org/format/#fetching-filtering", + "internal": false, + "reflection": false, + "title": "JSON API — Latest Specification (v1.0)", + "clicks": 9 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1048, + "name": "Graham", + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", + "created_at": "2016-04-13T14:53:46.405Z", + "cooked": "

Yes it does. But something has to provide a list of possibilities for these parameters. That will either be external documentation combined with some data from the payload, or a large number of links in the payload. (Or just external documentation when the possibilities aren’t dependant on the previous payload)

", + "post_number": 14, + "post_type": 1, + "updated_at": "2016-04-13T14:53:46.405Z", + "reply_count": 2, + "reply_to_post_number": 13, + "quote_count": 0, + "avg_time": 27, + "incoming_link_count": 0, + "reads": 13, + "score": 13.95, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Graham", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 259, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1050, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T14:54:31.325Z", + "cooked": "\n

I disagree.
\nThe links say what you can do right now, i.e. based on the current state of resources held by the server. The client cannot know that in advance.
\nHowever, the client can be told in advance that when a certain action is available, the client must supply certain information (i.e. the POST / PATCH body) in order for the server to carry out the action.

", + "post_number": 15, + "post_type": 1, + "updated_at": "2016-04-13T14:54:31.325Z", + "reply_count": 1, + "reply_to_post_number": 9, + "quote_count": 1, + "avg_time": 16, + "incoming_link_count": 0, + "reads": 13, + "score": 8.4, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1051, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T15:00:28.099Z", + "cooked": "\n

Well, a client could assume that it is entitled to filter on any of the resource’s attributes (and then the server could return error responses for requests it does not support).

\n

But I agree that is pretty unfriendly to the client developer, so personally I would put a list of filterable attributes in the API documentation. As you suggest, providing links for every option and every combination of options could significantly bloat the server response, and probably would be hard for a client to parse and present sensibly back to the user.

\n

I can easily imagine different clients offering different subsets of filters appropriate to their particular users. That feels like a client design time consideration, rather than a runtime consideration, so fits with my current rule of thumb about how different aspects of the API get communicated.

", + "post_number": 16, + "post_type": 1, + "updated_at": "2016-04-13T15:01:08.887Z", + "reply_count": 2, + "reply_to_post_number": 14, + "quote_count": 1, + "avg_time": 10, + "incoming_link_count": 1, + "reads": 14, + "score": 18.3, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1052, + "name": "Graham", + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", + "created_at": "2016-04-13T15:04:50.744Z", + "cooked": "

I have to confess, the more I think about it the more I’m starting to wonder against the idea of hyperlinks in the payloads that tell you what to do next. It works fantastically for simple cases, and it seems like a brilliant idea, but it starts to feel like it’s just making life harder for some situations where you end up breaking out of the resource payload anyway.

\n

And I know the big argument in favour of hyperlinks is that the server can do large-scale reorganisations and the client doesn’t need to know or care, because the new links provided will just work. But unfortunately it just doesn’t work that way in real life.

", + "post_number": 17, + "post_type": 1, + "updated_at": "2016-04-13T15:04:50.744Z", + "reply_count": 1, + "reply_to_post_number": 16, + "quote_count": 0, + "avg_time": 9, + "incoming_link_count": 0, + "reads": 13, + "score": 8.05, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Graham", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 259, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1054, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T15:40:58.402Z", + "cooked": "\n

I disagree (as outlined above in reply to @A-Helberg).
\nJust because we can’t use them for everything, that doesn’t mean we shouldn’t use them for anything.

", + "post_number": 18, + "post_type": 1, + "updated_at": "2016-04-13T15:40:58.402Z", + "reply_count": 1, + "reply_to_post_number": 17, + "quote_count": 1, + "avg_time": 10, + "incoming_link_count": 1, + "reads": 10, + "score": 12.5, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1056, + "name": "Graham", + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", + "created_at": "2016-04-13T16:08:47.849Z", + "cooked": "

That’s true. I’m just a huge fan of consistency is all. It gets awkward using an API when some things work one way and other things work in a completely different way. If everything is the same then you know what you’re dealing with.

", + "post_number": 19, + "post_type": 1, + "updated_at": "2016-04-13T16:08:47.849Z", + "reply_count": 0, + "reply_to_post_number": 18, + "quote_count": 0, + "avg_time": 7, + "incoming_link_count": 0, + "reads": 11, + "score": 2.55, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Graham", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 259, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1057, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2016-04-13T19:54:15.126Z", + "cooked": "

@Sazzer as @jlangley said you would use a filter parameter for “faceted searching”. And I agree that having a link to each possible value for search value is not the way to go, and hence the reason for starting this thread, which I’ll explain in a bit.

\n\n

You are right a client can know in advance when a certain action will be available, and the way you do that, is by making your client privy to business rules and logic. This is a very bad type of coupling and exactly the reason HATEOAS was introduced.

\n\n

Correct but what about the case where the filter parater is not an attribute on the resource?

\n\n

And any of a multitude of API updates would, invalidate your client.
\nConsider the case, mentioned above, where a client only cares about a certain filterable attribute, the only breaking change I can think of right now, is a name change. If the client was able to get the filterable parameters from the API. The absence of the parameter it cares about, means it’s no longer available and the client and degrade gracefully.

", + "post_number": 20, + "post_type": 1, + "updated_at": "2016-04-13T19:54:15.126Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 2, + "avg_time": 9, + "incoming_link_count": 0, + "reads": 12, + "score": 7.85, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1058, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2016-04-13T20:02:57.727Z", + "cooked": "\n

This quote is the crux, of why I want to get some discussion around this thread’s topic.

\n
\n

The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI. This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.
\n…
\nIf the Request-URI is not an asterisk, the OPTIONS request applies only to the options that are available when communicating with that resource.
\n…
\nThe response body, if any, SHOULD also include information about the communication options

\n
\n

The problem we are trying to solve, is getting our available “communication options”, from the server. This is exactly what the OPTIONS method tries to solve.

\n

OPTIONS requests may include a body, and we can use this body to get exactly the type of “options” we care about a particular point in time. The OPTIONS body would include, all allowed methods, the attributes and relationships for the resource, and all query parameters.

\n

In my opinion such a response should include validation information, for each of the attributes, such that the client can reasonably validate the content it is sending to the server, without making a round trip. This would be validations for length and for example format.

", + "post_number": 21, + "post_type": 1, + "updated_at": "2016-04-13T20:02:57.727Z", + "reply_count": 2, + "reply_to_post_number": null, + "quote_count": 1, + "avg_time": 11, + "incoming_link_count": 1, + "reads": 12, + "score": 17.95, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1059, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2016-04-13T20:07:12.905Z", + "cooked": "

As an aside, my motivation for HATEOAS as a driving princaple in API design, is as follows.

\n

Consider a regular website, that supports user registration. As a first pass the site requires, username and password, when you submit the form, you are registered. Later in the application’s life, we change the form to be: email and password. And later on we add extra fields, and we can do whatever!

\n

None of this requires you to update your browser (the client), because the the server’s response is self contained. The client can always infer what actions it should take, by following links, or submitting forms.

", + "post_number": 22, + "post_type": 1, + "updated_at": "2016-04-13T20:07:12.905Z", + "reply_count": 2, + "reply_to_post_number": 21, + "quote_count": 0, + "avg_time": 11, + "incoming_link_count": 0, + "reads": 11, + "score": 12.75, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1060, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T22:34:05.990Z", + "cooked": "\n

You’re thinking of filtering on related resources then I presume? There might be a good case for that, but I suspect you would be better served by doing either of the following:

\n
    \n
  1. \n

    Use a different representation model

    \n
  2. \n
  3. \n

    Use a different technical solution (e.g. GraphQL)

    \n
  4. \n
\n\n

Not that many - you would need to remove or rename a previously allowed filter to break clients, adding a new filter wouldn’t be a breaking change.

\n\n

Where a query parameter or attribute element is only allowed to have a restricted set of values (e.g. an enum, where a client might typically use a drop-down selector) I do like to have the API provide (via related resources) the list of allowed values rather than just put this in the API documentation (this way even removing an allowed value doesn’t break the client \":slight_smile:\"). But currently I don’t believe this approach applies generally to filters though, due to the potential explosion of combinations when an API allows multiple filters.

", + "post_number": 23, + "post_type": 1, + "updated_at": "2016-04-13T22:34:05.990Z", + "reply_count": 0, + "reply_to_post_number": 20, + "quote_count": 1, + "avg_time": 9, + "incoming_link_count": 1, + "reads": 12, + "score": 7.85, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html", + "internal": false, + "reflection": false, + "title": "GraphQL Introduction | React", + "clicks": 1 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1061, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T22:40:09.638Z", + "cooked": "\n

I don’t think you need OPTIONS to do this though. Couldn’t you just use related resources (as per the IANA create-form and edit-form approach?

\n\n

You’re still going to have to validate that data on the server anyway though. I can see that doing client-side validation might be attractive if the network is slow / not reliable, but I suspect you would have just as many problems downloading the validation information before submission as you would retrying after the server rejects the submission.

", + "post_number": 24, + "post_type": 1, + "updated_at": "2016-04-13T22:40:09.638Z", + "reply_count": 1, + "reply_to_post_number": 21, + "quote_count": 1, + "avg_time": 7, + "incoming_link_count": 0, + "reads": 10, + "score": 7.35, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "http://www.iana.org/assignments/link-relations/link-relations.xhtml", + "internal": false, + "reflection": false, + "title": "Link Relations", + "clicks": 5 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1062, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2016-04-13T22:51:57.619Z", + "cooked": "

@jlangley Agreed, I originally looked at the create and edit form links. And this is another solution. But this requires adding a new top-level key in jsonapi, that would represent something analogous to a form, which I am not opposed to, and similar work will be needed for responses to an OPTIONS request, but…

\n

My idea behind using OPTIONS, is that they can be conceptually separated from other requests/responses. If you make anything other than an OPTIONS request, you can safely assume your getting a resource back, and not a specification for a resource.

\n

This would also be a non breaking change to the spec, ie. a jsonapi 1.0 implementation would still work exactly the same with a 1.x version that supports OPTIONS

\n

With regards to round trip for validation, definitely it is possible that this does not improve roundtrip, but this is not the goal. Currently you can get a list of errors back, but those errors are only human readable, and a client can’t really do much with them. I believe having some basic data validation, will improve a client’s user experience A great deal

", + "post_number": 25, + "post_type": 1, + "updated_at": "2016-04-13T22:54:55.644Z", + "reply_count": 1, + "reply_to_post_number": 24, + "quote_count": 0, + "avg_time": 5, + "incoming_link_count": 0, + "reads": 10, + "score": 7.25, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1063, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T22:54:47.141Z", + "cooked": "\n

In a (transactional) “regular website” the browser isn’t the client - the V in the MVC / MVVM web framework running on the server is the client for the API (and that will need updating to cater for the changes you describe). The browser is just rendering the UI (HTML) generated by the client, and the UI hasn’t had a breaking change because changing the API hasn’t caused the semantics of HTML to change.

\n

This doesn’t mean that hypermedia and HATEOAS have no value, but rather that we should recognise that they aren’t “silver bullets” either.

", + "post_number": 26, + "post_type": 1, + "updated_at": "2016-04-13T22:54:47.141Z", + "reply_count": 1, + "reply_to_post_number": 22, + "quote_count": 1, + "avg_time": 3, + "incoming_link_count": 0, + "reads": 12, + "score": 7.55, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1064, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2016-04-13T22:59:34.195Z", + "cooked": "\n

I wasn’t implying anything about API’s here, sorry for the confusion. I meant client, in the case of client-server applications.

", + "post_number": 27, + "post_type": 1, + "updated_at": "2016-04-13T22:59:34.195Z", + "reply_count": 0, + "reply_to_post_number": 26, + "quote_count": 1, + "avg_time": 4, + "incoming_link_count": 0, + "reads": 11, + "score": 2.4, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 1065, + "name": "", + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "created_at": "2016-04-13T23:09:13.586Z", + "cooked": "\n

No need for that - you can just define the forms under relationships.

\n\n

That’s a nice distinction and I quite like that as an idea. I’m still not sold on the idea that this meta information is best provided at runtime though; and even if it is, should we be using directly in-band data to convey this, or should we link to somewhere else (e.g. API documentation \":wink:\") where this can be found?

\n\n

I disagree. Another reason why I chose JSON API was the structured error information it provides.

\n\n

Perhaps this would be more valuable where you expect to have a high proportion of novice users (e.g. a public facing API), and so you might expect the server to respond with a high proportion of rejections. The things I’m working on right now will only be available internally / to selected partners so this is less of a worry for me.

", + "post_number": 28, + "post_type": 1, + "updated_at": "2016-04-13T23:09:13.586Z", + "reply_count": 0, + "reply_to_post_number": 25, + "quote_count": 1, + "avg_time": 14, + "incoming_link_count": 9, + "reads": 12, + "score": 48.1, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "http://jsonapi.org/format/#error-objects", + "internal": false, + "reflection": false, + "title": "JSON API — Latest Specification (v1.0)", + "clicks": 2 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 251, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2786, + "name": "Alan Crosswell", + "username": "n2ygk", + "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png", + "created_at": "2018-03-26T15:47:20.859Z", + "cooked": "

I’m wondering if it’s time to revisit this idea now that OAS 3.0 is out there and it seems to be where the world of API modeling and documentation is converging. Unlike Swagger 2.0, OAS 3.0 is able to represent jsonapi thanks to addition of some missing json-schema constructs.

\n

I’ve been working a bit with django-restframework-json-api and it seems the DRF OPTIONS metadata would be a good place to plug this in as a proof of concept.

", + "post_number": 29, + "post_type": 1, + "updated_at": "2018-03-26T15:47:20.859Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 220, + "incoming_link_count": 0, + "reads": 4, + "score": 16.8, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Alan Crosswell", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "https://www.openapis.org/", + "internal": false, + "reflection": false, + "title": "Home - OpenAPI Initiative", + "clicks": 1 + }, + { + "url": "http://www.django-rest-framework.org/api-guide/metadata/", + "internal": false, + "reflection": false, + "title": "Metadata - Django REST framework", + "clicks": 1 + }, + { + "url": "https://blog.readme.io/an-example-filled-guide-to-swagger-3-2/", + "internal": false, + "reflection": false, + "title": "A Visual Guide to What's New in Swagger 3.0", + "clicks": 1 + } + ], + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 563, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2787, + "name": "Andre Helberg", + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "created_at": "2018-03-27T06:05:35.616Z", + "cooked": "

I still think this is a marvellous idea. Back when I started this thread I was hoping to get some community feedback on what a proper setup would be, but I have since realised, you don’t need it (the feedback I mean).

\n

If you are working on a proof of concept congratulations \":tada:\"
\nYou now have an amazing project to give a talk about (at almost any tech conference since APIs are language agnostic)

\n

If you write a blog post to share what you’ve done, please link it, I’d love to read it!

\n

Some hidden benefits of this include:
\nAutomatic rendering of resource “forms”
\nOTA(over the air) updates of resources without the need to update clients
\nClient side validation + single source of truth for these validations

", + "post_number": 30, + "post_type": 1, + "updated_at": "2018-03-27T06:05:35.616Z", + "reply_count": 0, + "reply_to_post_number": 29, + "quote_count": 0, + "avg_time": 165, + "incoming_link_count": 0, + "reads": 3, + "score": 8.85, + "yours": false, + "topic_id": 407, + "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "display_username": "Andre Helberg", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "n2ygk", + "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 252, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + } + ], + "stream": [ + 939, + 1006, + 1018, + 1020, + 1033, + 1035, + 1039, + 1042, + 1043, + 1044, + 1045, + 1046, + 1047, + 1048, + 1050, + 1051, + 1052, + 1054, + 1056, + 1057, + 1058, + 1059, + 1060, + 1061, + 1062, + 1063, + 1064, + 1065, + 2786, + 2787 + ] + }, + "timeline_lookup": [ + [ + 1, + 868 + ], + [ + 2, + 854 + ], + [ + 3, + 853 + ], + [ + 7, + 852 + ], + [ + 10, + 851 + ], + [ + 29, + 139 + ] + ], + "suggested_topics": [ + { + "id": 1158, + "title": "Relationships within abstract relationship", + "fancy_title": "Relationships within abstract relationship", + "slug": "relationships-within-abstract-relationship", + "posts_count": 14, + "reply_count": 6, + "highest_post_number": 14, + "image_url": null, + "created_at": "2017-11-20T23:01:24.465Z", + "last_posted_at": "2018-03-15T05:04:58.419Z", + "bumped": true, + "bumped_at": "2018-03-15T05:04:58.419Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 2, + "views": 1106, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 513, + "username": "a-komarev", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-komarev/{size}/440_1.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + } + ] + }, + { + "id": 1164, + "title": "Complex Nested Arrays", + "fancy_title": "Complex Nested Arrays", + "slug": "complex-nested-arrays", + "posts_count": 1, + "reply_count": 0, + "highest_post_number": 1, + "image_url": null, + "created_at": "2017-12-05T18:40:58.012Z", + "last_posted_at": "2017-12-05T18:40:58.106Z", + "bumped": true, + "bumped_at": "2017-12-06T20:47:19.587Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 0, + "views": 532, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": "latest single", + "description": "Original Poster, Most Recent Poster", + "user": { + "id": 581, + "username": "Wesley", + "avatar_template": "https://avatars.discourse.org/v2/letter/w/eb9ed0/{size}.png" + } + } + ] + }, + { + "id": 1193, + "title": "Is it allowed to have an attribute called \"links\" inside \"attributes\"?", + "fancy_title": "Is it allowed to have an attribute called “links” inside “attributes”?", + "slug": "is-it-allowed-to-have-an-attribute-called-links-inside-attributes", + "posts_count": 3, + "reply_count": 1, + "highest_post_number": 3, + "image_url": null, + "created_at": "2018-01-25T16:59:15.093Z", + "last_posted_at": "2018-01-25T17:42:40.319Z", + "bumped": true, + "bumped_at": "2018-01-25T17:42:40.319Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 0, + "views": 378, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": "latest", + "description": "Original Poster, Most Recent Poster", + "user": { + "id": 341, + "username": "Buju", + "avatar_template": "/user_avatar/discuss.jsonapi.org/buju/{size}/321_1.png" + } + }, + { + "extras": null, + "description": "Frequent Poster", + "user": { + "id": 15, + "username": "dgeb", + "avatar_template": "/user_avatar/discuss.jsonapi.org/dgeb/{size}/17_1.png" + } + } + ] + }, + { + "id": 1194, + "title": "Auth Header Question", + "fancy_title": "Auth Header Question", + "slug": "auth-header-question", + "posts_count": 2, + "reply_count": 0, + "highest_post_number": 2, + "image_url": null, + "created_at": "2018-01-30T19:32:48.514Z", + "last_posted_at": "2018-01-31T05:48:56.600Z", + "bumped": true, + "bumped_at": "2018-01-31T05:48:56.600Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 0, + "views": 373, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 581, + "username": "Wesley", + "avatar_template": "https://avatars.discourse.org/v2/letter/w/eb9ed0/{size}.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + } + ] + }, + { + "id": 1137, + "title": "303 response on POSTing resource without conflicts", + "fancy_title": "303 response on POSTing resource without conflicts", + "slug": "303-response-on-posting-resource-without-conflicts", + "posts_count": 8, + "reply_count": 3, + "highest_post_number": 8, + "image_url": null, + "created_at": "2017-10-23T01:07:28.611Z", + "last_posted_at": "2017-10-26T13:29:35.909Z", + "bumped": true, + "bumped_at": "2017-10-26T13:29:35.909Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 0, + "views": 773, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 513, + "username": "a-komarev", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-komarev/{size}/440_1.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + } + ] + } + ], + "id": 407, + "title": "[RFC] Standardising an OPTIONS response on a server that supports jsonapi", + "fancy_title": "[RFC] Standardising an OPTIONS response on a server that supports jsonapi", + "posts_count": 30, + "created_at": "2016-03-28T07:43:44.543Z", + "views": 3393, + "reply_count": 23, + "like_count": 0, + "last_posted_at": "2018-03-27T06:05:35.616Z", + "visible": true, + "closed": false, + "archived": false, + "has_summary": false, + "archetype": "regular", + "slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", + "category_id": 1, + "word_count": 3807, + "deleted_at": null, + "user_id": 252, + "featured_link": null, + "pinned_globally": false, + "pinned_at": null, + "pinned_until": null, + "draft": null, + "draft_key": "topic_407", + "draft_sequence": null, + "unpinned": null, + "pinned": false, + "details": { + "created_by": { + "id": 252, + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "last_poster": { + "id": 252, + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" + }, + "participants": [ + { + "id": 252, + "username": "A-Helberg", + "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", + "post_count": 12, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 251, + "username": "jlangley", + "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", + "post_count": 11, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 259, + "username": "Sazzer", + "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", + "post_count": 5, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 49, + "username": "lukasoppermann", + "avatar_template": "/user_avatar/discuss.jsonapi.org/lukasoppermann/{size}/178_1.png", + "post_count": 1, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 563, + "username": "n2ygk", + "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png", + "post_count": 1, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + } + ], + "links": [ + { + "url": "https://tools.ietf.org/html/rfc6570", + "title": "RFC 6570 - URI Template", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 11, + "user_id": 252, + "domain": "tools.ietf.org", + "root_domain": "ietf.org" + }, + { + "url": "http://jsonapi.org/format/#fetching-filtering", + "title": "JSON API — Latest Specification (v1.0)", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 9, + "user_id": 251, + "domain": "jsonapi.org", + "root_domain": "jsonapi.org" + }, + { + "url": "http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven", + "title": "REST APIs must be hypertext-driven » Untangled", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 8, + "user_id": 252, + "domain": "roy.gbiv.com", + "root_domain": "gbiv.com" + }, + { + "url": "http://www.iana.org/assignments/link-relations/link-relations.xhtml", + "title": "Link Relations", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 5, + "user_id": 251, + "domain": "www.iana.org", + "root_domain": "iana.org" + }, + { + "url": "https://github.com/apiaryio/dredd", + "title": "GitHub - apiaryio/dredd: Language-agnostic HTTP API Testing Framework", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 2, + "user_id": 251, + "domain": "github.com", + "root_domain": "github.com" + }, + { + "url": "http://jsonapi.org/format/#error-objects", + "title": "JSON API — Latest Specification (v1.0)", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 2, + "user_id": 251, + "domain": "jsonapi.org", + "root_domain": "jsonapi.org" + }, + { + "url": "https://www.ietf.org/rfc/rfc2616.txt", + "title": null, + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 2, + "user_id": 252, + "domain": "www.ietf.org", + "root_domain": "ietf.org" + }, + { + "url": "https://blog.readme.io/an-example-filled-guide-to-swagger-3-2/", + "title": "A Visual Guide to What's New in Swagger 3.0", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 1, + "user_id": 563, + "domain": "blog.readme.io", + "root_domain": "readme.io" + }, + { + "url": "https://www.openapis.org/", + "title": "Home - OpenAPI Initiative", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 1, + "user_id": 563, + "domain": "www.openapis.org", + "root_domain": "openapis.org" + }, + { + "url": "http://www.django-rest-framework.org/api-guide/metadata/", + "title": "Metadata - Django REST framework", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 1, + "user_id": 563, + "domain": "www.django-rest-framework.org", + "root_domain": "django-rest-framework.org" + }, + { + "url": "https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html", + "title": "GraphQL Introduction | React", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 1, + "user_id": 251, + "domain": "facebook.github.io", + "root_domain": "facebook.github.io" + } + ], + "notification_level": 1, + "can_flag_topic": false + }, + "current_post_number": 24, + "highest_post_number": 30, + "deleted_by": null, + "actions_summary": [ + { + "id": 4, + "count": 0, + "hidden": false, + "can_act": false + }, + { + "id": 8, + "count": 0, + "hidden": false, + "can_act": false + }, + { + "id": 7, + "count": 0, + "hidden": false, + "can_act": false + } + ], + "chunk_size": 20, + "bookmarked": null, + "topic_timer": null, + "message_bus_last_id": 0, + "participant_count": 5, + "can_vote": false, + "vote_count": null, + "user_voted": false +} diff --git a/example/1353.discuss.jsonapi.org.json b/example/1353.discuss.jsonapi.org.json new file mode 100644 index 00000000..94b2cf9a --- /dev/null +++ b/example/1353.discuss.jsonapi.org.json @@ -0,0 +1,1499 @@ +{ + "post_stream": { + "posts": [ + { + "id": 2150, + "name": "", + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", + "created_at": "2017-07-06T01:56:25.763Z", + "cooked": "

I’ve been reading the specs over and over again.

\n

If an article has many comments,

\n

1.) are you supposed to POST to /comments where in body, you have a relationship to article?

\n

or

\n

2.) are you supposed to POST to /articles/33/comments?

\n

and even if you do the 2nd method, do you also put the relationship to the article even if it is in the url?

", + "post_number": 1, + "post_type": 1, + "updated_at": "2017-07-06T01:56:25.763Z", + "reply_count": 0, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 43, + "incoming_link_count": 162, + "reads": 26, + "score": 822.35, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [ + { + "id": 2, + "count": 1 + } + ], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 502, + "hidden": false, + "trust_level": 0, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2154, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-06T19:14:50.492Z", + "cooked": "

Currently, the spec is pretty specific about what is supposed to happen in this case.

\n

If you scroll down to the POST request portion at Update to-many relationships, you’ll notice the /resource/{id}/relationships/{rel-name} link requires a resource identifier object to be POSTed to add itself to the set of resources related to the primary resource through the rel-name.

\n

This unfortunately, seems to be a 2 step process based on the current spec, while some contributors claim you can use the convention of /resource/{id}/{rel-name} to directly post a member of the relationship, this is unintuitive and hackey and most certainly not in the spec as far as I can see. It also breaks the hypermedia driven paradigm by requiring you to implement stateful information in the URL.

\n

The ideal solution would be for them to allow you to post a ‘resource object’ to the resource relationship link directly, and require the server to create the resource if it is of the correct type and then add the resource to the relationship set, following the conventional error handling process described in the spec.

", + "post_number": 2, + "post_type": 1, + "updated_at": "2017-07-06T19:15:23.157Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 55, + "incoming_link_count": 0, + "reads": 26, + "score": 12.95, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "http://jsonapi.org/format/#crud-updating-to-many-relationships", + "internal": false, + "reflection": false, + "title": "JSON API — Latest Specification (v1.0)", + "clicks": 25 + } + ], + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2156, + "name": "", + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "created_at": "2017-07-06T20:07:36.449Z", + "cooked": "

Nested relationships are a common feature of RESTful APIs. I don’t see anything un-intuitive about posting to /resource/{id}/{rel-name}, or even having an entire set of RESTful endpoints there.

", + "post_number": 3, + "post_type": 1, + "updated_at": "2017-07-06T20:07:36.449Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 47, + "incoming_link_count": 0, + "reads": 27, + "score": 12.75, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 503, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2158, + "name": "", + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", + "created_at": "2017-07-06T21:20:23.566Z", + "cooked": "

Thanks,

\n

Not sure if I want to follow the specs to the dot, or do what is “familiar” to non json-api people.

", + "post_number": 4, + "post_type": 1, + "updated_at": "2017-07-06T21:20:23.566Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 28, + "incoming_link_count": 3, + "reads": 27, + "score": 26.8, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 502, + "hidden": false, + "trust_level": 0, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2159, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-06T21:52:48.297Z", + "cooked": "\n

That’s understandable, but those aren’t RESTful APIs. Those are likely CRUD APIs, and json-api is about enhancing and pushing CRUD api users into a more hypermedia driven space.

\n

URL resource partitioning is easy in the short term and handicaps the API designer and consumer in the long term.

\n\n

/giphy Do or do not, there is no try.

\n

Having said that, I did actually just put up a suggestion on GH about solving this issue here.

\n

If you are going to follow the spec, follow the spec because ultimately the people who benefit from the use of the spec are your users. Snowflake or partial implementations which go outside the bounds of ‘unconditionally compliant’ and ‘conditionally compliant’ to the specification actually hurt the consumer in the long run, as their abstractions for the structure aren’t complete, and they can’t use nice tools like Katharsis to operate against your service.

\n

Back on the initial question, as I posted in github issue, I believe there is a better path than your backreference approach which is currently supported by the specification. Namely you POST a compound document with the user defined ID ‘resource identifier object’ to the relationships link /resource/{id}/relationships/{rel-name} with the new resource in the included section.

\n

However, I think the direct POST / PATCH would be an easier way to implement a client long term.

", + "post_number": 5, + "post_type": 1, + "updated_at": "2017-07-06T21:57:26.587Z", + "reply_count": 1, + "reply_to_post_number": 3, + "quote_count": 2, + "avg_time": 35, + "incoming_link_count": 2, + "reads": 28, + "score": 37.35, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "https://github.com/json-api/json-api/issues/1158#issuecomment-313527090", + "internal": false, + "reflection": false, + "title": "Creating new relationship · Issue #1158 · json-api/json-api · GitHub", + "clicks": 21 + }, + { + "url": "https://github.com/katharsis-project/katharsis-framework", + "internal": false, + "reflection": false, + "title": "GitHub - katharsis-project/katharsis-framework: Katharsis adds powerful layer for RESTful endpoints providing implementenation of JSON:API standard", + "clicks": 8 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" + }, + "actions_summary": [ + { + "id": 2, + "count": 1 + } + ], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2160, + "name": "", + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", + "created_at": "2017-07-06T22:00:07.189Z", + "cooked": "

Thanks, do you guys know of any public apis that implement json api spec perfectly? one with “creating” one to many relationships ?

", + "post_number": 6, + "post_type": 1, + "updated_at": "2017-07-06T22:00:07.189Z", + "reply_count": 0, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 22, + "incoming_link_count": 0, + "reads": 24, + "score": 5.9, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 502, + "hidden": false, + "trust_level": 0, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2162, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-06T22:16:26.863Z", + "cooked": "

That might be a better question for @steveklabnik or @dgeb. However, is there a reason you are implementing this all yourself, and not using a library which has done a lot of this heavy lifting for you in your language of choice?

", + "post_number": 7, + "post_type": 1, + "updated_at": "2017-07-06T22:16:26.863Z", + "reply_count": 0, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 21, + "incoming_link_count": 0, + "reads": 24, + "score": 5.85, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2163, + "name": "", + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "created_at": "2017-07-06T22:18:03.058Z", + "cooked": "\n

This is only an option if you use UUIDs for the id field, which not everyone does nor should they.

\n

AFAICT using backreferences in the URL is a valid interpretation of the spec, and as long as it is properly linked, completely valid HATEOAS.

", + "post_number": 8, + "post_type": 1, + "updated_at": "2017-07-06T22:18:03.058Z", + "reply_count": 1, + "reply_to_post_number": 5, + "quote_count": 1, + "avg_time": 25, + "incoming_link_count": 0, + "reads": 24, + "score": 11.05, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 503, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2164, + "name": "", + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", + "created_at": "2017-07-06T22:32:52.281Z", + "cooked": "

I’m using phoenix and elixir and ja_serializer,

\n

it does basics of converting JSON attributes/relationships to Ecto friendly data, but you still have to setup and design the Urls yourself

", + "post_number": 9, + "post_type": 1, + "updated_at": "2017-07-06T22:32:52.281Z", + "reply_count": 0, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 27, + "incoming_link_count": 1, + "reads": 23, + "score": 10.95, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 502, + "hidden": false, + "trust_level": 0, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2166, + "name": "", + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", + "created_at": "2017-07-07T00:34:50.422Z", + "cooked": "

do you guys know of any api documentation that shows a proper json spec implementation?

\n

for example this is for angellist
\nhttps://market.mashape.com/community/angellist

\n

but it is doing the CRUD method

", + "post_number": 10, + "post_type": 1, + "updated_at": "2017-07-07T00:34:50.422Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 32, + "incoming_link_count": 2, + "reads": 23, + "score": 21.2, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "https://market.mashape.com/community/angellist", + "internal": false, + "reflection": false, + "title": "AngelList API Documentation", + "clicks": 17 + } + ], + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 502, + "hidden": false, + "trust_level": 0, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2167, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-07T14:30:15.674Z", + "cooked": "\n

I’m not sure I understand why you make this assertion at all, and identifier is meant to be a key value, why would the format of the identifier matter in any way where a user should not use it? I’ll let you make your arguments for this statement if you chose before I comment.

\n

As far as the back references are concerned, from what I see you are correct. I previously stated exactly that, the backreference method was a valid approach under the specification, but I did make the claim it required a bit of mental gymnastics. I believe this claim is both true and a valid criticism of its usability. The compound document approach is also currently valid within the specification, and far more straightforward.

\n

For the sake of concisely defining the behavior of the specification it isn’t in our collective best interests to dilute the means operating against a server running a json-api service, however I do think as far as semantic sugar is concerned the best approach would be to simply allow the inclusion of a ‘resource object’ in the POST and PATCH collections. My previous paragraph, would of course enumerate the way you COULD do the same now albeit in a more verbose, and constrained fashion.

", + "post_number": 11, + "post_type": 1, + "updated_at": "2017-07-07T14:30:15.674Z", + "reply_count": 0, + "reply_to_post_number": 8, + "quote_count": 1, + "avg_time": 31, + "incoming_link_count": 3, + "reads": 20, + "score": 20.55, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2176, + "name": "", + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "created_at": "2017-07-13T03:15:05.076Z", + "cooked": "

You’ll find they’re quite similar, except unlike CRUD, we’re abstracting things a bit more. You’ll use links, relationships, and sub-resources to help abstract away things like pivot tables and foreign keys. The hardest part is not getting hung up on your data representation and coming up with more elegant ways to represent your data.

\n

There isn’t a “wrong” or “right” way to do it as long as the spec is followed. JSONAPI is more of a standardized set of tools to accomplish these tasks. Welcome to the real difficult part of programming: systems design.

", + "post_number": 12, + "post_type": 1, + "updated_at": "2017-07-13T03:15:05.076Z", + "reply_count": 1, + "reply_to_post_number": 10, + "quote_count": 0, + "avg_time": 29, + "incoming_link_count": 0, + "reads": 20, + "score": 10.45, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 503, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2181, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-13T15:45:23.717Z", + "cooked": "

While I’m very aware of the issues you bring up when designing a hypermedia driven service and agree with the overall premise that system design is the most important and complicated part of the implementation, you still haven’t answered my question. Why “shouldn’t” people use UUID’s? You made no mention of any negative property of their use to recommend against it.

\n

The traditional incremented integer ‘best practice’ is always a worse decision for security and cohesion concerns.

\n

As far as the other methods go, I used the term ‘semantic sugar’ to reference ‘syntactic sugar’ meaning it was functionally equivalent but a more intuitive process.

\n

The proposed modification approach is slightly better than the compound document approach which is markedly better than the back reference approach, when comparing their relative intuitiveness.

", + "post_number": 13, + "post_type": 1, + "updated_at": "2017-07-13T15:45:23.717Z", + "reply_count": 0, + "reply_to_post_number": 12, + "quote_count": 0, + "avg_time": 25, + "incoming_link_count": 0, + "reads": 21, + "score": 5.45, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2248, + "name": "", + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "created_at": "2017-07-28T04:53:59.584Z", + "cooked": "

@michaelhibay My last post referring to system design was directed at @mikeni and designed to re-align this thread on topic. I’m not going to wreck this poor guys inquiry with pointless bickering.

\n

I say UUIDs are a dumb decision and I don’t like the compound doc approach. You say incremented integers are flawed and don’t like backreferences. Ultimately, he must make the design decision best for his project.

", + "post_number": 14, + "post_type": 1, + "updated_at": "2017-07-28T04:53:59.584Z", + "reply_count": 1, + "reply_to_post_number": null, + "quote_count": 0, + "avg_time": 30, + "incoming_link_count": 0, + "reads": 21, + "score": 10.7, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 503, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2251, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-28T14:21:24.820Z", + "cooked": "

I apologize if my response came off as bikeshedding in your mind. While I feel I am well versed in many subjects, I heartily welcome every opportunity to learn and was very interested to know your thoughts. I was challenging the assertion of UUID usage being a mistake in situations as a chance to learn why you would say this.

\n

The back-reference solution is approaching a bike-shedding discussion which would be better served as a specification level discussion rather than an implementation one to be sure. However, the point is certainly somewhat moot if and when the use of hypermedia can obviate the URL, but hypermedia is unfortunately not a requirement and my statement was the spec should offer limited solutions to the requirement to keep the interaction and documents as uniform as possible.

", + "post_number": 15, + "post_type": 1, + "updated_at": "2017-07-28T14:21:24.820Z", + "reply_count": 1, + "reply_to_post_number": 14, + "quote_count": 0, + "avg_time": 25, + "incoming_link_count": 0, + "reads": 22, + "score": 10.65, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2252, + "name": "", + "username": "cam", + "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png", + "created_at": "2017-07-28T14:49:56.646Z", + "cooked": "

Michael, in the “Update to-many relationships” of the spec, can you please put an example of a POST with a NEW comment (meaning how the URL and json would look like)? From your statement above, it sounds like adding a NEW comment is a two step process. Are you indicating that you would have to first do a POST to /comments and then a POST to /articles/1/relationships/comments? I started a new thread yesterday about this issue with foreign keys and this does not work in that situation. I’m trying to understand the correct way to deal with foreign keys and updating to many relationships.

\n

The more examples, the easier it is to understand what one should do in different scenarios. That would be very helpful. Thank you.
\n–Cam–

", + "post_number": 16, + "post_type": 1, + "updated_at": "2017-07-28T14:56:42.102Z", + "reply_count": 2, + "reply_to_post_number": 2, + "quote_count": 0, + "avg_time": 29, + "incoming_link_count": 2, + "reads": 21, + "score": 25.65, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 2, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 499, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2254, + "name": "", + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "created_at": "2017-07-28T17:01:02.162Z", + "cooked": "

You can do this with one request using a compound document posted to /comments. I agree, though, there needs to be more use cases of this in action. You would POST the following, and the relationships would be created along with the object.

\n

http://jsonapi.org/format/#document-compound-documents

", + "post_number": 17, + "post_type": 1, + "updated_at": "2017-07-28T17:04:00.964Z", + "reply_count": 0, + "reply_to_post_number": 16, + "quote_count": 0, + "avg_time": 31, + "incoming_link_count": 0, + "reads": 20, + "score": 5.55, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "http://jsonapi.org/format/#document-compound-documents", + "internal": false, + "reflection": false, + "clicks": 25 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "cam", + "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 503, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2255, + "name": "", + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "created_at": "2017-07-28T17:10:57.711Z", + "cooked": "

Until I have a measurably better suggestion, I won’t complain about it anymore. I don’t have numbers to back up any claim that I’d make, and all my complaints would be performance related.

\n

I’ve been researching alternatives, and if I get around to benchmarking them I’ll post the results in a new thread.

", + "post_number": 18, + "post_type": 1, + "updated_at": "2017-07-28T17:10:57.711Z", + "reply_count": 1, + "reply_to_post_number": 15, + "quote_count": 0, + "avg_time": 26, + "incoming_link_count": 1, + "reads": 20, + "score": 15.3, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "read": true, + "user_title": null, + "reply_to_user": { + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 503, + "hidden": false, + "trust_level": 1, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + }, + { + "id": 2256, + "name": "", + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "created_at": "2017-07-28T17:32:34.478Z", + "cooked": "

There’s no need to carry this discussion in multiple threads. I responded to the thread you created, and we can continue there.

\n\n

I’d be interested in seeing those results. Despite what they reinforce in college, performance related issues which are outside the bounds of algorithmic complexity are generally best addressed when they show signs of being a problem. I regularly have to fight the urge of premature optimization, so I understand where you are coming from.

", + "post_number": 19, + "post_type": 1, + "updated_at": "2017-07-28T17:32:34.478Z", + "reply_count": 0, + "reply_to_post_number": 16, + "quote_count": 1, + "avg_time": 34, + "incoming_link_count": 4, + "reads": 21, + "score": 25.9, + "yours": false, + "topic_id": 1020, + "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", + "display_username": "", + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_bg_color": null, + "primary_group_flair_color": null, + "version": 1, + "can_edit": false, + "can_delete": false, + "can_recover": false, + "can_wiki": false, + "link_counts": [ + { + "url": "http://discuss.jsonapi.org/t/posting-related-data/1054/3", + "internal": true, + "reflection": false, + "title": "POSTing related data", + "clicks": 30 + } + ], + "read": true, + "user_title": null, + "reply_to_user": { + "username": "cam", + "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png" + }, + "actions_summary": [], + "moderator": false, + "admin": false, + "staff": false, + "user_id": 415, + "hidden": false, + "trust_level": 2, + "deleted_at": null, + "user_deleted": false, + "edit_reason": null, + "can_view_edit_history": true, + "wiki": false, + "can_accept_answer": false, + "can_unaccept_answer": false, + "accepted_answer": false + } + ], + "stream": [ + 2150, + 2154, + 2156, + 2158, + 2159, + 2160, + 2162, + 2163, + 2164, + 2166, + 2167, + 2176, + 2181, + 2248, + 2251, + 2252, + 2254, + 2255, + 2256 + ] + }, + "timeline_lookup": [ + [ + 1, + 403 + ], + [ + 2, + 402 + ], + [ + 11, + 401 + ], + [ + 12, + 396 + ], + [ + 13, + 395 + ], + [ + 14, + 381 + ], + [ + 15, + 380 + ] + ], + "suggested_topics": [ + { + "id": 1121, + "title": "Is it valid for a server to return a response with no body?", + "fancy_title": "Is it valid for a server to return a response with no body?", + "slug": "is-it-valid-for-a-server-to-return-a-response-with-no-body", + "posts_count": 3, + "reply_count": 0, + "highest_post_number": 3, + "image_url": null, + "created_at": "2017-10-06T15:09:32.969Z", + "last_posted_at": "2017-10-06T18:49:15.183Z", + "bumped": true, + "bumped_at": "2017-10-06T18:49:15.183Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 3, + "views": 569, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 565, + "username": "bundacia", + "avatar_template": "/user_avatar/discuss.jsonapi.org/bundacia/{size}/496_1.png" + } + }, + { + "extras": null, + "description": "Frequent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 563, + "username": "n2ygk", + "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png" + } + } + ] + }, + { + "id": 1150, + "title": "Status of Versioning - What's the silver bullet?", + "fancy_title": "Status of Versioning - What’s the silver bullet?", + "slug": "status-of-versioning-whats-the-silver-bullet", + "posts_count": 1, + "reply_count": 0, + "highest_post_number": 1, + "image_url": null, + "created_at": "2017-11-03T10:15:37.624Z", + "last_posted_at": "2017-11-03T10:15:37.685Z", + "bumped": true, + "bumped_at": "2017-11-03T10:15:37.685Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 0, + "views": 113, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": "latest single", + "description": "Original Poster, Most Recent Poster", + "user": { + "id": 572, + "username": "mblackritter", + "avatar_template": "/user_avatar/discuss.jsonapi.org/mblackritter/{size}/498_1.png" + } + } + ] + }, + { + "id": 1192, + "title": "Returning page meta data", + "fancy_title": "Returning page meta data", + "slug": "returning-page-meta-data", + "posts_count": 2, + "reply_count": 0, + "highest_post_number": 2, + "image_url": null, + "created_at": "2018-01-25T15:34:35.664Z", + "last_posted_at": "2018-03-08T13:37:46.651Z", + "bumped": true, + "bumped_at": "2018-03-08T13:37:46.651Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 1, + "views": 416, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 599, + "username": "marceloverdijk", + "avatar_template": "/user_avatar/discuss.jsonapi.org/marceloverdijk/{size}/521_1.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + } + ] + }, + { + "id": 1194, + "title": "Auth Header Question", + "fancy_title": "Auth Header Question", + "slug": "auth-header-question", + "posts_count": 2, + "reply_count": 0, + "highest_post_number": 2, + "image_url": null, + "created_at": "2018-01-30T19:32:48.514Z", + "last_posted_at": "2018-01-31T05:48:56.600Z", + "bumped": true, + "bumped_at": "2018-01-31T05:48:56.600Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 0, + "views": 373, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 581, + "username": "Wesley", + "avatar_template": "https://avatars.discourse.org/v2/letter/w/eb9ed0/{size}.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + } + ] + }, + { + "id": 1065, + "title": "Idea: Request Cycle Library", + "fancy_title": "Idea: Request Cycle Library", + "slug": "idea-request-cycle-library", + "posts_count": 7, + "reply_count": 3, + "highest_post_number": 7, + "image_url": null, + "created_at": "2017-08-13T19:03:54.180Z", + "last_posted_at": "2017-09-02T15:38:20.661Z", + "bumped": true, + "bumped_at": "2017-09-02T15:38:20.661Z", + "unseen": false, + "pinned": false, + "unpinned": null, + "visible": true, + "closed": false, + "archived": false, + "bookmarked": null, + "liked": null, + "archetype": "regular", + "like_count": 1, + "views": 764, + "category_id": 1, + "featured_link": null, + "posters": [ + { + "extras": null, + "description": "Original Poster", + "user": { + "id": 503, + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" + } + }, + { + "extras": null, + "description": "Frequent Poster", + "user": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + } + }, + { + "extras": "latest", + "description": "Most Recent Poster", + "user": { + "id": 61, + "username": "beauby", + "avatar_template": "https://avatars.discourse.org/v2/letter/b/f475e1/{size}.png" + } + } + ] + } + ], + "id": 1020, + "title": "POST /comments with relationship or POST /articles/33/comments", + "fancy_title": "POST /comments with relationship or POST /articles/33/comments", + "posts_count": 19, + "created_at": "2017-07-06T01:56:25.691Z", + "views": 2026, + "reply_count": 10, + "like_count": 2, + "last_posted_at": "2017-07-28T17:32:34.478Z", + "visible": true, + "closed": false, + "archived": false, + "has_summary": false, + "archetype": "regular", + "slug": "post-comments-with-relationship-or-post-articles-33-comments", + "category_id": 1, + "word_count": 2562, + "deleted_at": null, + "user_id": 502, + "featured_link": null, + "pinned_globally": false, + "pinned_at": null, + "pinned_until": null, + "draft": null, + "draft_key": "topic_1020", + "draft_sequence": null, + "unpinned": null, + "pinned": false, + "details": { + "created_by": { + "id": 502, + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png" + }, + "last_poster": { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" + }, + "participants": [ + { + "id": 415, + "username": "michaelhibay", + "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", + "post_count": 7, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 503, + "username": "derrekbertrand", + "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", + "post_count": 6, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 502, + "username": "mikeni", + "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", + "post_count": 5, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + }, + { + "id": 499, + "username": "cam", + "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png", + "post_count": 1, + "primary_group_name": null, + "primary_group_flair_url": null, + "primary_group_flair_color": null, + "primary_group_flair_bg_color": null + } + ], + "links": [ + { + "url": "http://discuss.jsonapi.org/t/posting-related-data/1054/3", + "title": "POSTing related data", + "internal": true, + "attachment": false, + "reflection": false, + "clicks": 30, + "user_id": 415, + "domain": "discuss.jsonapi.org", + "root_domain": "jsonapi.org" + }, + { + "url": "http://jsonapi.org/format/#crud-updating-to-many-relationships", + "title": "JSON API — Latest Specification (v1.0)", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 25, + "user_id": 415, + "domain": "jsonapi.org", + "root_domain": "jsonapi.org" + }, + { + "url": "http://jsonapi.org/format/#document-compound-documents", + "title": null, + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 25, + "user_id": 503, + "domain": "jsonapi.org", + "root_domain": "jsonapi.org" + }, + { + "url": "https://github.com/json-api/json-api/issues/1158#issuecomment-313527090", + "title": "Creating new relationship · Issue #1158 · json-api/json-api · GitHub", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 21, + "user_id": 415, + "domain": "github.com", + "root_domain": "github.com" + }, + { + "url": "https://market.mashape.com/community/angellist", + "title": "AngelList API Documentation", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 17, + "user_id": 502, + "domain": "market.mashape.com", + "root_domain": "mashape.com" + }, + { + "url": "https://github.com/katharsis-project/katharsis-framework", + "title": "GitHub - katharsis-project/katharsis-framework: Katharsis adds powerful layer for RESTful endpoints providing implementenation of JSON:API standard", + "internal": false, + "attachment": false, + "reflection": false, + "clicks": 8, + "user_id": 415, + "domain": "github.com", + "root_domain": "github.com" + } + ], + "notification_level": 1, + "can_flag_topic": false + }, + "current_post_number": 1, + "highest_post_number": 19, + "deleted_by": null, + "actions_summary": [ + { + "id": 4, + "count": 0, + "hidden": false, + "can_act": false + }, + { + "id": 8, + "count": 0, + "hidden": false, + "can_act": false + }, + { + "id": 7, + "count": 0, + "hidden": false, + "can_act": false + } + ], + "chunk_size": 20, + "bookmarked": null, + "topic_timer": null, + "message_bus_last_id": 6, + "participant_count": 4, + "can_vote": false, + "vote_count": null, + "user_voted": false +} diff --git a/example/tests/unit/test_pagination.py b/example/tests/unit/test_pagination.py index f6e95db0..b2cd1621 100644 --- a/example/tests/unit/test_pagination.py +++ b/example/tests/unit/test_pagination.py @@ -13,11 +13,11 @@ class TestLimitOffset: """ - Unit tests for `pagination.JsonApiLimitOffsetPagination`. + Unit tests for `pagination.JSONAPILimitOffsetPagination`. """ def setup(self): - class ExamplePagination(pagination.JsonApiLimitOffsetPagination): + class ExamplePagination(pagination.JSONAPILimitOffsetPagination): default_limit = 10 max_limit = 15 @@ -91,7 +91,7 @@ def test_limit_offset_deprecation(self): reason="python2.7 fails for unknown reason") class TestPageNumber: """ - Unit tests for `pagination.JsonApiPageNumberPagination`. + Unit tests for `pagination.JSONAPIPageNumberPagination`. TODO: add unit tests for changing query parameter names, limits, etc. """ def test_page_number_deprecation(self): diff --git a/example/views.py b/example/views.py index 5dfc3341..a42a80ae 100644 --- a/example/views.py +++ b/example/views.py @@ -34,7 +34,7 @@ def get_object(self): return super(BlogViewSet, self).get_object() -class JsonApiViewSet(ModelViewSet): +class JSONAPIViewSet(ModelViewSet): """ This is an example on how to configure DRF-jsonapi from within a class. It allows using DRF-jsonapi alongside @@ -58,12 +58,12 @@ def handle_exception(self, exc): exc.status_code = HTTP_422_UNPROCESSABLE_ENTITY # exception handler can't be set on class so you have to # override the error response in this method - response = super(JsonApiViewSet, self).handle_exception(exc) + response = super(JSONAPIViewSet, self).handle_exception(exc) context = self.get_exception_handler_context() return format_drf_errors(response, context, exc) -class BlogCustomViewSet(JsonApiViewSet): +class BlogCustomViewSet(JSONAPIViewSet): queryset = Blog.objects.all() serializer_class = BlogSerializer diff --git a/rest_framework_json_api/pagination.py b/rest_framework_json_api/pagination.py index 00873c99..6b1f0fc5 100644 --- a/rest_framework_json_api/pagination.py +++ b/rest_framework_json_api/pagination.py @@ -9,7 +9,7 @@ from rest_framework.views import Response -class JsonApiPageNumberPagination(PageNumberPagination): +class JSONAPIPageNumberPagination(PageNumberPagination): """ A json-api compatible pagination format """ @@ -50,7 +50,7 @@ def get_paginated_response(self, data): }) -class JsonApiLimitOffsetPagination(LimitOffsetPagination): +class JSONAPILimitOffsetPagination(LimitOffsetPagination): """ A limit/offset based style. For example: http://api.example.org/accounts/?page[limit]=100 @@ -100,7 +100,23 @@ def get_paginated_response(self, data): }) -class PageNumberPagination(JsonApiPageNumberPagination): +class JsonApiPageNumberPagination(JSONAPIPageNumberPagination): + """ + Deprecated due to desire to use `JSONAPI` prefix for all classes. + """ + page_query_param = 'page' + page_size_query_param = 'page_size' + + def __init__(self): + warnings.warn( + 'JsonApiPageNumberPagination is deprecated. Use JSONAPIPageNumberPagination ' + 'or create custom pagination. See ' + 'https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#pagination', + DeprecationWarning) + super(PageNumberPagination, self).__init__() + + +class PageNumberPagination(JSONAPIPageNumberPagination): """ Deprecated paginator that uses different query parameters """ @@ -109,14 +125,29 @@ class PageNumberPagination(JsonApiPageNumberPagination): def __init__(self): warnings.warn( - 'PageNumberPagination is deprecated. Use JsonApiPageNumberPagination ' + 'PageNumberPagination is deprecated. Use JSONAPIPageNumberPagination ' 'or create custom pagination. See ' 'https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#pagination', DeprecationWarning) super(PageNumberPagination, self).__init__() -class LimitOffsetPagination(JsonApiLimitOffsetPagination): +class JsonApiLimitOffsetPagination(JSONAPILimitOffsetPagination): + """ + Deprecated due to desire to use `JSONAPI` prefix for all classes. + """ + max_limit = None + + def __init__(self): + warnings.warn( + 'JsonApiLimitOffsetPagination is deprecated. Use JSONAPILimitOffsetPagination ' + 'or create custom pagination. See ' + 'https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#pagination', + DeprecationWarning) + super(LimitOffsetPagination, self).__init__() + + +class LimitOffsetPagination(JSONAPILimitOffsetPagination): """ Deprecated paginator that uses a different max_limit """ @@ -124,7 +155,7 @@ class LimitOffsetPagination(JsonApiLimitOffsetPagination): def __init__(self): warnings.warn( - 'LimitOffsetPagination is deprecated. Use JsonApiLimitOffsetPagination ' + 'LimitOffsetPagination is deprecated. Use JSONAPILimitOffsetPagination ' 'or create custom pagination. See ' 'https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#pagination', DeprecationWarning) From 29aa5ff2e4cc140f9354240d4e0356bbaac5acc5 Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Mon, 20 Aug 2018 15:07:59 -0400 Subject: [PATCH 7/8] fix tox -e flake8 to only check our code --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index d5cca046..24df43e4 100644 --- a/tox.ini +++ b/tox.ini @@ -20,7 +20,7 @@ commands = [testenv:flake8] deps = flake8 -commands = flake8 +commands = flake8 --exclude=migrations rest_framework_json_api example skip_install = true [testenv:isort] From efc0a78bde9ee3218247fa24276366fc01bdee17 Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Mon, 20 Aug 2018 15:16:32 -0400 Subject: [PATCH 8/8] remove inadvertently added files --- example/1045.discuss.jsonapi.org.json | 1632 ------------------------- example/1353.discuss.jsonapi.org.json | 1499 ----------------------- 2 files changed, 3131 deletions(-) delete mode 100644 example/1045.discuss.jsonapi.org.json delete mode 100644 example/1353.discuss.jsonapi.org.json diff --git a/example/1045.discuss.jsonapi.org.json b/example/1045.discuss.jsonapi.org.json deleted file mode 100644 index 79614a85..00000000 --- a/example/1045.discuss.jsonapi.org.json +++ /dev/null @@ -1,1632 +0,0 @@ -{ - "post_stream": { - "posts": [ - { - "id": 1045, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2016-04-13T14:25:18.023Z", - "cooked": "

@Sazzer I can’t imagine a situation where you would have so many links from a resource that it would become unmanageable.

\n

For your specific example, when you want to specify something like pagination links to specific pages you should use URL templates

", - "post_number": 11, - "post_type": 1, - "updated_at": "2016-04-13T14:25:18.023Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 14, - "incoming_link_count": 2, - "reads": 15, - "score": 18.7, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "https://tools.ietf.org/html/rfc6570", - "internal": false, - "reflection": false, - "title": "RFC 6570 - URI Template", - "clicks": 11 - } - ], - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1046, - "name": "Graham", - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", - "created_at": "2016-04-13T14:43:34.171Z", - "cooked": "

@A-Helberg Ok - I’d not come across that before. Are they valid in JSON API Links?

\n

Take, for example, faceted searching. Your search results might want to specify a number of facets and values that you can filter on to restrict down what you are seeing. If you are providing a link for every single value of every single facet then this can get to be very big very quickly. If you instead simply provide the facets and values then the payload size is much smaller, and if the client knows how to then it can build the URLs from those input values. If using these URL Templates in JSONAPI is valid then this becomes possible to do.

", - "post_number": 12, - "post_type": 1, - "updated_at": "2016-04-13T14:43:34.171Z", - "reply_count": 1, - "reply_to_post_number": 11, - "quote_count": 0, - "avg_time": 14, - "incoming_link_count": 1, - "reads": 14, - "score": 13.5, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Graham", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 259, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1047, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T14:50:28.351Z", - "cooked": "\n

JSON API wants you to use a filter parameter for this.

", - "post_number": 13, - "post_type": 1, - "updated_at": "2016-04-13T14:50:28.351Z", - "reply_count": 1, - "reply_to_post_number": 12, - "quote_count": 1, - "avg_time": 17, - "incoming_link_count": 1, - "reads": 13, - "score": 13.45, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "http://jsonapi.org/format/#fetching-filtering", - "internal": false, - "reflection": false, - "title": "JSON API — Latest Specification (v1.0)", - "clicks": 9 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1048, - "name": "Graham", - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", - "created_at": "2016-04-13T14:53:46.405Z", - "cooked": "

Yes it does. But something has to provide a list of possibilities for these parameters. That will either be external documentation combined with some data from the payload, or a large number of links in the payload. (Or just external documentation when the possibilities aren’t dependant on the previous payload)

", - "post_number": 14, - "post_type": 1, - "updated_at": "2016-04-13T14:53:46.405Z", - "reply_count": 2, - "reply_to_post_number": 13, - "quote_count": 0, - "avg_time": 27, - "incoming_link_count": 0, - "reads": 13, - "score": 13.95, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Graham", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 259, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1050, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T14:54:31.325Z", - "cooked": "\n

I disagree.
\nThe links say what you can do right now, i.e. based on the current state of resources held by the server. The client cannot know that in advance.
\nHowever, the client can be told in advance that when a certain action is available, the client must supply certain information (i.e. the POST / PATCH body) in order for the server to carry out the action.

", - "post_number": 15, - "post_type": 1, - "updated_at": "2016-04-13T14:54:31.325Z", - "reply_count": 1, - "reply_to_post_number": 9, - "quote_count": 1, - "avg_time": 16, - "incoming_link_count": 0, - "reads": 13, - "score": 8.4, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1051, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T15:00:28.099Z", - "cooked": "\n

Well, a client could assume that it is entitled to filter on any of the resource’s attributes (and then the server could return error responses for requests it does not support).

\n

But I agree that is pretty unfriendly to the client developer, so personally I would put a list of filterable attributes in the API documentation. As you suggest, providing links for every option and every combination of options could significantly bloat the server response, and probably would be hard for a client to parse and present sensibly back to the user.

\n

I can easily imagine different clients offering different subsets of filters appropriate to their particular users. That feels like a client design time consideration, rather than a runtime consideration, so fits with my current rule of thumb about how different aspects of the API get communicated.

", - "post_number": 16, - "post_type": 1, - "updated_at": "2016-04-13T15:01:08.887Z", - "reply_count": 2, - "reply_to_post_number": 14, - "quote_count": 1, - "avg_time": 10, - "incoming_link_count": 1, - "reads": 14, - "score": 18.3, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1052, - "name": "Graham", - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", - "created_at": "2016-04-13T15:04:50.744Z", - "cooked": "

I have to confess, the more I think about it the more I’m starting to wonder against the idea of hyperlinks in the payloads that tell you what to do next. It works fantastically for simple cases, and it seems like a brilliant idea, but it starts to feel like it’s just making life harder for some situations where you end up breaking out of the resource payload anyway.

\n

And I know the big argument in favour of hyperlinks is that the server can do large-scale reorganisations and the client doesn’t need to know or care, because the new links provided will just work. But unfortunately it just doesn’t work that way in real life.

", - "post_number": 17, - "post_type": 1, - "updated_at": "2016-04-13T15:04:50.744Z", - "reply_count": 1, - "reply_to_post_number": 16, - "quote_count": 0, - "avg_time": 9, - "incoming_link_count": 0, - "reads": 13, - "score": 8.05, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Graham", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 259, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1054, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T15:40:58.402Z", - "cooked": "\n

I disagree (as outlined above in reply to @A-Helberg).
\nJust because we can’t use them for everything, that doesn’t mean we shouldn’t use them for anything.

", - "post_number": 18, - "post_type": 1, - "updated_at": "2016-04-13T15:40:58.402Z", - "reply_count": 1, - "reply_to_post_number": 17, - "quote_count": 1, - "avg_time": 10, - "incoming_link_count": 1, - "reads": 10, - "score": 12.5, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1056, - "name": "Graham", - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", - "created_at": "2016-04-13T16:08:47.849Z", - "cooked": "

That’s true. I’m just a huge fan of consistency is all. It gets awkward using an API when some things work one way and other things work in a completely different way. If everything is the same then you know what you’re dealing with.

", - "post_number": 19, - "post_type": 1, - "updated_at": "2016-04-13T16:08:47.849Z", - "reply_count": 0, - "reply_to_post_number": 18, - "quote_count": 0, - "avg_time": 7, - "incoming_link_count": 0, - "reads": 11, - "score": 2.55, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Graham", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 259, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1057, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2016-04-13T19:54:15.126Z", - "cooked": "

@Sazzer as @jlangley said you would use a filter parameter for “faceted searching”. And I agree that having a link to each possible value for search value is not the way to go, and hence the reason for starting this thread, which I’ll explain in a bit.

\n\n

You are right a client can know in advance when a certain action will be available, and the way you do that, is by making your client privy to business rules and logic. This is a very bad type of coupling and exactly the reason HATEOAS was introduced.

\n\n

Correct but what about the case where the filter parater is not an attribute on the resource?

\n\n

And any of a multitude of API updates would, invalidate your client.
\nConsider the case, mentioned above, where a client only cares about a certain filterable attribute, the only breaking change I can think of right now, is a name change. If the client was able to get the filterable parameters from the API. The absence of the parameter it cares about, means it’s no longer available and the client and degrade gracefully.

", - "post_number": 20, - "post_type": 1, - "updated_at": "2016-04-13T19:54:15.126Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 2, - "avg_time": 9, - "incoming_link_count": 0, - "reads": 12, - "score": 7.85, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1058, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2016-04-13T20:02:57.727Z", - "cooked": "\n

This quote is the crux, of why I want to get some discussion around this thread’s topic.

\n
\n

The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI. This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.
\n…
\nIf the Request-URI is not an asterisk, the OPTIONS request applies only to the options that are available when communicating with that resource.
\n…
\nThe response body, if any, SHOULD also include information about the communication options

\n
\n

The problem we are trying to solve, is getting our available “communication options”, from the server. This is exactly what the OPTIONS method tries to solve.

\n

OPTIONS requests may include a body, and we can use this body to get exactly the type of “options” we care about a particular point in time. The OPTIONS body would include, all allowed methods, the attributes and relationships for the resource, and all query parameters.

\n

In my opinion such a response should include validation information, for each of the attributes, such that the client can reasonably validate the content it is sending to the server, without making a round trip. This would be validations for length and for example format.

", - "post_number": 21, - "post_type": 1, - "updated_at": "2016-04-13T20:02:57.727Z", - "reply_count": 2, - "reply_to_post_number": null, - "quote_count": 1, - "avg_time": 11, - "incoming_link_count": 1, - "reads": 12, - "score": 17.95, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1059, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2016-04-13T20:07:12.905Z", - "cooked": "

As an aside, my motivation for HATEOAS as a driving princaple in API design, is as follows.

\n

Consider a regular website, that supports user registration. As a first pass the site requires, username and password, when you submit the form, you are registered. Later in the application’s life, we change the form to be: email and password. And later on we add extra fields, and we can do whatever!

\n

None of this requires you to update your browser (the client), because the the server’s response is self contained. The client can always infer what actions it should take, by following links, or submitting forms.

", - "post_number": 22, - "post_type": 1, - "updated_at": "2016-04-13T20:07:12.905Z", - "reply_count": 2, - "reply_to_post_number": 21, - "quote_count": 0, - "avg_time": 11, - "incoming_link_count": 0, - "reads": 11, - "score": 12.75, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1060, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T22:34:05.990Z", - "cooked": "\n

You’re thinking of filtering on related resources then I presume? There might be a good case for that, but I suspect you would be better served by doing either of the following:

\n
    \n
  1. \n

    Use a different representation model

    \n
  2. \n
  3. \n

    Use a different technical solution (e.g. GraphQL)

    \n
  4. \n
\n\n

Not that many - you would need to remove or rename a previously allowed filter to break clients, adding a new filter wouldn’t be a breaking change.

\n\n

Where a query parameter or attribute element is only allowed to have a restricted set of values (e.g. an enum, where a client might typically use a drop-down selector) I do like to have the API provide (via related resources) the list of allowed values rather than just put this in the API documentation (this way even removing an allowed value doesn’t break the client \":slight_smile:\"). But currently I don’t believe this approach applies generally to filters though, due to the potential explosion of combinations when an API allows multiple filters.

", - "post_number": 23, - "post_type": 1, - "updated_at": "2016-04-13T22:34:05.990Z", - "reply_count": 0, - "reply_to_post_number": 20, - "quote_count": 1, - "avg_time": 9, - "incoming_link_count": 1, - "reads": 12, - "score": 7.85, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html", - "internal": false, - "reflection": false, - "title": "GraphQL Introduction | React", - "clicks": 1 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1061, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T22:40:09.638Z", - "cooked": "\n

I don’t think you need OPTIONS to do this though. Couldn’t you just use related resources (as per the IANA create-form and edit-form approach?

\n\n

You’re still going to have to validate that data on the server anyway though. I can see that doing client-side validation might be attractive if the network is slow / not reliable, but I suspect you would have just as many problems downloading the validation information before submission as you would retrying after the server rejects the submission.

", - "post_number": 24, - "post_type": 1, - "updated_at": "2016-04-13T22:40:09.638Z", - "reply_count": 1, - "reply_to_post_number": 21, - "quote_count": 1, - "avg_time": 7, - "incoming_link_count": 0, - "reads": 10, - "score": 7.35, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "http://www.iana.org/assignments/link-relations/link-relations.xhtml", - "internal": false, - "reflection": false, - "title": "Link Relations", - "clicks": 5 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1062, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2016-04-13T22:51:57.619Z", - "cooked": "

@jlangley Agreed, I originally looked at the create and edit form links. And this is another solution. But this requires adding a new top-level key in jsonapi, that would represent something analogous to a form, which I am not opposed to, and similar work will be needed for responses to an OPTIONS request, but…

\n

My idea behind using OPTIONS, is that they can be conceptually separated from other requests/responses. If you make anything other than an OPTIONS request, you can safely assume your getting a resource back, and not a specification for a resource.

\n

This would also be a non breaking change to the spec, ie. a jsonapi 1.0 implementation would still work exactly the same with a 1.x version that supports OPTIONS

\n

With regards to round trip for validation, definitely it is possible that this does not improve roundtrip, but this is not the goal. Currently you can get a list of errors back, but those errors are only human readable, and a client can’t really do much with them. I believe having some basic data validation, will improve a client’s user experience A great deal

", - "post_number": 25, - "post_type": 1, - "updated_at": "2016-04-13T22:54:55.644Z", - "reply_count": 1, - "reply_to_post_number": 24, - "quote_count": 0, - "avg_time": 5, - "incoming_link_count": 0, - "reads": 10, - "score": 7.25, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1063, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T22:54:47.141Z", - "cooked": "\n

In a (transactional) “regular website” the browser isn’t the client - the V in the MVC / MVVM web framework running on the server is the client for the API (and that will need updating to cater for the changes you describe). The browser is just rendering the UI (HTML) generated by the client, and the UI hasn’t had a breaking change because changing the API hasn’t caused the semantics of HTML to change.

\n

This doesn’t mean that hypermedia and HATEOAS have no value, but rather that we should recognise that they aren’t “silver bullets” either.

", - "post_number": 26, - "post_type": 1, - "updated_at": "2016-04-13T22:54:47.141Z", - "reply_count": 1, - "reply_to_post_number": 22, - "quote_count": 1, - "avg_time": 3, - "incoming_link_count": 0, - "reads": 12, - "score": 7.55, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1064, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2016-04-13T22:59:34.195Z", - "cooked": "\n

I wasn’t implying anything about API’s here, sorry for the confusion. I meant client, in the case of client-server applications.

", - "post_number": 27, - "post_type": 1, - "updated_at": "2016-04-13T22:59:34.195Z", - "reply_count": 0, - "reply_to_post_number": 26, - "quote_count": 1, - "avg_time": 4, - "incoming_link_count": 0, - "reads": 11, - "score": 2.4, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 1065, - "name": "", - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "created_at": "2016-04-13T23:09:13.586Z", - "cooked": "\n

No need for that - you can just define the forms under relationships.

\n\n

That’s a nice distinction and I quite like that as an idea. I’m still not sold on the idea that this meta information is best provided at runtime though; and even if it is, should we be using directly in-band data to convey this, or should we link to somewhere else (e.g. API documentation \":wink:\") where this can be found?

\n\n

I disagree. Another reason why I chose JSON API was the structured error information it provides.

\n\n

Perhaps this would be more valuable where you expect to have a high proportion of novice users (e.g. a public facing API), and so you might expect the server to respond with a high proportion of rejections. The things I’m working on right now will only be available internally / to selected partners so this is less of a worry for me.

", - "post_number": 28, - "post_type": 1, - "updated_at": "2016-04-13T23:09:13.586Z", - "reply_count": 0, - "reply_to_post_number": 25, - "quote_count": 1, - "avg_time": 14, - "incoming_link_count": 9, - "reads": 12, - "score": 48.1, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "http://jsonapi.org/format/#error-objects", - "internal": false, - "reflection": false, - "title": "JSON API — Latest Specification (v1.0)", - "clicks": 2 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 251, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2786, - "name": "Alan Crosswell", - "username": "n2ygk", - "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png", - "created_at": "2018-03-26T15:47:20.859Z", - "cooked": "

I’m wondering if it’s time to revisit this idea now that OAS 3.0 is out there and it seems to be where the world of API modeling and documentation is converging. Unlike Swagger 2.0, OAS 3.0 is able to represent jsonapi thanks to addition of some missing json-schema constructs.

\n

I’ve been working a bit with django-restframework-json-api and it seems the DRF OPTIONS metadata would be a good place to plug this in as a proof of concept.

", - "post_number": 29, - "post_type": 1, - "updated_at": "2018-03-26T15:47:20.859Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 220, - "incoming_link_count": 0, - "reads": 4, - "score": 16.8, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Alan Crosswell", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "https://www.openapis.org/", - "internal": false, - "reflection": false, - "title": "Home - OpenAPI Initiative", - "clicks": 1 - }, - { - "url": "http://www.django-rest-framework.org/api-guide/metadata/", - "internal": false, - "reflection": false, - "title": "Metadata - Django REST framework", - "clicks": 1 - }, - { - "url": "https://blog.readme.io/an-example-filled-guide-to-swagger-3-2/", - "internal": false, - "reflection": false, - "title": "A Visual Guide to What's New in Swagger 3.0", - "clicks": 1 - } - ], - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 563, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2787, - "name": "Andre Helberg", - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "created_at": "2018-03-27T06:05:35.616Z", - "cooked": "

I still think this is a marvellous idea. Back when I started this thread I was hoping to get some community feedback on what a proper setup would be, but I have since realised, you don’t need it (the feedback I mean).

\n

If you are working on a proof of concept congratulations \":tada:\"
\nYou now have an amazing project to give a talk about (at almost any tech conference since APIs are language agnostic)

\n

If you write a blog post to share what you’ve done, please link it, I’d love to read it!

\n

Some hidden benefits of this include:
\nAutomatic rendering of resource “forms”
\nOTA(over the air) updates of resources without the need to update clients
\nClient side validation + single source of truth for these validations

", - "post_number": 30, - "post_type": 1, - "updated_at": "2018-03-27T06:05:35.616Z", - "reply_count": 0, - "reply_to_post_number": 29, - "quote_count": 0, - "avg_time": 165, - "incoming_link_count": 0, - "reads": 3, - "score": 8.85, - "yours": false, - "topic_id": 407, - "topic_slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "display_username": "Andre Helberg", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "n2ygk", - "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 252, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - } - ], - "stream": [ - 939, - 1006, - 1018, - 1020, - 1033, - 1035, - 1039, - 1042, - 1043, - 1044, - 1045, - 1046, - 1047, - 1048, - 1050, - 1051, - 1052, - 1054, - 1056, - 1057, - 1058, - 1059, - 1060, - 1061, - 1062, - 1063, - 1064, - 1065, - 2786, - 2787 - ] - }, - "timeline_lookup": [ - [ - 1, - 868 - ], - [ - 2, - 854 - ], - [ - 3, - 853 - ], - [ - 7, - 852 - ], - [ - 10, - 851 - ], - [ - 29, - 139 - ] - ], - "suggested_topics": [ - { - "id": 1158, - "title": "Relationships within abstract relationship", - "fancy_title": "Relationships within abstract relationship", - "slug": "relationships-within-abstract-relationship", - "posts_count": 14, - "reply_count": 6, - "highest_post_number": 14, - "image_url": null, - "created_at": "2017-11-20T23:01:24.465Z", - "last_posted_at": "2018-03-15T05:04:58.419Z", - "bumped": true, - "bumped_at": "2018-03-15T05:04:58.419Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 2, - "views": 1106, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 513, - "username": "a-komarev", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-komarev/{size}/440_1.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - } - ] - }, - { - "id": 1164, - "title": "Complex Nested Arrays", - "fancy_title": "Complex Nested Arrays", - "slug": "complex-nested-arrays", - "posts_count": 1, - "reply_count": 0, - "highest_post_number": 1, - "image_url": null, - "created_at": "2017-12-05T18:40:58.012Z", - "last_posted_at": "2017-12-05T18:40:58.106Z", - "bumped": true, - "bumped_at": "2017-12-06T20:47:19.587Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 0, - "views": 532, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": "latest single", - "description": "Original Poster, Most Recent Poster", - "user": { - "id": 581, - "username": "Wesley", - "avatar_template": "https://avatars.discourse.org/v2/letter/w/eb9ed0/{size}.png" - } - } - ] - }, - { - "id": 1193, - "title": "Is it allowed to have an attribute called \"links\" inside \"attributes\"?", - "fancy_title": "Is it allowed to have an attribute called “links” inside “attributes”?", - "slug": "is-it-allowed-to-have-an-attribute-called-links-inside-attributes", - "posts_count": 3, - "reply_count": 1, - "highest_post_number": 3, - "image_url": null, - "created_at": "2018-01-25T16:59:15.093Z", - "last_posted_at": "2018-01-25T17:42:40.319Z", - "bumped": true, - "bumped_at": "2018-01-25T17:42:40.319Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 0, - "views": 378, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": "latest", - "description": "Original Poster, Most Recent Poster", - "user": { - "id": 341, - "username": "Buju", - "avatar_template": "/user_avatar/discuss.jsonapi.org/buju/{size}/321_1.png" - } - }, - { - "extras": null, - "description": "Frequent Poster", - "user": { - "id": 15, - "username": "dgeb", - "avatar_template": "/user_avatar/discuss.jsonapi.org/dgeb/{size}/17_1.png" - } - } - ] - }, - { - "id": 1194, - "title": "Auth Header Question", - "fancy_title": "Auth Header Question", - "slug": "auth-header-question", - "posts_count": 2, - "reply_count": 0, - "highest_post_number": 2, - "image_url": null, - "created_at": "2018-01-30T19:32:48.514Z", - "last_posted_at": "2018-01-31T05:48:56.600Z", - "bumped": true, - "bumped_at": "2018-01-31T05:48:56.600Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 0, - "views": 373, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 581, - "username": "Wesley", - "avatar_template": "https://avatars.discourse.org/v2/letter/w/eb9ed0/{size}.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - } - ] - }, - { - "id": 1137, - "title": "303 response on POSTing resource without conflicts", - "fancy_title": "303 response on POSTing resource without conflicts", - "slug": "303-response-on-posting-resource-without-conflicts", - "posts_count": 8, - "reply_count": 3, - "highest_post_number": 8, - "image_url": null, - "created_at": "2017-10-23T01:07:28.611Z", - "last_posted_at": "2017-10-26T13:29:35.909Z", - "bumped": true, - "bumped_at": "2017-10-26T13:29:35.909Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 0, - "views": 773, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 513, - "username": "a-komarev", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-komarev/{size}/440_1.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - } - ] - } - ], - "id": 407, - "title": "[RFC] Standardising an OPTIONS response on a server that supports jsonapi", - "fancy_title": "[RFC] Standardising an OPTIONS response on a server that supports jsonapi", - "posts_count": 30, - "created_at": "2016-03-28T07:43:44.543Z", - "views": 3393, - "reply_count": 23, - "like_count": 0, - "last_posted_at": "2018-03-27T06:05:35.616Z", - "visible": true, - "closed": false, - "archived": false, - "has_summary": false, - "archetype": "regular", - "slug": "rfc-standardising-an-options-response-on-a-server-that-supports-jsonapi", - "category_id": 1, - "word_count": 3807, - "deleted_at": null, - "user_id": 252, - "featured_link": null, - "pinned_globally": false, - "pinned_at": null, - "pinned_until": null, - "draft": null, - "draft_key": "topic_407", - "draft_sequence": null, - "unpinned": null, - "pinned": false, - "details": { - "created_by": { - "id": 252, - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "last_poster": { - "id": 252, - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png" - }, - "participants": [ - { - "id": 252, - "username": "A-Helberg", - "avatar_template": "/user_avatar/discuss.jsonapi.org/a-helberg/{size}/250_1.png", - "post_count": 12, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 251, - "username": "jlangley", - "avatar_template": "https://avatars.discourse.org/v2/letter/j/9e8a1a/{size}.png", - "post_count": 11, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 259, - "username": "Sazzer", - "avatar_template": "/user_avatar/discuss.jsonapi.org/sazzer/{size}/262_1.png", - "post_count": 5, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 49, - "username": "lukasoppermann", - "avatar_template": "/user_avatar/discuss.jsonapi.org/lukasoppermann/{size}/178_1.png", - "post_count": 1, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 563, - "username": "n2ygk", - "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png", - "post_count": 1, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - } - ], - "links": [ - { - "url": "https://tools.ietf.org/html/rfc6570", - "title": "RFC 6570 - URI Template", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 11, - "user_id": 252, - "domain": "tools.ietf.org", - "root_domain": "ietf.org" - }, - { - "url": "http://jsonapi.org/format/#fetching-filtering", - "title": "JSON API — Latest Specification (v1.0)", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 9, - "user_id": 251, - "domain": "jsonapi.org", - "root_domain": "jsonapi.org" - }, - { - "url": "http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven", - "title": "REST APIs must be hypertext-driven » Untangled", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 8, - "user_id": 252, - "domain": "roy.gbiv.com", - "root_domain": "gbiv.com" - }, - { - "url": "http://www.iana.org/assignments/link-relations/link-relations.xhtml", - "title": "Link Relations", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 5, - "user_id": 251, - "domain": "www.iana.org", - "root_domain": "iana.org" - }, - { - "url": "https://github.com/apiaryio/dredd", - "title": "GitHub - apiaryio/dredd: Language-agnostic HTTP API Testing Framework", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 2, - "user_id": 251, - "domain": "github.com", - "root_domain": "github.com" - }, - { - "url": "http://jsonapi.org/format/#error-objects", - "title": "JSON API — Latest Specification (v1.0)", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 2, - "user_id": 251, - "domain": "jsonapi.org", - "root_domain": "jsonapi.org" - }, - { - "url": "https://www.ietf.org/rfc/rfc2616.txt", - "title": null, - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 2, - "user_id": 252, - "domain": "www.ietf.org", - "root_domain": "ietf.org" - }, - { - "url": "https://blog.readme.io/an-example-filled-guide-to-swagger-3-2/", - "title": "A Visual Guide to What's New in Swagger 3.0", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 1, - "user_id": 563, - "domain": "blog.readme.io", - "root_domain": "readme.io" - }, - { - "url": "https://www.openapis.org/", - "title": "Home - OpenAPI Initiative", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 1, - "user_id": 563, - "domain": "www.openapis.org", - "root_domain": "openapis.org" - }, - { - "url": "http://www.django-rest-framework.org/api-guide/metadata/", - "title": "Metadata - Django REST framework", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 1, - "user_id": 563, - "domain": "www.django-rest-framework.org", - "root_domain": "django-rest-framework.org" - }, - { - "url": "https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html", - "title": "GraphQL Introduction | React", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 1, - "user_id": 251, - "domain": "facebook.github.io", - "root_domain": "facebook.github.io" - } - ], - "notification_level": 1, - "can_flag_topic": false - }, - "current_post_number": 24, - "highest_post_number": 30, - "deleted_by": null, - "actions_summary": [ - { - "id": 4, - "count": 0, - "hidden": false, - "can_act": false - }, - { - "id": 8, - "count": 0, - "hidden": false, - "can_act": false - }, - { - "id": 7, - "count": 0, - "hidden": false, - "can_act": false - } - ], - "chunk_size": 20, - "bookmarked": null, - "topic_timer": null, - "message_bus_last_id": 0, - "participant_count": 5, - "can_vote": false, - "vote_count": null, - "user_voted": false -} diff --git a/example/1353.discuss.jsonapi.org.json b/example/1353.discuss.jsonapi.org.json deleted file mode 100644 index 94b2cf9a..00000000 --- a/example/1353.discuss.jsonapi.org.json +++ /dev/null @@ -1,1499 +0,0 @@ -{ - "post_stream": { - "posts": [ - { - "id": 2150, - "name": "", - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", - "created_at": "2017-07-06T01:56:25.763Z", - "cooked": "

I’ve been reading the specs over and over again.

\n

If an article has many comments,

\n

1.) are you supposed to POST to /comments where in body, you have a relationship to article?

\n

or

\n

2.) are you supposed to POST to /articles/33/comments?

\n

and even if you do the 2nd method, do you also put the relationship to the article even if it is in the url?

", - "post_number": 1, - "post_type": 1, - "updated_at": "2017-07-06T01:56:25.763Z", - "reply_count": 0, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 43, - "incoming_link_count": 162, - "reads": 26, - "score": 822.35, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [ - { - "id": 2, - "count": 1 - } - ], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 502, - "hidden": false, - "trust_level": 0, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2154, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-06T19:14:50.492Z", - "cooked": "

Currently, the spec is pretty specific about what is supposed to happen in this case.

\n

If you scroll down to the POST request portion at Update to-many relationships, you’ll notice the /resource/{id}/relationships/{rel-name} link requires a resource identifier object to be POSTed to add itself to the set of resources related to the primary resource through the rel-name.

\n

This unfortunately, seems to be a 2 step process based on the current spec, while some contributors claim you can use the convention of /resource/{id}/{rel-name} to directly post a member of the relationship, this is unintuitive and hackey and most certainly not in the spec as far as I can see. It also breaks the hypermedia driven paradigm by requiring you to implement stateful information in the URL.

\n

The ideal solution would be for them to allow you to post a ‘resource object’ to the resource relationship link directly, and require the server to create the resource if it is of the correct type and then add the resource to the relationship set, following the conventional error handling process described in the spec.

", - "post_number": 2, - "post_type": 1, - "updated_at": "2017-07-06T19:15:23.157Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 55, - "incoming_link_count": 0, - "reads": 26, - "score": 12.95, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "http://jsonapi.org/format/#crud-updating-to-many-relationships", - "internal": false, - "reflection": false, - "title": "JSON API — Latest Specification (v1.0)", - "clicks": 25 - } - ], - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2156, - "name": "", - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "created_at": "2017-07-06T20:07:36.449Z", - "cooked": "

Nested relationships are a common feature of RESTful APIs. I don’t see anything un-intuitive about posting to /resource/{id}/{rel-name}, or even having an entire set of RESTful endpoints there.

", - "post_number": 3, - "post_type": 1, - "updated_at": "2017-07-06T20:07:36.449Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 47, - "incoming_link_count": 0, - "reads": 27, - "score": 12.75, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 503, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2158, - "name": "", - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", - "created_at": "2017-07-06T21:20:23.566Z", - "cooked": "

Thanks,

\n

Not sure if I want to follow the specs to the dot, or do what is “familiar” to non json-api people.

", - "post_number": 4, - "post_type": 1, - "updated_at": "2017-07-06T21:20:23.566Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 28, - "incoming_link_count": 3, - "reads": 27, - "score": 26.8, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 502, - "hidden": false, - "trust_level": 0, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2159, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-06T21:52:48.297Z", - "cooked": "\n

That’s understandable, but those aren’t RESTful APIs. Those are likely CRUD APIs, and json-api is about enhancing and pushing CRUD api users into a more hypermedia driven space.

\n

URL resource partitioning is easy in the short term and handicaps the API designer and consumer in the long term.

\n\n

/giphy Do or do not, there is no try.

\n

Having said that, I did actually just put up a suggestion on GH about solving this issue here.

\n

If you are going to follow the spec, follow the spec because ultimately the people who benefit from the use of the spec are your users. Snowflake or partial implementations which go outside the bounds of ‘unconditionally compliant’ and ‘conditionally compliant’ to the specification actually hurt the consumer in the long run, as their abstractions for the structure aren’t complete, and they can’t use nice tools like Katharsis to operate against your service.

\n

Back on the initial question, as I posted in github issue, I believe there is a better path than your backreference approach which is currently supported by the specification. Namely you POST a compound document with the user defined ID ‘resource identifier object’ to the relationships link /resource/{id}/relationships/{rel-name} with the new resource in the included section.

\n

However, I think the direct POST / PATCH would be an easier way to implement a client long term.

", - "post_number": 5, - "post_type": 1, - "updated_at": "2017-07-06T21:57:26.587Z", - "reply_count": 1, - "reply_to_post_number": 3, - "quote_count": 2, - "avg_time": 35, - "incoming_link_count": 2, - "reads": 28, - "score": 37.35, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "https://github.com/json-api/json-api/issues/1158#issuecomment-313527090", - "internal": false, - "reflection": false, - "title": "Creating new relationship · Issue #1158 · json-api/json-api · GitHub", - "clicks": 21 - }, - { - "url": "https://github.com/katharsis-project/katharsis-framework", - "internal": false, - "reflection": false, - "title": "GitHub - katharsis-project/katharsis-framework: Katharsis adds powerful layer for RESTful endpoints providing implementenation of JSON:API standard", - "clicks": 8 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" - }, - "actions_summary": [ - { - "id": 2, - "count": 1 - } - ], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2160, - "name": "", - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", - "created_at": "2017-07-06T22:00:07.189Z", - "cooked": "

Thanks, do you guys know of any public apis that implement json api spec perfectly? one with “creating” one to many relationships ?

", - "post_number": 6, - "post_type": 1, - "updated_at": "2017-07-06T22:00:07.189Z", - "reply_count": 0, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 22, - "incoming_link_count": 0, - "reads": 24, - "score": 5.9, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 502, - "hidden": false, - "trust_level": 0, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2162, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-06T22:16:26.863Z", - "cooked": "

That might be a better question for @steveklabnik or @dgeb. However, is there a reason you are implementing this all yourself, and not using a library which has done a lot of this heavy lifting for you in your language of choice?

", - "post_number": 7, - "post_type": 1, - "updated_at": "2017-07-06T22:16:26.863Z", - "reply_count": 0, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 21, - "incoming_link_count": 0, - "reads": 24, - "score": 5.85, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2163, - "name": "", - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "created_at": "2017-07-06T22:18:03.058Z", - "cooked": "\n

This is only an option if you use UUIDs for the id field, which not everyone does nor should they.

\n

AFAICT using backreferences in the URL is a valid interpretation of the spec, and as long as it is properly linked, completely valid HATEOAS.

", - "post_number": 8, - "post_type": 1, - "updated_at": "2017-07-06T22:18:03.058Z", - "reply_count": 1, - "reply_to_post_number": 5, - "quote_count": 1, - "avg_time": 25, - "incoming_link_count": 0, - "reads": 24, - "score": 11.05, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 503, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2164, - "name": "", - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", - "created_at": "2017-07-06T22:32:52.281Z", - "cooked": "

I’m using phoenix and elixir and ja_serializer,

\n

it does basics of converting JSON attributes/relationships to Ecto friendly data, but you still have to setup and design the Urls yourself

", - "post_number": 9, - "post_type": 1, - "updated_at": "2017-07-06T22:32:52.281Z", - "reply_count": 0, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 27, - "incoming_link_count": 1, - "reads": 23, - "score": 10.95, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 502, - "hidden": false, - "trust_level": 0, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2166, - "name": "", - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", - "created_at": "2017-07-07T00:34:50.422Z", - "cooked": "

do you guys know of any api documentation that shows a proper json spec implementation?

\n

for example this is for angellist
\nhttps://market.mashape.com/community/angellist

\n

but it is doing the CRUD method

", - "post_number": 10, - "post_type": 1, - "updated_at": "2017-07-07T00:34:50.422Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 32, - "incoming_link_count": 2, - "reads": 23, - "score": 21.2, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "https://market.mashape.com/community/angellist", - "internal": false, - "reflection": false, - "title": "AngelList API Documentation", - "clicks": 17 - } - ], - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 502, - "hidden": false, - "trust_level": 0, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2167, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-07T14:30:15.674Z", - "cooked": "\n

I’m not sure I understand why you make this assertion at all, and identifier is meant to be a key value, why would the format of the identifier matter in any way where a user should not use it? I’ll let you make your arguments for this statement if you chose before I comment.

\n

As far as the back references are concerned, from what I see you are correct. I previously stated exactly that, the backreference method was a valid approach under the specification, but I did make the claim it required a bit of mental gymnastics. I believe this claim is both true and a valid criticism of its usability. The compound document approach is also currently valid within the specification, and far more straightforward.

\n

For the sake of concisely defining the behavior of the specification it isn’t in our collective best interests to dilute the means operating against a server running a json-api service, however I do think as far as semantic sugar is concerned the best approach would be to simply allow the inclusion of a ‘resource object’ in the POST and PATCH collections. My previous paragraph, would of course enumerate the way you COULD do the same now albeit in a more verbose, and constrained fashion.

", - "post_number": 11, - "post_type": 1, - "updated_at": "2017-07-07T14:30:15.674Z", - "reply_count": 0, - "reply_to_post_number": 8, - "quote_count": 1, - "avg_time": 31, - "incoming_link_count": 3, - "reads": 20, - "score": 20.55, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2176, - "name": "", - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "created_at": "2017-07-13T03:15:05.076Z", - "cooked": "

You’ll find they’re quite similar, except unlike CRUD, we’re abstracting things a bit more. You’ll use links, relationships, and sub-resources to help abstract away things like pivot tables and foreign keys. The hardest part is not getting hung up on your data representation and coming up with more elegant ways to represent your data.

\n

There isn’t a “wrong” or “right” way to do it as long as the spec is followed. JSONAPI is more of a standardized set of tools to accomplish these tasks. Welcome to the real difficult part of programming: systems design.

", - "post_number": 12, - "post_type": 1, - "updated_at": "2017-07-13T03:15:05.076Z", - "reply_count": 1, - "reply_to_post_number": 10, - "quote_count": 0, - "avg_time": 29, - "incoming_link_count": 0, - "reads": 20, - "score": 10.45, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 503, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2181, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-13T15:45:23.717Z", - "cooked": "

While I’m very aware of the issues you bring up when designing a hypermedia driven service and agree with the overall premise that system design is the most important and complicated part of the implementation, you still haven’t answered my question. Why “shouldn’t” people use UUID’s? You made no mention of any negative property of their use to recommend against it.

\n

The traditional incremented integer ‘best practice’ is always a worse decision for security and cohesion concerns.

\n

As far as the other methods go, I used the term ‘semantic sugar’ to reference ‘syntactic sugar’ meaning it was functionally equivalent but a more intuitive process.

\n

The proposed modification approach is slightly better than the compound document approach which is markedly better than the back reference approach, when comparing their relative intuitiveness.

", - "post_number": 13, - "post_type": 1, - "updated_at": "2017-07-13T15:45:23.717Z", - "reply_count": 0, - "reply_to_post_number": 12, - "quote_count": 0, - "avg_time": 25, - "incoming_link_count": 0, - "reads": 21, - "score": 5.45, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2248, - "name": "", - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "created_at": "2017-07-28T04:53:59.584Z", - "cooked": "

@michaelhibay My last post referring to system design was directed at @mikeni and designed to re-align this thread on topic. I’m not going to wreck this poor guys inquiry with pointless bickering.

\n

I say UUIDs are a dumb decision and I don’t like the compound doc approach. You say incremented integers are flawed and don’t like backreferences. Ultimately, he must make the design decision best for his project.

", - "post_number": 14, - "post_type": 1, - "updated_at": "2017-07-28T04:53:59.584Z", - "reply_count": 1, - "reply_to_post_number": null, - "quote_count": 0, - "avg_time": 30, - "incoming_link_count": 0, - "reads": 21, - "score": 10.7, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 503, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2251, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-28T14:21:24.820Z", - "cooked": "

I apologize if my response came off as bikeshedding in your mind. While I feel I am well versed in many subjects, I heartily welcome every opportunity to learn and was very interested to know your thoughts. I was challenging the assertion of UUID usage being a mistake in situations as a chance to learn why you would say this.

\n

The back-reference solution is approaching a bike-shedding discussion which would be better served as a specification level discussion rather than an implementation one to be sure. However, the point is certainly somewhat moot if and when the use of hypermedia can obviate the URL, but hypermedia is unfortunately not a requirement and my statement was the spec should offer limited solutions to the requirement to keep the interaction and documents as uniform as possible.

", - "post_number": 15, - "post_type": 1, - "updated_at": "2017-07-28T14:21:24.820Z", - "reply_count": 1, - "reply_to_post_number": 14, - "quote_count": 0, - "avg_time": 25, - "incoming_link_count": 0, - "reads": 22, - "score": 10.65, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2252, - "name": "", - "username": "cam", - "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png", - "created_at": "2017-07-28T14:49:56.646Z", - "cooked": "

Michael, in the “Update to-many relationships” of the spec, can you please put an example of a POST with a NEW comment (meaning how the URL and json would look like)? From your statement above, it sounds like adding a NEW comment is a two step process. Are you indicating that you would have to first do a POST to /comments and then a POST to /articles/1/relationships/comments? I started a new thread yesterday about this issue with foreign keys and this does not work in that situation. I’m trying to understand the correct way to deal with foreign keys and updating to many relationships.

\n

The more examples, the easier it is to understand what one should do in different scenarios. That would be very helpful. Thank you.
\n–Cam–

", - "post_number": 16, - "post_type": 1, - "updated_at": "2017-07-28T14:56:42.102Z", - "reply_count": 2, - "reply_to_post_number": 2, - "quote_count": 0, - "avg_time": 29, - "incoming_link_count": 2, - "reads": 21, - "score": 25.65, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 2, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 499, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2254, - "name": "", - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "created_at": "2017-07-28T17:01:02.162Z", - "cooked": "

You can do this with one request using a compound document posted to /comments. I agree, though, there needs to be more use cases of this in action. You would POST the following, and the relationships would be created along with the object.

\n

http://jsonapi.org/format/#document-compound-documents

", - "post_number": 17, - "post_type": 1, - "updated_at": "2017-07-28T17:04:00.964Z", - "reply_count": 0, - "reply_to_post_number": 16, - "quote_count": 0, - "avg_time": 31, - "incoming_link_count": 0, - "reads": 20, - "score": 5.55, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "http://jsonapi.org/format/#document-compound-documents", - "internal": false, - "reflection": false, - "clicks": 25 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "cam", - "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 503, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2255, - "name": "", - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "created_at": "2017-07-28T17:10:57.711Z", - "cooked": "

Until I have a measurably better suggestion, I won’t complain about it anymore. I don’t have numbers to back up any claim that I’d make, and all my complaints would be performance related.

\n

I’ve been researching alternatives, and if I get around to benchmarking them I’ll post the results in a new thread.

", - "post_number": 18, - "post_type": 1, - "updated_at": "2017-07-28T17:10:57.711Z", - "reply_count": 1, - "reply_to_post_number": 15, - "quote_count": 0, - "avg_time": 26, - "incoming_link_count": 1, - "reads": 20, - "score": 15.3, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "read": true, - "user_title": null, - "reply_to_user": { - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 503, - "hidden": false, - "trust_level": 1, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - }, - { - "id": 2256, - "name": "", - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "created_at": "2017-07-28T17:32:34.478Z", - "cooked": "

There’s no need to carry this discussion in multiple threads. I responded to the thread you created, and we can continue there.

\n\n

I’d be interested in seeing those results. Despite what they reinforce in college, performance related issues which are outside the bounds of algorithmic complexity are generally best addressed when they show signs of being a problem. I regularly have to fight the urge of premature optimization, so I understand where you are coming from.

", - "post_number": 19, - "post_type": 1, - "updated_at": "2017-07-28T17:32:34.478Z", - "reply_count": 0, - "reply_to_post_number": 16, - "quote_count": 1, - "avg_time": 34, - "incoming_link_count": 4, - "reads": 21, - "score": 25.9, - "yours": false, - "topic_id": 1020, - "topic_slug": "post-comments-with-relationship-or-post-articles-33-comments", - "display_username": "", - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_bg_color": null, - "primary_group_flair_color": null, - "version": 1, - "can_edit": false, - "can_delete": false, - "can_recover": false, - "can_wiki": false, - "link_counts": [ - { - "url": "http://discuss.jsonapi.org/t/posting-related-data/1054/3", - "internal": true, - "reflection": false, - "title": "POSTing related data", - "clicks": 30 - } - ], - "read": true, - "user_title": null, - "reply_to_user": { - "username": "cam", - "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png" - }, - "actions_summary": [], - "moderator": false, - "admin": false, - "staff": false, - "user_id": 415, - "hidden": false, - "trust_level": 2, - "deleted_at": null, - "user_deleted": false, - "edit_reason": null, - "can_view_edit_history": true, - "wiki": false, - "can_accept_answer": false, - "can_unaccept_answer": false, - "accepted_answer": false - } - ], - "stream": [ - 2150, - 2154, - 2156, - 2158, - 2159, - 2160, - 2162, - 2163, - 2164, - 2166, - 2167, - 2176, - 2181, - 2248, - 2251, - 2252, - 2254, - 2255, - 2256 - ] - }, - "timeline_lookup": [ - [ - 1, - 403 - ], - [ - 2, - 402 - ], - [ - 11, - 401 - ], - [ - 12, - 396 - ], - [ - 13, - 395 - ], - [ - 14, - 381 - ], - [ - 15, - 380 - ] - ], - "suggested_topics": [ - { - "id": 1121, - "title": "Is it valid for a server to return a response with no body?", - "fancy_title": "Is it valid for a server to return a response with no body?", - "slug": "is-it-valid-for-a-server-to-return-a-response-with-no-body", - "posts_count": 3, - "reply_count": 0, - "highest_post_number": 3, - "image_url": null, - "created_at": "2017-10-06T15:09:32.969Z", - "last_posted_at": "2017-10-06T18:49:15.183Z", - "bumped": true, - "bumped_at": "2017-10-06T18:49:15.183Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 3, - "views": 569, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 565, - "username": "bundacia", - "avatar_template": "/user_avatar/discuss.jsonapi.org/bundacia/{size}/496_1.png" - } - }, - { - "extras": null, - "description": "Frequent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 563, - "username": "n2ygk", - "avatar_template": "https://avatars.discourse.org/v2/letter/n/50afbb/{size}.png" - } - } - ] - }, - { - "id": 1150, - "title": "Status of Versioning - What's the silver bullet?", - "fancy_title": "Status of Versioning - What’s the silver bullet?", - "slug": "status-of-versioning-whats-the-silver-bullet", - "posts_count": 1, - "reply_count": 0, - "highest_post_number": 1, - "image_url": null, - "created_at": "2017-11-03T10:15:37.624Z", - "last_posted_at": "2017-11-03T10:15:37.685Z", - "bumped": true, - "bumped_at": "2017-11-03T10:15:37.685Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 0, - "views": 113, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": "latest single", - "description": "Original Poster, Most Recent Poster", - "user": { - "id": 572, - "username": "mblackritter", - "avatar_template": "/user_avatar/discuss.jsonapi.org/mblackritter/{size}/498_1.png" - } - } - ] - }, - { - "id": 1192, - "title": "Returning page meta data", - "fancy_title": "Returning page meta data", - "slug": "returning-page-meta-data", - "posts_count": 2, - "reply_count": 0, - "highest_post_number": 2, - "image_url": null, - "created_at": "2018-01-25T15:34:35.664Z", - "last_posted_at": "2018-03-08T13:37:46.651Z", - "bumped": true, - "bumped_at": "2018-03-08T13:37:46.651Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 1, - "views": 416, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 599, - "username": "marceloverdijk", - "avatar_template": "/user_avatar/discuss.jsonapi.org/marceloverdijk/{size}/521_1.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - } - ] - }, - { - "id": 1194, - "title": "Auth Header Question", - "fancy_title": "Auth Header Question", - "slug": "auth-header-question", - "posts_count": 2, - "reply_count": 0, - "highest_post_number": 2, - "image_url": null, - "created_at": "2018-01-30T19:32:48.514Z", - "last_posted_at": "2018-01-31T05:48:56.600Z", - "bumped": true, - "bumped_at": "2018-01-31T05:48:56.600Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 0, - "views": 373, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 581, - "username": "Wesley", - "avatar_template": "https://avatars.discourse.org/v2/letter/w/eb9ed0/{size}.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - } - ] - }, - { - "id": 1065, - "title": "Idea: Request Cycle Library", - "fancy_title": "Idea: Request Cycle Library", - "slug": "idea-request-cycle-library", - "posts_count": 7, - "reply_count": 3, - "highest_post_number": 7, - "image_url": null, - "created_at": "2017-08-13T19:03:54.180Z", - "last_posted_at": "2017-09-02T15:38:20.661Z", - "bumped": true, - "bumped_at": "2017-09-02T15:38:20.661Z", - "unseen": false, - "pinned": false, - "unpinned": null, - "visible": true, - "closed": false, - "archived": false, - "bookmarked": null, - "liked": null, - "archetype": "regular", - "like_count": 1, - "views": 764, - "category_id": 1, - "featured_link": null, - "posters": [ - { - "extras": null, - "description": "Original Poster", - "user": { - "id": 503, - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png" - } - }, - { - "extras": null, - "description": "Frequent Poster", - "user": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - } - }, - { - "extras": "latest", - "description": "Most Recent Poster", - "user": { - "id": 61, - "username": "beauby", - "avatar_template": "https://avatars.discourse.org/v2/letter/b/f475e1/{size}.png" - } - } - ] - } - ], - "id": 1020, - "title": "POST /comments with relationship or POST /articles/33/comments", - "fancy_title": "POST /comments with relationship or POST /articles/33/comments", - "posts_count": 19, - "created_at": "2017-07-06T01:56:25.691Z", - "views": 2026, - "reply_count": 10, - "like_count": 2, - "last_posted_at": "2017-07-28T17:32:34.478Z", - "visible": true, - "closed": false, - "archived": false, - "has_summary": false, - "archetype": "regular", - "slug": "post-comments-with-relationship-or-post-articles-33-comments", - "category_id": 1, - "word_count": 2562, - "deleted_at": null, - "user_id": 502, - "featured_link": null, - "pinned_globally": false, - "pinned_at": null, - "pinned_until": null, - "draft": null, - "draft_key": "topic_1020", - "draft_sequence": null, - "unpinned": null, - "pinned": false, - "details": { - "created_by": { - "id": 502, - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png" - }, - "last_poster": { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png" - }, - "participants": [ - { - "id": 415, - "username": "michaelhibay", - "avatar_template": "/user_avatar/discuss.jsonapi.org/michaelhibay/{size}/405_1.png", - "post_count": 7, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 503, - "username": "derrekbertrand", - "avatar_template": "/user_avatar/discuss.jsonapi.org/derrekbertrand/{size}/426_1.png", - "post_count": 6, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 502, - "username": "mikeni", - "avatar_template": "https://avatars.discourse.org/v2/letter/m/e36b37/{size}.png", - "post_count": 5, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - }, - { - "id": 499, - "username": "cam", - "avatar_template": "https://avatars.discourse.org/v2/letter/c/bbe5ce/{size}.png", - "post_count": 1, - "primary_group_name": null, - "primary_group_flair_url": null, - "primary_group_flair_color": null, - "primary_group_flair_bg_color": null - } - ], - "links": [ - { - "url": "http://discuss.jsonapi.org/t/posting-related-data/1054/3", - "title": "POSTing related data", - "internal": true, - "attachment": false, - "reflection": false, - "clicks": 30, - "user_id": 415, - "domain": "discuss.jsonapi.org", - "root_domain": "jsonapi.org" - }, - { - "url": "http://jsonapi.org/format/#crud-updating-to-many-relationships", - "title": "JSON API — Latest Specification (v1.0)", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 25, - "user_id": 415, - "domain": "jsonapi.org", - "root_domain": "jsonapi.org" - }, - { - "url": "http://jsonapi.org/format/#document-compound-documents", - "title": null, - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 25, - "user_id": 503, - "domain": "jsonapi.org", - "root_domain": "jsonapi.org" - }, - { - "url": "https://github.com/json-api/json-api/issues/1158#issuecomment-313527090", - "title": "Creating new relationship · Issue #1158 · json-api/json-api · GitHub", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 21, - "user_id": 415, - "domain": "github.com", - "root_domain": "github.com" - }, - { - "url": "https://market.mashape.com/community/angellist", - "title": "AngelList API Documentation", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 17, - "user_id": 502, - "domain": "market.mashape.com", - "root_domain": "mashape.com" - }, - { - "url": "https://github.com/katharsis-project/katharsis-framework", - "title": "GitHub - katharsis-project/katharsis-framework: Katharsis adds powerful layer for RESTful endpoints providing implementenation of JSON:API standard", - "internal": false, - "attachment": false, - "reflection": false, - "clicks": 8, - "user_id": 415, - "domain": "github.com", - "root_domain": "github.com" - } - ], - "notification_level": 1, - "can_flag_topic": false - }, - "current_post_number": 1, - "highest_post_number": 19, - "deleted_by": null, - "actions_summary": [ - { - "id": 4, - "count": 0, - "hidden": false, - "can_act": false - }, - { - "id": 8, - "count": 0, - "hidden": false, - "can_act": false - }, - { - "id": 7, - "count": 0, - "hidden": false, - "can_act": false - } - ], - "chunk_size": 20, - "bookmarked": null, - "topic_timer": null, - "message_bus_last_id": 6, - "participant_count": 4, - "can_vote": false, - "vote_count": null, - "user_voted": false -}