From cb9563338a245b6831884360bdf0317c2678aad1 Mon Sep 17 00:00:00 2001 From: Alexey Karapetov Date: Mon, 25 Mar 2019 00:21:27 -0700 Subject: [PATCH] Separate Document --- README.md | 5 +- example/cars_server/controller.dart | 2 +- example/cars_server/dao.dart | 2 +- example/cars_server/job_queue.dart | 2 +- lib/client.dart | 1 - lib/document.dart | 11 - lib/parser.dart | 197 ------------------ lib/src/client/client.dart | 4 +- lib/src/client/response.dart | 2 +- lib/src/document/document.dart | 40 ---- lib/src/document/error.dart | 65 ------ lib/src/document/identifier.dart | 19 -- lib/src/document/identifier_object.dart | 17 -- lib/src/document/link.dart | 27 --- lib/src/document/pagination.dart | 30 --- lib/src/document/primary_data.dart | 20 -- lib/src/document/relationship.dart | 80 ------- lib/src/document/resource.dart | 38 ---- .../document/resource_collection_data.dart | 35 ---- lib/src/document/resource_data.dart | 32 --- lib/src/document/resource_object.dart | 85 -------- lib/src/server/controller.dart | 2 +- lib/src/server/document_builder.dart | 2 +- lib/src/server/server.dart | 4 +- lib/src/server/standard_document_builder.dart | 3 +- pubspec.yaml | 3 +- test/functional/create_test.dart | 2 +- test/functional/fetch_test.dart | 2 +- test/functional/update_test.dart | 2 +- test/unit/document_test.dart | 19 -- test/unit/example.json | 97 --------- test/unit/parser_test.dart | 27 --- 32 files changed, 18 insertions(+), 859 deletions(-) delete mode 100644 lib/document.dart delete mode 100644 lib/parser.dart delete mode 100644 lib/src/document/document.dart delete mode 100644 lib/src/document/error.dart delete mode 100644 lib/src/document/identifier.dart delete mode 100644 lib/src/document/identifier_object.dart delete mode 100644 lib/src/document/link.dart delete mode 100644 lib/src/document/pagination.dart delete mode 100644 lib/src/document/primary_data.dart delete mode 100644 lib/src/document/relationship.dart delete mode 100644 lib/src/document/resource.dart delete mode 100644 lib/src/document/resource_collection_data.dart delete mode 100644 lib/src/document/resource_data.dart delete mode 100644 lib/src/document/resource_object.dart delete mode 100644 test/unit/document_test.dart delete mode 100644 test/unit/example.json delete mode 100644 test/unit/parser_test.dart diff --git a/README.md b/README.md index cea0f56..a9e9f0b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ -# Implementation of [{json:api} v1.0](http://jsonapi.org) in Dart -[{json:api} v1.0](http://jsonapi.org) is a specification for building APIs in JSON. This library implements +# JSON:API Client/Server in Dart +[JSON:API](http://jsonapi.org) is a specification for building APIs in JSON. This library implements a Client (VM, Flutter, Web), and a Server (VM only). - ## Supported features - Fetching single resources and resource collections - Collection pagination diff --git a/example/cars_server/controller.dart b/example/cars_server/controller.dart index 18fed81..2c3c26e 100644 --- a/example/cars_server/controller.dart +++ b/example/cars_server/controller.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'dart:math'; -import 'package:json_api/document.dart'; import 'package:json_api/server.dart'; import 'package:json_api/src/server/request_target.dart'; +import 'package:json_api_document/document.dart'; import 'package:uuid/uuid.dart'; import 'dao.dart'; diff --git a/example/cars_server/dao.dart b/example/cars_server/dao.dart index 0f81cd0..0fd257b 100644 --- a/example/cars_server/dao.dart +++ b/example/cars_server/dao.dart @@ -1,5 +1,5 @@ -import 'package:json_api/document.dart'; import 'package:json_api/src/nullable.dart'; +import 'package:json_api_document/document.dart'; import 'job_queue.dart'; import 'model.dart'; diff --git a/example/cars_server/job_queue.dart b/example/cars_server/job_queue.dart index b5cd59a..6dfdff1 100644 --- a/example/cars_server/job_queue.dart +++ b/example/cars_server/job_queue.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:json_api/document.dart'; +import 'package:json_api_document/document.dart'; import 'package:uuid/uuid.dart'; class Job { diff --git a/lib/client.dart b/lib/client.dart index 4c2a0fb..157c5df 100644 --- a/lib/client.dart +++ b/lib/client.dart @@ -1,2 +1 @@ -export 'package:json_api/document.dart'; export 'package:json_api/src/client/client.dart'; diff --git a/lib/document.dart b/lib/document.dart deleted file mode 100644 index 6ed66f4..0000000 --- a/lib/document.dart +++ /dev/null @@ -1,11 +0,0 @@ -export 'package:json_api/src/document/document.dart'; -export 'package:json_api/src/document/error.dart'; -export 'package:json_api/src/document/identifier.dart'; -export 'package:json_api/src/document/identifier_object.dart'; -export 'package:json_api/src/document/link.dart'; -export 'package:json_api/src/document/primary_data.dart'; -export 'package:json_api/src/document/relationship.dart'; -export 'package:json_api/src/document/resource.dart'; -export 'package:json_api/src/document/resource_collection_data.dart'; -export 'package:json_api/src/document/resource_data.dart'; -export 'package:json_api/src/document/resource_object.dart'; diff --git a/lib/parser.dart b/lib/parser.dart deleted file mode 100644 index 748a5f0..0000000 --- a/lib/parser.dart +++ /dev/null @@ -1,197 +0,0 @@ -import 'package:json_api/document.dart'; -import 'package:json_api/src/document/pagination.dart'; - -class ParserException implements Exception { - final String message; - - ParserException(this.message); -} - -class JsonApiParser { - const JsonApiParser(); - - Document parseDocument( - Object json, Data parsePrimaryData(Object json)) { - if (json is Map) { - // TODO: validate `meta` - if (json.containsKey('errors')) { - final errors = json['errors']; - if (errors is List) { - return Document.error(errors.map(parseError), meta: json['meta']); - } - } else if (json.containsKey('data')) { - return Document(parsePrimaryData(json), meta: json['meta']); - } else { - return Document.empty(json['meta']); - } - } - throw ParserException('Can not parse Document from $json'); - } - - JsonApiError parseError(Object json) { - if (json is Map) { - Link about; - if (json['links'] is Map) about = parseLink(json['links']['about']); - - String pointer; - String parameter; - if (json['source'] is Map) { - parameter = json['source']['parameter']; - pointer = json['source']['pointer']; - } - return JsonApiError( - id: json['id'], - about: about, - status: json['status'], - code: json['code'], - title: json['title'], - detail: json['detail'], - sourcePointer: pointer, - sourceParameter: parameter, - meta: json['meta']); - } - throw ParserException('Can not parse ErrorObject from $json'); - } - - /// Parses a JSON:API Document or the `relationship` member of a Resource object. - Relationship parseRelationship(Object json) { - if (json is Map) { - if (json.containsKey('data')) { - final data = json['data']; - if (data == null || data is Map) { - return parseToOne(json); - } - if (data is List) { - return parseToMany(json); - } - } else { - final links = parseLinks(json['links']); - return Relationship(self: links['self'], related: links['related']); - } - } - throw ParserException('Can not parse Relationship from $json'); - } - - /// Parses the `relationships` member of a Resource Object - Map parseRelationships(Object json) { - if (json == null) return {}; - if (json is Map) { - return json.map((k, v) => MapEntry(k.toString(), parseRelationship(v))); - } - throw ParserException('Can not parse Relationship map from $json'); - } - - /// Parses the `data` member of a JSON:API Document - ResourceObject parseResourceObject(Object json) { - final mapOrNull = (_) => _ == null || _ is Map; - if (json is Map) { - final relationships = json['relationships']; - final attributes = json['attributes']; - final links = parseLinks(json['links']); - - if (mapOrNull(relationships) && mapOrNull(attributes)) { - return ResourceObject(json['type'], json['id'], - attributes: attributes, - relationships: parseRelationships(relationships), - self: links['self']); - } - } - throw ParserException('Can not parse ResourceObject from $json'); - } - - /// Parse the document - ResourceData parseResourceData(Object json) { - if (json is Map) { - final links = parseLinks(json['links']); - final included = json['included']; - final resources = []; - if (included is List) { - resources.addAll(included.map(parseResourceObject)); - } - final data = parseResourceObject(json['data']); - return ResourceData(data, - self: links['self'], - included: resources.isNotEmpty ? resources : null); - } - throw ParserException('Can not parse SingleResourceObject from $json'); - } - - /// Parse the document - ResourceCollectionData parseResourceCollectionData(Object json) { - if (json is Map) { - final links = parseLinks(json['links']); - final included = json['included']; - final resources = []; - if (included is List) { - resources.addAll(included.map(parseResourceObject)); - } - final data = json['data']; - if (data is List) { - return ResourceCollectionData(data.map(parseResourceObject), - self: links['self'], - pagination: Pagination.fromLinks(links), - included: resources.isNotEmpty ? resources : null); - } - } - throw ParserException('Can not parse ResourceObjectCollection from $json'); - } - - ToOne parseToOne(Object json) { - if (json is Map) { - final links = parseLinks(json['links']); - if (json.containsKey('data')) { - final data = json['data']; - if (data == null) { - return ToOne.empty(self: links['self'], related: links['related']); - } - if (data is Map) { - return ToOne(parseIdentifierObject(data), - self: links['self'], related: links['related']); - } - } - } - throw ParserException('Can not parse ToOne from $json'); - } - - ToMany parseToMany(Object json) { - if (json is Map) { - final links = parseLinks(json['links']); - if (json.containsKey('data')) { - final data = json['data']; - if (data is List) { - return ToMany(data.map(parseIdentifierObject), - self: links['self'], related: links['related']); - } - } - } - throw ParserException('Can not parse ToMany from $json'); - } - - IdentifierObject parseIdentifierObject(Object json) { - if (json is Map) { - return IdentifierObject(json['type'], json['id']); - } - throw ParserException('Can not parse IdentifierObject from $json'); - } - - Link parseLink(Object json) { - if (json is String) return Link(Uri.parse(json)); - if (json is Map) { - return LinkObject(Uri.parse(json['href']), meta: json['meta']); - } - throw ParserException('Can not parse Link from $json'); - } - - /// Parses the document's `links` member into a map. - /// The retuning map does not have null values. - /// - /// Details on the `links` member: https://jsonapi.org/format/#document-links - Map parseLinks(Object json) { - if (json == null) return {}; - if (json is Map) { - return (json..removeWhere((_, v) => v == null)) - .map((k, v) => MapEntry(k.toString(), parseLink(v))); - } - throw ParserException('Can not parse links from $json'); - } -} diff --git a/lib/src/client/client.dart b/lib/src/client/client.dart index 4fde6fa..eb57552 100644 --- a/lib/src/client/client.dart +++ b/lib/src/client/client.dart @@ -2,11 +2,11 @@ import 'dart:async'; import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:json_api/document.dart'; -import 'package:json_api/parser.dart'; import 'package:json_api/src/client/response.dart'; import 'package:json_api/src/client/status_code.dart'; import 'package:json_api/src/nullable.dart'; +import 'package:json_api_document/document.dart'; +import 'package:json_api_document/parser.dart'; typedef Document ResponseParser(Object j); diff --git a/lib/src/client/response.dart b/lib/src/client/response.dart index 5cd4529..76fdcac 100644 --- a/lib/src/client/response.dart +++ b/lib/src/client/response.dart @@ -1,6 +1,6 @@ -import 'package:json_api/document.dart'; import 'package:json_api/src/client/status_code.dart'; import 'package:json_api/src/nullable.dart'; +import 'package:json_api_document/document.dart'; /// A response returned by JSON:API client class Response { diff --git a/lib/src/document/document.dart b/lib/src/document/document.dart deleted file mode 100644 index bcd87fc..0000000 --- a/lib/src/document/document.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:json_api/src/document/error.dart'; -import 'package:json_api/src/document/primary_data.dart'; - -class Document { - /// The Primary Data - final Data data; - - final List errors; - final Map meta; - - Document(this.data, {Map meta}) - : this.errors = null, - this.meta = (meta == null || meta.isEmpty ? null : Map.from(meta)); - - Document.error(Iterable errors, {Map meta}) - : this.data = null, - this.errors = List.from(errors), - this.meta = (meta == null || meta.isEmpty ? null : Map.from(meta)); - - Document.empty(Map meta) - : this.data = null, - this.errors = null, - this.meta = (meta == null || meta.isEmpty ? null : Map.from(meta)) { - ArgumentError.checkNotNull(meta, 'meta'); - } - - Map toJson() { - Map json = {}; - if (data != null) { - json = data.toJson(); - } else if (errors != null) { - json = {'errors': errors}; - } - if (meta != null && meta.isNotEmpty) { - json['meta'] = meta; - } - // TODO: add `jsonapi` member - return json; - } -} diff --git a/lib/src/document/error.dart b/lib/src/document/error.dart deleted file mode 100644 index ae97082..0000000 --- a/lib/src/document/error.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'link.dart'; - -/// [JsonApiError] is a JSON object representing an occurred on the server. -/// -/// More on this: https://jsonapi.org/format/#errors -class JsonApiError { - /// A unique identifier for this particular occurrence of the problem. - String id; - - /// A link that leads to further details about this particular occurrence of the problem. - Link about; - - /// The HTTP status code applicable to this problem, expressed as a string value. - String status; - - /// An application-specific error code, expressed as a string value. - String code; - - /// A short, human-readable summary of the problem that SHOULD NOT change - /// from occurrence to occurrence of the problem, except for purposes of localization. - String title; - - /// A human-readable explanation specific to this occurrence of the problem. - /// Like title, this field’s value can be localized. - String detail; - - /// A JSON Pointer [RFC6901] to the associated entity in the request document - /// [e.g. "/data" for a primary data object, or "/data/attributes/title" for a specific attribute]. - String sourcePointer; - - /// A string indicating which URI query parameter caused the error. - String sourceParameter; - - /// A meta object containing non-standard meta-information about the error. - final meta = {}; - - JsonApiError( - {this.id, - this.about, - this.status, - this.code, - this.title, - this.detail, - this.sourceParameter, - this.sourcePointer, - Map meta}) { - this.meta.addAll(meta ?? {}); - } - - Map toJson() { - final json = {}; - if (id != null) json['id'] = id; - if (status != null) json['status'] = status; - if (code != null) json['code'] = code; - if (title != null) json['title'] = title; - if (detail != null) json['detail'] = detail; - if (meta.isNotEmpty) json['meta'] = meta; - if (about != null) json['links'] = {'about': about}; - final source = Map(); - if (sourcePointer != null) source['pointer'] = sourcePointer; - if (sourceParameter != null) source['parameter'] = sourceParameter; - if (source.isNotEmpty) json['source'] = source; - return json; - } -} diff --git a/lib/src/document/identifier.dart b/lib/src/document/identifier.dart deleted file mode 100644 index 8314087..0000000 --- a/lib/src/document/identifier.dart +++ /dev/null @@ -1,19 +0,0 @@ -/// Resource identifier -/// -/// Together with [Resource] forms the core of the Document model. -/// Identifiers are passed between the server and the client in the form -/// of [IdentifierObject]s. -class Identifier { - /// Resource type - final String type; - - /// Resource id - final String id; - - /// Neither [type] nor [id] can be null or empty. - Identifier(this.type, this.id) { - // TODO: check for emptiness - ArgumentError.checkNotNull(id, 'id'); - ArgumentError.checkNotNull(type, 'type'); - } -} diff --git a/lib/src/document/identifier_object.dart b/lib/src/document/identifier_object.dart deleted file mode 100644 index 2f25ca3..0000000 --- a/lib/src/document/identifier_object.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:json_api/src/document/identifier.dart'; - -/// [IdentifierObject] is a JSON representation of the [Identifier]. -/// It carries all JSON-related logic and the Meta-data. -class IdentifierObject { - final String type; - final String id; - - IdentifierObject(this.type, this.id); - - static IdentifierObject fromIdentifier(Identifier id) => - IdentifierObject(id.type, id.id); - - Identifier toIdentifier() => Identifier(type, id); - - toJson() => {'type': type, 'id': id}; -} diff --git a/lib/src/document/link.dart b/lib/src/document/link.dart deleted file mode 100644 index ebfcd90..0000000 --- a/lib/src/document/link.dart +++ /dev/null @@ -1,27 +0,0 @@ -/// A JSON:API link -/// https://jsonapi.org/format/#document-links -class Link { - final Uri uri; - - Link(this.uri) { - ArgumentError.checkNotNull(uri, 'uri'); - } - - toJson() => uri.toString(); -} - -/// A JSON:API link object -/// https://jsonapi.org/format/#document-links -class LinkObject extends Link { - final Map meta; - - LinkObject(Uri href, {Map meta}) - : meta = Map.unmodifiable(meta ?? {}), - super(href); - - toJson() { - final json = {'href': uri.toString()}; - if (meta != null && meta.isNotEmpty) json['meta'] = meta; - return json; - } -} diff --git a/lib/src/document/pagination.dart b/lib/src/document/pagination.dart deleted file mode 100644 index 78300df..0000000 --- a/lib/src/document/pagination.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:json_api/src/document/link.dart'; - -class Pagination { - final Link prev; - final Link next; - final Link first; - final Link last; - - Pagination({this.last, this.first, this.prev, this.next}); - - const Pagination.empty() - : prev = null, - next = null, - first = null, - last = null; - - Map toLinks() => { - 'prev': prev, - 'next': next, - 'first': first, - 'last': last - }..removeWhere((_, v) => v == null); - - static Pagination fromLinks(Map links) => Pagination( - first: links['first'], - last: links['last'], - next: links['next'], - prev: links['prev'], - ); -} diff --git a/lib/src/document/primary_data.dart b/lib/src/document/primary_data.dart deleted file mode 100644 index 11ba8ca..0000000 --- a/lib/src/document/primary_data.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:json_api/src/document/link.dart'; - -/// The top-level Primary Data. This is the essentials of the JSON:API Document. -/// -/// [PrimaryData] may be considered a Document itself with two limitations: -/// -/// - it always has the `data` key (could have `null` value for empty to-one relationships) -/// -/// - it can not have `meta` and `jsonapi` keys -abstract class PrimaryData { - final Link self; - - PrimaryData({this.self}); - - /// The top-level `links` object - Map toLinks() => self == null ? {} : {'self': self}; - - /// Top-level JSON object - Map toJson(); -} diff --git a/lib/src/document/relationship.dart b/lib/src/document/relationship.dart deleted file mode 100644 index f30150d..0000000 --- a/lib/src/document/relationship.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:json_api/src/document/identifier.dart'; -import 'package:json_api/src/document/identifier_object.dart'; -import 'package:json_api/src/document/link.dart'; -import 'package:json_api/src/document/pagination.dart'; -import 'package:json_api/src/document/primary_data.dart'; - -/// The Relationship represents the references between the resources. -/// -/// A Relationship can be a JSON:API Document itself when -/// requested separately as described here https://jsonapi.org/format/#fetching-relationships. -/// -/// It can also be a part of [ResourceObject].relationships map. -/// -/// More on this: https://jsonapi.org/format/#document-resource-object-relationships -class Relationship extends PrimaryData { - final Link related; - - Relationship({this.related, Link self}) : super(self: self); - - Map toLinks() => related == null - ? super.toLinks() - : (super.toLinks()..['related'] = related); - - /// Top-level JSON object - Map toJson() { - final json = {}; - final links = toLinks(); - if (links.isNotEmpty) json['links'] = links; - return json; - } -} - -/// Relationship to-one -class ToOne extends Relationship { - /// Resource Linkage - /// - /// Can be null for empty relationships - /// - /// More on this: https://jsonapi.org/format/#document-resource-object-linkage - final IdentifierObject linkage; - - ToOne(this.linkage, {Link self, Link related}) - : super(self: self, related: related); - - ToOne.empty({Link self, Link related}) - : linkage = null, - super(self: self, related: related); - - Map toJson() => super.toJson()..['data'] = linkage; - - /// Converts to [Identifier]. - /// For empty relationships return null. - Identifier toIdentifier() => linkage?.toIdentifier(); -} - -/// Relationship to-many -class ToMany extends Relationship { - /// Resource Linkage - /// - /// Can be empty for empty relationships - /// - /// More on this: https://jsonapi.org/format/#document-resource-object-linkage - final linkage = []; - - final Pagination pagination; - - ToMany(Iterable linkage, - {Link self, Link related, this.pagination = const Pagination.empty()}) - : super(self: self, related: related) { - this.linkage.addAll(linkage); - } - - Map toLinks() => super.toLinks()..addAll(pagination.toLinks()); - - Map toJson() => super.toJson()..['data'] = linkage; - - /// Converts to List<[Identifier]>. - /// For empty relationships returns an empty List. - Iterable toIdentifiers() => linkage.map((_) => _.toIdentifier()); -} diff --git a/lib/src/document/resource.dart b/lib/src/document/resource.dart deleted file mode 100644 index 4a87304..0000000 --- a/lib/src/document/resource.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:json_api/src/document/identifier.dart'; - -/// Resource -/// -/// Together with [Identifier] forms the core of the Document model. -/// Resources are passed between the server and the client in the form -/// of [ResourceObject]s. -class Resource { - /// Resource type - final String type; - - /// Resource id - /// - /// May be null for resources to be created on the cars_server - final String id; - - /// Resource attributes - final attributes = {}; - - /// to-one relationships - final toOne = {}; - - /// to-many relationships - final toMany = >{}; - - /// True if the Resource has a non-empty id - bool get hasId => id != null && id.isNotEmpty; - - Resource(this.type, this.id, - {Map attributes, - Map toOne, - Map> toMany}) { - ArgumentError.checkNotNull(type, 'type'); - this.attributes.addAll(attributes ?? {}); - this.toOne.addAll(toOne ?? {}); - this.toMany.addAll(toMany ?? {}); - } -} diff --git a/lib/src/document/resource_collection_data.dart b/lib/src/document/resource_collection_data.dart deleted file mode 100644 index c887860..0000000 --- a/lib/src/document/resource_collection_data.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:json_api/src/document/link.dart'; -import 'package:json_api/src/document/pagination.dart'; -import 'package:json_api/src/document/primary_data.dart'; -import 'package:json_api/src/document/resource_object.dart'; - -/// Represents a resource collection or a collection of related resources of a to-many relationship -class ResourceCollectionData extends PrimaryData { - final collection = []; - final Pagination pagination; - - /// For Compound Documents this member contains the included resources - final List included; - - ResourceCollectionData(Iterable collection, - {Link self, - Iterable included, - this.pagination = const Pagination.empty()}) - : this.included = - (included == null || included.isEmpty ? null : List.from(included)), - super(self: self) { - this.collection.addAll(collection); - } - - @override - Map toJson() { - final json = {'data': collection}; - if (included != null && included.isNotEmpty) { - json['included'] = included; - } - - final links = toLinks()..addAll(pagination.toLinks()); - if (links.isNotEmpty) json['links'] = links; - return json; - } -} diff --git a/lib/src/document/resource_data.dart b/lib/src/document/resource_data.dart deleted file mode 100644 index c0118f9..0000000 --- a/lib/src/document/resource_data.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:json_api/src/document/link.dart'; -import 'package:json_api/src/document/primary_data.dart'; -import 'package:json_api/src/document/resource.dart'; -import 'package:json_api/src/document/resource_object.dart'; - -/// Represents a single resource or a single related resource of a to-one relationship\\\\\\\\ -class ResourceData extends PrimaryData { - final ResourceObject resourceObject; - - /// For Compound Documents this member contains the included resources - final List included; - - ResourceData(this.resourceObject, - {Link self, Iterable included}) - : this.included = - (included == null || included.isEmpty ? null : List.from(included)), - super(self: self); - - @override - Map toJson() { - final json = {'data': resourceObject}; - if (included != null && included.isNotEmpty) { - json['included'] = included; - } - - final links = toLinks(); - if (links.isNotEmpty) json['links'] = links; - return json; - } - - Resource toResource() => resourceObject.toResource(); -} diff --git a/lib/src/document/resource_object.dart b/lib/src/document/resource_object.dart deleted file mode 100644 index c385fe4..0000000 --- a/lib/src/document/resource_object.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'package:json_api/src/document/identifier.dart'; -import 'package:json_api/src/document/identifier_object.dart'; -import 'package:json_api/src/document/link.dart'; -import 'package:json_api/src/document/relationship.dart'; -import 'package:json_api/src/document/resource.dart'; -import 'package:json_api/src/nullable.dart'; - -/// [ResourceObject] is a JSON representation of a [Resource]. -/// -/// It carries all JSON-related logic and the Meta-data. -/// In a JSON:API Document it can be the value of the `data` member (a `data` -/// member element in case of a collection) or a member of the `included` -/// resource collection. -/// -/// More on this: https://jsonapi.org/format/#document-resource-objects -class ResourceObject { - final String type; - final String id; - final Link self; - final attributes = {}; - final relationships = {}; - - ResourceObject(this.type, this.id, - {Map attributes, - Map relationships, - this.self}) { - this.attributes.addAll(attributes ?? {}); - this.relationships.addAll(relationships ?? {}); - } - - static ResourceObject fromResource(Resource resource) { - final relationships = {} - ..addAll(resource.toOne.map((k, v) => - MapEntry(k, ToOne(nullable(IdentifierObject.fromIdentifier)(v))))) - ..addAll(resource.toMany.map((k, v) => - MapEntry(k, ToMany(v.map(IdentifierObject.fromIdentifier))))); - - return ResourceObject(resource.type, resource.id, - attributes: resource.attributes, relationships: relationships); - } - - /// Returns the JSON object to be used in the `data` or `included` members - /// of a JSON:API Document - Map toJson() { - final json = {'type': type, 'id': id}; - if (attributes.isNotEmpty) { - json['attributes'] = attributes; - } - if (relationships.isNotEmpty) { - json['relationships'] = relationships; - } - if (self != null) { - json['links'] = {'self': self}; - } - return json; - } - - /// Converts to [Resource] if possible. The standard allows relationships - /// without `data` member. In this case the original [Resource] can not be - /// recovered and this method will throw a [StateError]. - /// - /// TODO: we probably need `isIncomplete` flag to check for this. - Resource toResource() { - final toOne = {}; - final toMany = >{}; - final incomplete = {}; - relationships.forEach((name, rel) { - if (rel is ToOne) { - toOne[name] = rel.toIdentifier(); - } else if (rel is ToMany) { - toMany[name] = rel.toIdentifiers().toList(); - } else { - incomplete[name] = rel; - } - }); - - if (incomplete.isNotEmpty) { - throw StateError('Can not convert to resource' - ' due to incomplete relationships data: ${incomplete.keys}'); - } - - return Resource(type, id, - attributes: attributes, toOne: toOne, toMany: toMany); - } -} diff --git a/lib/src/server/controller.dart b/lib/src/server/controller.dart index 6000cca..c7406b8 100644 --- a/lib/src/server/controller.dart +++ b/lib/src/server/controller.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'dart:io'; -import 'package:json_api/document.dart'; import 'package:json_api/src/server/collection.dart'; import 'package:json_api/src/server/request_target.dart'; +import 'package:json_api_document/document.dart'; abstract class JsonApiController { Future fetchCollection( diff --git a/lib/src/server/document_builder.dart b/lib/src/server/document_builder.dart index 7c9dcb7..af333cb 100644 --- a/lib/src/server/document_builder.dart +++ b/lib/src/server/document_builder.dart @@ -1,6 +1,6 @@ -import 'package:json_api/document.dart'; import 'package:json_api/src/server/collection.dart'; import 'package:json_api/src/server/request_target.dart'; +import 'package:json_api_document/document.dart'; /// The Document builder is used by JsonApiServer. It abstracts the process /// of building response documents and is responsible for such aspects as diff --git a/lib/src/server/server.dart b/lib/src/server/server.dart index f0ead75..aacd444 100644 --- a/lib/src/server/server.dart +++ b/lib/src/server/server.dart @@ -2,13 +2,13 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:json_api/document.dart'; -import 'package:json_api/parser.dart'; import 'package:json_api/src/server/collection.dart'; import 'package:json_api/src/server/controller.dart'; import 'package:json_api/src/server/document_builder.dart'; import 'package:json_api/src/server/request_target.dart'; import 'package:json_api/src/server/router.dart'; +import 'package:json_api_document/document.dart'; +import 'package:json_api_document/parser.dart'; class JsonApiServer { final URLDesign url; diff --git a/lib/src/server/standard_document_builder.dart b/lib/src/server/standard_document_builder.dart index c91379a..20ad485 100644 --- a/lib/src/server/standard_document_builder.dart +++ b/lib/src/server/standard_document_builder.dart @@ -1,11 +1,10 @@ -import 'package:json_api/document.dart'; -import 'package:json_api/src/document/pagination.dart'; import 'package:json_api/src/nullable.dart'; import 'package:json_api/src/server/collection.dart'; import 'package:json_api/src/server/document_builder.dart'; import 'package:json_api/src/server/page.dart'; import 'package:json_api/src/server/request_target.dart'; import 'package:json_api/src/server/router.dart'; +import 'package:json_api_document/document.dart'; class StandardDocumentBuilder implements DocumentBuilder { final URLDesign design; diff --git a/pubspec.yaml b/pubspec.yaml index 34b6a6b..d76b27d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,8 @@ description: "JSON:API v1.0 (http://jsonapi.org) Document, Client, and Server" homepage: "https://github.com/f3ath/json-api-dart" name: "json_api" version: "0.5.0" -dependencies: +dependencies: + json_api_document: "^0.5.0" collection: "^1.14.11" http: "^0.12.0" dev_dependencies: diff --git a/test/functional/create_test.dart b/test/functional/create_test.dart index 8e56dae..163ea31 100644 --- a/test/functional/create_test.dart +++ b/test/functional/create_test.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:json_api/client.dart'; -import 'package:json_api/document.dart'; +import 'package:json_api_document/document.dart'; import 'package:test/test.dart'; import '../../example/cars_server.dart'; diff --git a/test/functional/fetch_test.dart b/test/functional/fetch_test.dart index e1f112e..d5b7efe 100644 --- a/test/functional/fetch_test.dart +++ b/test/functional/fetch_test.dart @@ -1,7 +1,7 @@ import 'dart:io'; import 'package:json_api/client.dart'; -import 'package:json_api/document.dart'; +import 'package:json_api_document/document.dart'; import 'package:test/test.dart'; import '../../example/cars_server.dart'; diff --git a/test/functional/update_test.dart b/test/functional/update_test.dart index 5c4fcfd..0054512 100644 --- a/test/functional/update_test.dart +++ b/test/functional/update_test.dart @@ -1,7 +1,7 @@ import 'dart:io'; import 'package:json_api/client.dart'; -import 'package:json_api/document.dart'; +import 'package:json_api_document/document.dart'; import 'package:test/test.dart'; import '../../example/cars_server.dart'; diff --git a/test/unit/document_test.dart b/test/unit/document_test.dart deleted file mode 100644 index 3a64112..0000000 --- a/test/unit/document_test.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:json_api/document.dart'; -import 'package:json_matcher/json_matcher.dart'; -import 'package:test/test.dart'; - -void main() { - group('Document', () { - group('JSON Conversion', () { - test('Can convert a single resource', () { - final doc = Document(ResourceData(ResourceObject('foo', 'bar'))); - - expect( - doc, - encodesToJson({ - 'data': {'type': 'foo', 'id': 'bar'} - })); - }); - }); - }); -} diff --git a/test/unit/example.json b/test/unit/example.json deleted file mode 100644 index 3ccb7fc..0000000 --- a/test/unit/example.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "links": { - "self": "http://example.com/articles", - "next": "http://example.com/articles?page=2", - "last": "http://example.com/articles?page=10" - }, - "data": [ - { - "type": "articles", - "id": "1", - "attributes": { - "title": "JSON:API paints my bikeshed!" - }, - "relationships": { - "author": { - "links": { - "self": "http://example.com/articles/1/relationships/author", - "related": "http://example.com/articles/1/author" - }, - "data": { - "type": "people", - "id": "9" - } - }, - "comments": { - "links": { - "self": "http://example.com/articles/1/relationships/comments", - "related": "http://example.com/articles/1/comments" - }, - "data": [ - { - "type": "comments", - "id": "5" - }, - { - "type": "comments", - "id": "12" - } - ] - } - }, - "links": { - "self": "http://example.com/articles/1" - } - } - ], - "included": [ - { - "type": "people", - "id": "9", - "attributes": { - "firstName": "Dan", - "lastName": "Gebhardt", - "twitter": "dgeb" - }, - "links": { - "self": "http://example.com/people/9" - } - }, - { - "type": "comments", - "id": "5", - "attributes": { - "body": "First!" - }, - "relationships": { - "author": { - "data": { - "type": "people", - "id": "2" - } - } - }, - "links": { - "self": "http://example.com/comments/5" - } - }, - { - "type": "comments", - "id": "12", - "attributes": { - "body": "I like XML better" - }, - "relationships": { - "author": { - "data": { - "type": "people", - "id": "9" - } - } - }, - "links": { - "self": "http://example.com/comments/12" - } - } - ] -} \ No newline at end of file diff --git a/test/unit/parser_test.dart b/test/unit/parser_test.dart deleted file mode 100644 index 89c1be2..0000000 --- a/test/unit/parser_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:json_api/parser.dart'; -import 'package:json_matcher/json_matcher.dart'; -import 'package:test/test.dart'; - -void main() { - final parser = JsonApiParser(); - group('Parser', () { - try { - test('Can parse the example document', () { - // This is a slightly modified example from the JSON:API site - // See: https://jsonapi.org/ - final jsonString = - new File('test/unit/example.json').readAsStringSync(); - final jsonObject = json.decode(jsonString); - final doc = parser.parseDocument( - jsonObject, parser.parseResourceCollectionData); - - expect(doc, encodesToJson(jsonObject)); - }); - } catch (e, s) { - print(s); - } - }, testOn: 'vm'); -}