mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-23 06:16:50 +00:00
Document JSON API implementation defs and progress in class
This commit is contained in:
parent
9295080ee5
commit
bdb997b1d9
@ -32,6 +32,7 @@ Fixes:
|
||||
- [#1488](https://github.com/rails-api/active_model_serializers/pull/1488) Require ActiveSupport's string inflections (@nate00)
|
||||
|
||||
Misc:
|
||||
- [#1482](https://github.com/rails-api/active_model_serializers/pull/1482) Document JSON API implementation defs and progress in class. (@bf4)
|
||||
- [#1551](https://github.com/rails-api/active_model_serializers/pull/1551) Added codebeat badge (@korzonek)
|
||||
- [#1527](https://github.com/rails-api/active_model_serializers/pull/1527) Refactor fragment cache class. (@groyoh)
|
||||
- [#1560](https://github.com/rails-api/active_model_serializers/pull/1560) Update rubocop and address its warnings. (@bf4 @groyoh)
|
||||
|
||||
@ -84,10 +84,11 @@ module ActiveModelSerializers
|
||||
end
|
||||
|
||||
# Gotta be at the bottom to use the code above it :(
|
||||
require 'active_model_serializers/adapter/base'
|
||||
require 'active_model_serializers/adapter/null'
|
||||
require 'active_model_serializers/adapter/attributes'
|
||||
require 'active_model_serializers/adapter/json'
|
||||
require 'active_model_serializers/adapter/json_api'
|
||||
extend ActiveSupport::Autoload
|
||||
autoload :Base
|
||||
autoload :Null
|
||||
autoload :Attributes
|
||||
autoload :Json
|
||||
autoload :JsonApi
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,3 +1,23 @@
|
||||
# {http://jsonapi.org/format/ JSON API specification}
|
||||
# rubocop:disable Style/AsciiComments
|
||||
# TODO: implement!
|
||||
# ☐ https://github.com/rails-api/active_model_serializers/issues/1235
|
||||
# TODO: use uri_template in link generation?
|
||||
# ☐ https://github.com/rails-api/active_model_serializers/pull/1282#discussion_r42528812
|
||||
# see gem https://github.com/hannesg/uri_template
|
||||
# spec http://tools.ietf.org/html/rfc6570
|
||||
# impl https://developer.github.com/v3/#schema https://api.github.com/
|
||||
# TODO: validate against a JSON schema document?
|
||||
# ☐ https://github.com/rails-api/active_model_serializers/issues/1162
|
||||
# ☑ https://github.com/rails-api/active_model_serializers/pull/1270
|
||||
# TODO: Routing
|
||||
# ☐ https://github.com/rails-api/active_model_serializers/pull/1476
|
||||
# TODO: Query Params
|
||||
# ☑ `include` https://github.com/rails-api/active_model_serializers/pull/1131
|
||||
# ☑ `fields` https://github.com/rails-api/active_model_serializers/pull/700
|
||||
# ☑ `page[number]=3&page[size]=1` https://github.com/rails-api/active_model_serializers/pull/1041
|
||||
# ☐ `filter`
|
||||
# ☐ `sort`
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi < Base
|
||||
@ -30,13 +50,69 @@ module ActiveModelSerializers
|
||||
end
|
||||
|
||||
# {http://jsonapi.org/format/#document-top-level Primary data}
|
||||
# definition:
|
||||
# ☐ toplevel_data (required)
|
||||
# ☐ toplevel_included
|
||||
# ☑ toplevel_meta
|
||||
# ☑ toplevel_links
|
||||
# ☑ toplevel_jsonapi
|
||||
# structure:
|
||||
# {
|
||||
# data: toplevel_data,
|
||||
# included: toplevel_included,
|
||||
# meta: toplevel_meta,
|
||||
# links: toplevel_links,
|
||||
# jsonapi: toplevel_jsonapi
|
||||
# }.reject! {|_,v| v.nil? }
|
||||
def success_document(options)
|
||||
is_collection = serializer.respond_to?(:each)
|
||||
serializers = is_collection ? serializer : [serializer]
|
||||
primary_data, included = resource_objects_for(serializers)
|
||||
|
||||
hash = {}
|
||||
# toplevel_data
|
||||
# definition:
|
||||
# oneOf
|
||||
# resource
|
||||
# array of unique items of type 'resource'
|
||||
# null
|
||||
#
|
||||
# description:
|
||||
# The document's "primary data" is a representation of the resource or collection of resources
|
||||
# targeted by a request.
|
||||
#
|
||||
# Singular: the resource object.
|
||||
#
|
||||
# Collection: one of an array of resource objects, an array of resource identifier objects, or
|
||||
# an empty array ([]), for requests that target resource collections.
|
||||
#
|
||||
# None: null if the request is one that might correspond to a single resource, but doesn't currently.
|
||||
# structure:
|
||||
# if serializable_resource.resource?
|
||||
# resource
|
||||
# elsif serializable_resource.collection?
|
||||
# [
|
||||
# resource,
|
||||
# resource
|
||||
# ]
|
||||
# else
|
||||
# nil
|
||||
# end
|
||||
hash[:data] = is_collection ? primary_data : primary_data[0]
|
||||
# toplevel_included
|
||||
# alias included
|
||||
# definition:
|
||||
# array of unique items of type 'resource'
|
||||
#
|
||||
# description:
|
||||
# To reduce the number of HTTP requests, servers **MAY** allow
|
||||
# responses that include related resources along with the requested primary
|
||||
# resources. Such responses are called "compound documents".
|
||||
# structure:
|
||||
# [
|
||||
# resource,
|
||||
# resource
|
||||
# ]
|
||||
hash[:included] = included if included.any?
|
||||
|
||||
Jsonapi.add!(hash)
|
||||
@ -56,17 +132,31 @@ module ActiveModelSerializers
|
||||
|
||||
# {http://jsonapi.org/format/#errors JSON API Errors}
|
||||
# TODO: look into caching
|
||||
# rubocop:disable Style/AsciiComments
|
||||
# definition:
|
||||
# ☑ toplevel_errors array (required)
|
||||
# ☐ toplevel_meta
|
||||
# ☐ toplevel_jsonapi
|
||||
# rubocop:enable Style/AsciiComments
|
||||
# structure:
|
||||
# {
|
||||
# errors: toplevel_errors,
|
||||
# meta: toplevel_meta,
|
||||
# jsonapi: toplevel_jsonapi
|
||||
# }.reject! {|_,v| v.nil? }
|
||||
# prs:
|
||||
# https://github.com/rails-api/active_model_serializers/pull/1004
|
||||
def failure_document
|
||||
hash = {}
|
||||
# PR Please :)
|
||||
# Jsonapi.add!(hash)
|
||||
|
||||
# toplevel_errors
|
||||
# definition:
|
||||
# array of unique items of type 'error'
|
||||
# structure:
|
||||
# [
|
||||
# error,
|
||||
# error
|
||||
# ]
|
||||
if serializer.respond_to?(:each)
|
||||
hash[:errors] = serializer.flat_map do |error_serializer|
|
||||
Error.resource_errors(error_serializer)
|
||||
@ -89,6 +179,40 @@ module ActiveModelSerializers
|
||||
private
|
||||
|
||||
# {http://jsonapi.org/format/#document-resource-objects Primary data}
|
||||
# resource
|
||||
# definition:
|
||||
# JSON Object
|
||||
#
|
||||
# properties:
|
||||
# type (required) : String
|
||||
# id (required) : String
|
||||
# attributes
|
||||
# relationships
|
||||
# links
|
||||
# meta
|
||||
#
|
||||
# description:
|
||||
# "Resource objects" appear in a JSON API document to represent resources
|
||||
# structure:
|
||||
# {
|
||||
# type: 'admin--some-user',
|
||||
# id: '1336',
|
||||
# attributes: attributes,
|
||||
# relationships: relationships,
|
||||
# links: links,
|
||||
# meta: meta,
|
||||
# }.reject! {|_,v| v.nil? }
|
||||
# prs:
|
||||
# type
|
||||
# https://github.com/rails-api/active_model_serializers/pull/1122
|
||||
# [x] https://github.com/rails-api/active_model_serializers/pull/1213
|
||||
# https://github.com/rails-api/active_model_serializers/pull/1216
|
||||
# https://github.com/rails-api/active_model_serializers/pull/1029
|
||||
# links
|
||||
# [x] https://github.com/rails-api/active_model_serializers/pull/1246
|
||||
# [x] url helpers https://github.com/rails-api/active_model_serializers/issues/1269
|
||||
# meta
|
||||
# [x] https://github.com/rails-api/active_model_serializers/pull/1340
|
||||
def resource_objects_for(serializers)
|
||||
@primary = []
|
||||
@included = []
|
||||
@ -131,6 +255,21 @@ module ActiveModelSerializers
|
||||
end
|
||||
|
||||
# {http://jsonapi.org/format/#document-resource-object-attributes Document Resource Object Attributes}
|
||||
# attributes
|
||||
# definition:
|
||||
# JSON Object
|
||||
#
|
||||
# patternProperties:
|
||||
# ^(?!relationships$|links$)\\w[-\\w_]*$
|
||||
#
|
||||
# description:
|
||||
# Members of the attributes object ("attributes") represent information about the resource
|
||||
# object in which it's defined.
|
||||
# Attributes may contain any valid JSON value
|
||||
# structure:
|
||||
# {
|
||||
# foo: 'bar'
|
||||
# }
|
||||
def attributes_for(serializer, fields)
|
||||
serializer.attributes(fields).except(:id)
|
||||
end
|
||||
@ -151,8 +290,29 @@ module ActiveModelSerializers
|
||||
resource_object[:relationships] = relationships if relationships.any?
|
||||
|
||||
links = links_for(serializer)
|
||||
# toplevel_links
|
||||
# definition:
|
||||
# allOf
|
||||
# ☐ links
|
||||
# ☐ pagination
|
||||
#
|
||||
# description:
|
||||
# Link members related to the primary data.
|
||||
# structure:
|
||||
# links.merge!(pagination)
|
||||
# prs:
|
||||
# https://github.com/rails-api/active_model_serializers/pull/1247
|
||||
# https://github.com/rails-api/active_model_serializers/pull/1018
|
||||
resource_object[:links] = links if links.any?
|
||||
|
||||
# toplevel_meta
|
||||
# alias meta
|
||||
# definition:
|
||||
# meta
|
||||
# structure
|
||||
# {
|
||||
# :'git-ref' => 'abc123'
|
||||
# }
|
||||
meta = meta_for(serializer)
|
||||
resource_object[:meta] = meta unless meta.nil?
|
||||
|
||||
@ -160,6 +320,100 @@ module ActiveModelSerializers
|
||||
end
|
||||
|
||||
# {http://jsonapi.org/format/#document-resource-object-relationships Document Resource Object Relationship}
|
||||
# relationships
|
||||
# definition:
|
||||
# JSON Object
|
||||
#
|
||||
# patternProperties:
|
||||
# ^\\w[-\\w_]*$"
|
||||
#
|
||||
# properties:
|
||||
# data : relationshipsData
|
||||
# links
|
||||
# meta
|
||||
#
|
||||
# description:
|
||||
#
|
||||
# Members of the relationships object ("relationships") represent references from the
|
||||
# resource object in which it's defined to other resource objects."
|
||||
# structure:
|
||||
# {
|
||||
# links: links,
|
||||
# meta: meta,
|
||||
# data: relationshipsData
|
||||
# }.reject! {|_,v| v.nil? }
|
||||
#
|
||||
# prs:
|
||||
# links
|
||||
# [x] https://github.com/rails-api/active_model_serializers/pull/1454
|
||||
# meta
|
||||
# [x] https://github.com/rails-api/active_model_serializers/pull/1454
|
||||
# polymorphic
|
||||
# [ ] https://github.com/rails-api/active_model_serializers/pull/1420
|
||||
#
|
||||
# relationshipsData
|
||||
# definition:
|
||||
# oneOf
|
||||
# relationshipToOne
|
||||
# relationshipToMany
|
||||
#
|
||||
# description:
|
||||
# Member, whose value represents "resource linkage"
|
||||
# structure:
|
||||
# if has_one?
|
||||
# relationshipToOne
|
||||
# else
|
||||
# relationshipToMany
|
||||
# end
|
||||
#
|
||||
# definition:
|
||||
# anyOf
|
||||
# null
|
||||
# linkage
|
||||
#
|
||||
# relationshipToOne
|
||||
# description:
|
||||
#
|
||||
# References to other resource objects in a to-one ("relationship"). Relationships can be
|
||||
# specified by including a member in a resource's links object.
|
||||
#
|
||||
# None: Describes an empty to-one relationship.
|
||||
# structure:
|
||||
# if has_related?
|
||||
# linkage
|
||||
# else
|
||||
# nil
|
||||
# end
|
||||
#
|
||||
# relationshipToMany
|
||||
# definition:
|
||||
# array of unique items of type 'linkage'
|
||||
#
|
||||
# description:
|
||||
# An array of objects each containing "type" and "id" members for to-many relationships
|
||||
# structure:
|
||||
# [
|
||||
# linkage,
|
||||
# linkage
|
||||
# ]
|
||||
# prs:
|
||||
# polymorphic
|
||||
# [ ] https://github.com/rails-api/active_model_serializers/pull/1282
|
||||
#
|
||||
# linkage
|
||||
# definition:
|
||||
# type (required) : String
|
||||
# id (required) : String
|
||||
# meta
|
||||
#
|
||||
# description:
|
||||
# The "type" and "id" to non-empty members.
|
||||
# structure:
|
||||
# {
|
||||
# type: 'required-type',
|
||||
# id: 'required-id',
|
||||
# meta: meta
|
||||
# }.reject! {|_,v| v.nil? }
|
||||
def relationships_for(serializer, requested_associations)
|
||||
include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(requested_associations)
|
||||
serializer.associations(include_tree).each_with_object({}) do |association, hash|
|
||||
@ -174,6 +428,28 @@ module ActiveModelSerializers
|
||||
end
|
||||
|
||||
# {http://jsonapi.org/format/#document-links Document Links}
|
||||
# links
|
||||
# definition:
|
||||
# JSON Object
|
||||
#
|
||||
# properties:
|
||||
# self : URI
|
||||
# related : link
|
||||
#
|
||||
# description:
|
||||
# A resource object **MAY** contain references to other resource objects ("relationships").
|
||||
# Relationships may be to-one or to-many. Relationships can be specified by including a member
|
||||
# in a resource's links object.
|
||||
#
|
||||
# A `self` member’s value is a URL for the relationship itself (a "relationship URL"). This
|
||||
# URL allows the client to directly manipulate the relationship. For example, it would allow
|
||||
# a client to remove an `author` from an `article` without deleting the people resource
|
||||
# itself.
|
||||
# structure:
|
||||
# {
|
||||
# self: 'http://example.com/etc',
|
||||
# related: link
|
||||
# }.reject! {|_,v| v.nil? }
|
||||
def links_for(serializer)
|
||||
serializer._links.each_with_object({}) do |(name, value), hash|
|
||||
hash[name] = Link.new(serializer, value).as_json
|
||||
@ -181,6 +457,36 @@ module ActiveModelSerializers
|
||||
end
|
||||
|
||||
# {http://jsonapi.org/format/#fetching-pagination Pagination Links}
|
||||
# pagination
|
||||
# definition:
|
||||
# first : pageObject
|
||||
# last : pageObject
|
||||
# prev : pageObject
|
||||
# next : pageObject
|
||||
# structure:
|
||||
# {
|
||||
# first: pageObject,
|
||||
# last: pageObject,
|
||||
# prev: pageObject,
|
||||
# next: pageObject
|
||||
# }
|
||||
#
|
||||
# pageObject
|
||||
# definition:
|
||||
# oneOf
|
||||
# URI
|
||||
# null
|
||||
#
|
||||
# description:
|
||||
# The <x> page of data
|
||||
# structure:
|
||||
# if has_page?
|
||||
# 'http://example.com/some-page?page[number][x]'
|
||||
# else
|
||||
# nil
|
||||
# end
|
||||
# prs:
|
||||
# https://github.com/rails-api/active_model_serializers/pull/1041
|
||||
def pagination_links_for(serializer, options)
|
||||
PaginationLinks.new(serializer.object, options[:serialization_context]).serializable_hash(options)
|
||||
end
|
||||
@ -192,3 +498,4 @@ module ActiveModelSerializers
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Style/AsciiComments
|
||||
|
||||
@ -36,6 +36,12 @@ module ActiveModelSerializers
|
||||
# title : A short, human-readable summary of the problem. It **SHOULD NOT** change from
|
||||
# occurrence to occurrence of the problem, except for purposes of localization.
|
||||
# detail : A human-readable explanation specific to this occurrence of the problem.
|
||||
# structure:
|
||||
# {
|
||||
# title: 'SystemFailure',
|
||||
# detail: 'something went terribly wrong',
|
||||
# status: '500'
|
||||
# }.merge!(errorSource)
|
||||
def self.attribute_error_objects(attribute_name, attribute_errors)
|
||||
attribute_errors.map do |attribute_error|
|
||||
{
|
||||
@ -45,6 +51,7 @@ module ActiveModelSerializers
|
||||
end
|
||||
end
|
||||
|
||||
# errorSource
|
||||
# description:
|
||||
# oneOf
|
||||
# ☑ pointer : String
|
||||
@ -56,6 +63,16 @@ module ActiveModelSerializers
|
||||
# https://tools.ietf.org/html/rfc6901
|
||||
#
|
||||
# parameter: A string indicating which query parameter caused the error
|
||||
# structure:
|
||||
# if is_attribute?
|
||||
# {
|
||||
# pointer: '/data/attributes/red-button'
|
||||
# }
|
||||
# else
|
||||
# {
|
||||
# parameter: 'pres'
|
||||
# }
|
||||
# end
|
||||
def self.error_source(source_type, attribute_name)
|
||||
case source_type
|
||||
when :pointer
|
||||
|
||||
@ -2,6 +2,24 @@ module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi < Base
|
||||
# {http://jsonapi.org/format/#document-jsonapi-object Jsonapi Object}
|
||||
|
||||
# toplevel_jsonapi
|
||||
# definition:
|
||||
# JSON Object
|
||||
#
|
||||
# properties:
|
||||
# version : String
|
||||
# meta
|
||||
#
|
||||
# description:
|
||||
# An object describing the server's implementation
|
||||
# structure:
|
||||
# {
|
||||
# version: ActiveModelSerializers.config.jsonapi_version,
|
||||
# meta: ActiveModelSerializers.config.jsonapi_toplevel_meta
|
||||
# }.reject! { |_, v| v.blank? }
|
||||
# prs:
|
||||
# https://github.com/rails-api/active_model_serializers/pull/1050
|
||||
module Jsonapi
|
||||
module_function
|
||||
|
||||
|
||||
@ -3,6 +3,43 @@ require 'active_support/core_ext/module/delegation'
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
# link
|
||||
# definition:
|
||||
# oneOf
|
||||
# linkString
|
||||
# linkObject
|
||||
#
|
||||
# description:
|
||||
# A link **MUST** be represented as either: a string containing the link's URL or a link
|
||||
# object."
|
||||
# structure:
|
||||
# if href?
|
||||
# linkString
|
||||
# else
|
||||
# linkObject
|
||||
# end
|
||||
#
|
||||
# linkString
|
||||
# definition:
|
||||
# URI
|
||||
#
|
||||
# description:
|
||||
# A string containing the link's URL.
|
||||
# structure:
|
||||
# 'http://example.com/link-string'
|
||||
#
|
||||
# linkObject
|
||||
# definition:
|
||||
# JSON Object
|
||||
#
|
||||
# properties:
|
||||
# href (required) : URI
|
||||
# meta
|
||||
# structure:
|
||||
# {
|
||||
# href: 'http://example.com/link-object',
|
||||
# meta: meta,
|
||||
# }.reject! {|_,v| v.nil? }
|
||||
class Link
|
||||
include SerializationContext.url_helpers
|
||||
delegate :default_url_options, to: SerializationContext
|
||||
|
||||
@ -1,6 +1,16 @@
|
||||
module ActiveModelSerializers
|
||||
module Adapter
|
||||
class JsonApi
|
||||
# meta
|
||||
# definition:
|
||||
# JSON Object
|
||||
#
|
||||
# description:
|
||||
# Non-standard meta-information that can not be represented as an attribute or relationship.
|
||||
# structure:
|
||||
# {
|
||||
# attitude: 'adjustable'
|
||||
# }
|
||||
class Meta
|
||||
def initialize(serializer)
|
||||
@object = serializer.object
|
||||
|
||||
Loading…
Reference in New Issue
Block a user