From 6c1dd24bfab42ced5004378fd10a1c5e91ebb446 Mon Sep 17 00:00:00 2001 From: f3ath Date: Wed, 21 Apr 2021 22:12:35 -0700 Subject: [PATCH 1/3] Release --- README.md | 42 ++++++++- example/client.dart | 36 +++++-- lib/client.dart | 24 ++++- lib/src/client/client.dart | 26 ++---- lib/src/client/request.dart | 11 ++- lib/src/client/response.dart | 12 ++- lib/src/client/response/request_failure.dart | 8 +- lib/src/client/routing_client.dart | 98 +++++++++++--------- lib/src/document/outbound_document.dart | 22 +++-- lib/src/http/http_response.dart | 11 +++ lib/src/http/payload_codec.dart | 18 ++-- pubspec.yaml | 2 +- test/contract/errors_test.dart | 8 +- 13 files changed, 211 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 7ab02be..06f7d06 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,44 @@ -# JSON:API Client and Server for Dart/Flutter. Version 5. +# JSON:API Client and Server + +TL;DR: +```dart +import 'package:json_api/client.dart'; +import 'package:json_api/routing.dart'; + +void main() async { + /// Define the server's base URL + final baseUri = 'http://localhost:8080'; + + /// Use the standard recommended URL structure or implement your own + final uriDesign = StandardUriDesign(Uri.parse(baseUri)); + + /// The [RoutingClient] is most likely the right choice. + /// It has methods covering many standard use cases. + final client = RoutingClient(uriDesign); + + try { + /// Fetch the collection. + /// See other methods to query and manipulate resources. + final response = await client.fetchCollection('colors'); + + final resources = response.collection; + resources.map((resource) => resource.attributes).forEach((attr) { + final name = attr['name']; + final red = attr['red']; + final green = attr['green']; + final blue = attr['blue']; + print('$name - $red:$green:$blue'); + }); + } on RequestFailure catch (e) { + /// Catch error response + e.errors.forEach((error) => print('${error.title}')); + } +} +``` +This is a work-in-progress. You can help it by submitting a PR with a feature or documentation improvements. + + -[JSON:API] is a specification for building JSON APIs. The documentation is a work-in-progress. [JSON:API]: https://jsonapi.org diff --git a/example/client.dart b/example/client.dart index da5f693..6e9c04a 100644 --- a/example/client.dart +++ b/example/client.dart @@ -1,14 +1,32 @@ import 'package:json_api/client.dart'; import 'package:json_api/routing.dart'; -// START THE SERVER FIRST! void main() async { - final host = 'localhost'; - final port = 8080; - final uri = Uri(scheme: 'http', host: host, port: port); - final client = RoutingClient(StandardUriDesign(uri)); - final response = await client.fetchCollection('colors'); - response.collection.map((resource) => resource.attributes).forEach((attr) { - print('${attr['name']} - ${attr['red']}:${attr['green']}:${attr['blue']}'); - }); + /// Define the server's base URL + final baseUri = 'http://localhost:8080'; + + /// Use the standard recommended URL structure or implement your own + final uriDesign = StandardUriDesign(Uri.parse(baseUri)); + + /// The [RoutingClient] is most likely the right choice. + /// It has methods covering many standard use cases. + final client = RoutingClient(uriDesign); + + try { + /// Fetch the collection. + /// See other methods to query and manipulate resources. + final response = await client.fetchCollection('colors'); + + final resources = response.collection; + resources.map((resource) => resource.attributes).forEach((attr) { + final name = attr['name']; + final red = attr['red']; + final green = attr['green']; + final blue = attr['blue']; + print('$name - $red:$green:$blue'); + }); + } on RequestFailure catch (e) { + /// Catch error response + e.errors.forEach((error) => print('${error.title}')); + } } diff --git a/lib/client.dart b/lib/client.dart index 56e6904..9a383cb 100644 --- a/lib/client.dart +++ b/lib/client.dart @@ -1,4 +1,26 @@ -/// JSON:API client for Flutter, browsers and vm. +/// Provides JSON:API client for Flutter, browsers and vm. +/// +/// There are two clients implementation provided by this library. +/// +/// The firs one, [Client], is the most flexible but low level. It operates +/// generic [Request] and [Response] objects and performs basic operations +/// such as JSON conversion and error handling. It is agnostic to the document +/// structure and accepts any target URIs. +/// +/// By default, the [DisposableHandler] is used which internally creates +/// a new instance of Dart built-in HTTP client for each request and then +/// disposes it. If you want more control of the underlying http client, +/// one option can be to use the [PersistentHandler]. To use another HTTP client, +/// such as [dio](https://pub.dev/packages/dio) implement your own wrapper. +/// +/// The [codec] performs JSON encoding/decoding. The default implementation +/// uses native `dart:convert`. Provide your own [PayloadCodec] if you need +/// fine-grained control over JSON conversion. +/// +/// The [RoutingClient] is a wrapper over [Client] containing methods +/// representing the most common use cases of resource fetching and manipulation. +/// It can conveniently construct and parse JSON:API documents and URIs. +/// The [RoutingClient] should be your default choice. library client; export 'package:json_api/src/client/client.dart'; diff --git a/lib/src/client/client.dart b/lib/src/client/client.dart index 60e35cc..1a126a4 100644 --- a/lib/src/client/client.dart +++ b/lib/src/client/client.dart @@ -2,9 +2,12 @@ import 'package:json_api/http.dart'; import 'package:json_api/src/client/disposable_handler.dart'; import 'package:json_api/src/client/request.dart'; import 'package:json_api/src/client/response.dart'; -import 'package:json_api/src/client/response/request_failure.dart'; -/// A basic JSON:API client +/// A basic JSON:API client. +/// +/// The JSON:API [Request] is converted to [HttpRequest] and sent downstream +/// using the [handler]. Received [HttpResponse] is then converted back to +/// JSON:API [Response]. JSON conversion is performed by the [codec]. class Client { const Client( {PayloadCodec codec = const PayloadCodec(), @@ -15,8 +18,7 @@ class Client { final HttpHandler _http; final PayloadCodec _codec; - /// Sends the [request] to the server. - /// Throws a [RequestFailure] if the server responds with an error. + /// Sends the [request] to the given [uri]. Future send(Uri uri, Request request) async { final body = await _encode(request.document); final response = await _http.handle(HttpRequest( @@ -31,23 +33,13 @@ class Client { ...request.headers })); - final json = await _decode(response); - if (StatusCode(response.statusCode).isFailed) { - throw RequestFailure(response, json); - } - return Response(response, json); + final document = await _decode(response); + return Response(response, document); } Future _encode(Object? doc) async => doc == null ? '' : await _codec.encode(doc); Future _decode(HttpResponse response) async => - _isJsonApi(response) ? await _codec.decode(response.body) : null; - - /// True if body is not empty and Content-Type is application/vnd.api+json - bool _isJsonApi(HttpResponse response) => - response.body.isNotEmpty && - (response.headers['Content-Type'] ?? '') - .toLowerCase() - .startsWith(mediaType); + response.hasDocument ? await _codec.decode(response.body) : null; } diff --git a/lib/src/client/request.dart b/lib/src/client/request.dart index 35b10aa..e9fa7c2 100644 --- a/lib/src/client/request.dart +++ b/lib/src/client/request.dart @@ -1,21 +1,30 @@ import 'package:json_api/http.dart'; import 'package:json_api/query.dart'; +import 'package:json_api/src/client/client.dart'; -/// JSON:API request consumed by the client +/// A generic JSON:API request. class Request with HttpHeaders { + /// Creates a new instance if the request with the specified HTTP [method] + /// and [document]. Request(this.method, [this.document]); + /// Creates a GET request. Request.get() : this('get'); + /// Creates a POST request. Request.post([Object? document]) : this('post', document); + /// Creates a DELETE request. Request.delete([Object? document]) : this('delete', document); + /// Creates a PATCH request. Request.patch([Object? document]) : this('patch', document); /// HTTP method final String method; + /// JSON:API document. This object can be of any type as long as it is + /// encodable by the [PayloadCodec] used in the [Client]. final Object? document; /// Query parameters diff --git a/lib/src/client/response.dart b/lib/src/client/response.dart index 4e8705f..567c0e7 100644 --- a/lib/src/client/response.dart +++ b/lib/src/client/response.dart @@ -1,11 +1,13 @@ -import 'package:json_api/http.dart' as h; +import 'package:json_api/http.dart'; +/// A generic JSON:API response. class Response { - Response(this.http, this.json); + Response(this.http, this.document); /// HTTP response - final h.HttpResponse http; + final HttpResponse http; + + /// Decoded JSON document + final Map? document; - /// Raw JSON response - final Map? json; } diff --git a/lib/src/client/response/request_failure.dart b/lib/src/client/response/request_failure.dart index 72934a9..924c729 100644 --- a/lib/src/client/response/request_failure.dart +++ b/lib/src/client/response/request_failure.dart @@ -3,10 +3,10 @@ import 'package:json_api/http.dart'; /// Thrown when the server returns a non-successful response. class RequestFailure implements Exception { - RequestFailure(this.http, Map? json) { - if (json != null) { - errors.addAll(InboundDocument(json).errors()); - meta.addAll(InboundDocument(json).meta()); + RequestFailure(this.http, Map? document) { + if (document != null) { + errors.addAll(InboundDocument(document).errors()); + meta.addAll(InboundDocument(document).meta()); } } diff --git a/lib/src/client/routing_client.dart b/lib/src/client/routing_client.dart index 1353383..48c8a2f 100644 --- a/lib/src/client/routing_client.dart +++ b/lib/src/client/routing_client.dart @@ -1,3 +1,4 @@ +import 'package:json_api/client.dart'; import 'package:json_api/document.dart'; import 'package:json_api/routing.dart'; import 'package:json_api/src/client/client.dart'; @@ -13,10 +14,11 @@ import 'package:json_api/src/client/response/resource_updated.dart'; /// A routing JSON:API client class RoutingClient { - RoutingClient(this._uri, {Client client = const Client()}) : _client = client; + RoutingClient(this.baseUri, {Client client = const Client()}) + : _client = client; final Client _client; - final UriDesign _uri; + final UriDesign baseUri; /// Adds [identifiers] to a to-many relationship /// identified by [type], [id], [relationship]. @@ -30,11 +32,11 @@ class RoutingClient { List identifiers, { Map headers = const {}, }) async { - final response = await _client.send( - _uri.relationship(type, id, relationship), + final response = await send( + baseUri.relationship(type, id, relationship), Request.post(OutboundDataDocument.many(ToMany(identifiers))) ..headers.addAll(headers)); - return RelationshipUpdated.many(response.http, response.json); + return RelationshipUpdated.many(response.http, response.document); } /// Creates a new resource in the collection of type [type]. @@ -54,8 +56,8 @@ class RoutingClient { Map meta = const {}, Map headers = const {}, }) async { - final response = await _client.send( - _uri.collection(type), + final response = await send( + baseUri.collection(type), Request.post(OutboundDataDocument.newResource(NewResource(type) ..attributes.addAll(attributes) ..relationships.addAll({ @@ -66,7 +68,7 @@ class RoutingClient { ..headers.addAll(headers)); return ResourceCreated( - response.http, response.json ?? (throw FormatException())); + response.http, response.document ?? (throw FormatException())); } /// Deletes [identifiers] from a to-many relationship @@ -81,12 +83,12 @@ class RoutingClient { List identifiers, { Map headers = const {}, }) async { - final response = await _client.send( - _uri.relationship(type, id, relationship), + final response = await send( + baseUri.relationship(type, id, relationship), Request.delete(OutboundDataDocument.many(ToMany(identifiers))) ..headers.addAll(headers)); - return RelationshipUpdated.many(response.http, response.json); + return RelationshipUpdated.many(response.http, response.document); } /// Fetches a primary collection of type [type]. @@ -109,8 +111,8 @@ class RoutingClient { Iterable sort = const [], Map> fields = const {}, }) async { - final response = await _client.send( - _uri.collection(type), + final response = await send( + baseUri.collection(type), Request.get() ..headers.addAll(headers) ..query.addAll(query) @@ -120,7 +122,7 @@ class RoutingClient { ..sort(sort) ..fields(fields)); return CollectionFetched( - response.http, response.json ?? (throw FormatException())); + response.http, response.document ?? (throw FormatException())); } /// Fetches a related resource collection @@ -146,8 +148,8 @@ class RoutingClient { Map> fields = const {}, Map query = const {}, }) async { - final response = await _client.send( - _uri.related(type, id, relationship), + final response = await send( + baseUri.related(type, id, relationship), Request.get() ..headers.addAll(headers) ..query.addAll(query) @@ -157,7 +159,7 @@ class RoutingClient { ..sort(sort) ..fields(fields)); return CollectionFetched( - response.http, response.json ?? (throw FormatException())); + response.http, response.document ?? (throw FormatException())); } Future> fetchToOne( @@ -167,11 +169,10 @@ class RoutingClient { Map headers = const {}, Map query = const {}, }) async { - final response = await _client.send( - _uri.relationship(type, id, relationship), + final response = await send(baseUri.relationship(type, id, relationship), Request.get()..headers.addAll(headers)..query.addAll(query)); return RelationshipFetched.one( - response.http, response.json ?? (throw FormatException())); + response.http, response.document ?? (throw FormatException())); } Future> fetchToMany( @@ -181,11 +182,10 @@ class RoutingClient { Map headers = const {}, Map query = const {}, }) async { - final response = await _client.send( - _uri.relationship(type, id, relationship), + final response = await send(baseUri.relationship(type, id, relationship), Request.get()..headers.addAll(headers)..query.addAll(query)); return RelationshipFetched.many( - response.http, response.json ?? (throw FormatException())); + response.http, response.document ?? (throw FormatException())); } Future fetchRelatedResource( @@ -198,8 +198,8 @@ class RoutingClient { Iterable include = const [], Map> fields = const {}, }) async { - final response = await _client.send( - _uri.related(type, id, relationship), + final response = await send( + baseUri.related(type, id, relationship), Request.get() ..headers.addAll(headers) ..query.addAll(query) @@ -207,7 +207,7 @@ class RoutingClient { ..include(include) ..fields(fields)); return RelatedResourceFetched( - response.http, response.json ?? (throw FormatException())); + response.http, response.document ?? (throw FormatException())); } Future fetchResource( @@ -219,8 +219,8 @@ class RoutingClient { Map> fields = const {}, Map query = const {}, }) async { - final response = await _client.send( - _uri.resource(type, id), + final response = await send( + baseUri.resource(type, id), Request.get() ..headers.addAll(headers) ..query.addAll(query) @@ -229,7 +229,7 @@ class RoutingClient { ..fields(fields)); return ResourceFetched( - response.http, response.json ?? (throw FormatException())); + response.http, response.document ?? (throw FormatException())); } Future updateResource(String type, String id, @@ -238,8 +238,8 @@ class RoutingClient { Map> many = const {}, Map meta = const {}, Map headers = const {}}) async { - final response = await _client.send( - _uri.resource(type, id), + final response = await send( + baseUri.resource(type, id), Request.patch(OutboundDataDocument.resource(Resource(type, id) ..attributes.addAll(attributes) ..relationships.addAll({ @@ -248,7 +248,7 @@ class RoutingClient { }) ..meta.addAll(meta))) ..headers.addAll(headers)); - return ResourceUpdated(response.http, response.json); + return ResourceUpdated(response.http, response.document); } /// Creates a new resource with the given id on the server. @@ -261,8 +261,8 @@ class RoutingClient { Map meta = const {}, Map headers = const {}, }) async { - final response = await _client.send( - _uri.collection(type), + final response = await send( + baseUri.collection(type), Request.post(OutboundDataDocument.resource(Resource(type, id) ..attributes.addAll(attributes) ..relationships.addAll({ @@ -271,7 +271,7 @@ class RoutingClient { }) ..meta.addAll(meta))) ..headers.addAll(headers)); - return ResourceUpdated(response.http, response.json); + return ResourceUpdated(response.http, response.document); } Future> replaceToOne( @@ -281,11 +281,11 @@ class RoutingClient { Identifier identifier, { Map headers = const {}, }) async { - final response = await _client.send( - _uri.relationship(type, id, relationship), + final response = await send( + baseUri.relationship(type, id, relationship), Request.patch(OutboundDataDocument.one(ToOne(identifier))) ..headers.addAll(headers)); - return RelationshipUpdated.one(response.http, response.json); + return RelationshipUpdated.one(response.http, response.document); } Future> replaceToMany( @@ -295,23 +295,31 @@ class RoutingClient { Iterable identifiers, { Map headers = const {}, }) async { - final response = await _client.send( - _uri.relationship(type, id, relationship), + final response = await send( + baseUri.relationship(type, id, relationship), Request.patch(OutboundDataDocument.many(ToMany(identifiers))) ..headers.addAll(headers)); - return RelationshipUpdated.many(response.http, response.json); + return RelationshipUpdated.many(response.http, response.document); } Future> deleteToOne( String type, String id, String relationship, {Map headers = const {}}) async { - final response = await _client.send( - _uri.relationship(type, id, relationship), + final response = await send( + baseUri.relationship(type, id, relationship), Request.patch(OutboundDataDocument.one(ToOne.empty())) ..headers.addAll(headers)); - return RelationshipUpdated.one(response.http, response.json); + return RelationshipUpdated.one(response.http, response.document); } Future deleteResource(String type, String id) => - _client.send(_uri.resource(type, id), Request.delete()); + send(baseUri.resource(type, id), Request.delete()); + + Future send(Uri uri, Request request) async { + final response = await _client.send(uri, request); + if (response.http.isFailed) { + throw RequestFailure(response.http, response.document); + } + return response; + } } diff --git a/lib/src/document/outbound_document.dart b/lib/src/document/outbound_document.dart index aded1a1..381b764 100644 --- a/lib/src/document/outbound_document.dart +++ b/lib/src/document/outbound_document.dart @@ -2,15 +2,16 @@ import 'package:json_api/document.dart'; import 'package:json_api/src/document/link.dart'; import 'package:json_api/src/document/resource.dart'; -/// An empty outbound document. +/// A sever-to-client document. class OutboundDocument { /// The document "meta" object. final meta = {}; + /// Returns the JSON representation. Map toJson() => {'meta': meta}; } -/// An outbound error document. +/// A sever-to-client document with errors. class OutboundErrorDocument extends OutboundDocument { OutboundErrorDocument(Iterable errors) { this.errors.addAll(errors); @@ -26,31 +27,32 @@ class OutboundErrorDocument extends OutboundDocument { }; } -/// An outbound data document. +/// A sever-to-client document with data. class OutboundDataDocument extends OutboundDocument { /// Creates an instance of a document containing a single resource as the primary data. - OutboundDataDocument.resource(Resource? resource) : _data = resource; + OutboundDataDocument.resource(Resource? this.data); /// Creates an instance of a document containing a single to-be-created resource as the primary data. Used only in client-to-server requests. - OutboundDataDocument.newResource(NewResource resource) : _data = resource; + OutboundDataDocument.newResource(NewResource this.data); /// Creates an instance of a document containing a collection of resources as the primary data. OutboundDataDocument.collection(Iterable collection) - : _data = collection.toList(); + : data = collection.toList(); /// Creates an instance of a document containing a to-one relationship. - OutboundDataDocument.one(ToOne one) : _data = one.identifier { + OutboundDataDocument.one(ToOne one) : data = one.identifier { meta.addAll(one.meta); links.addAll(one.links); } /// Creates an instance of a document containing a to-many relationship. - OutboundDataDocument.many(ToMany many) : _data = many.toList() { + OutboundDataDocument.many(ToMany many) : data = many.toList() { meta.addAll(many.meta); links.addAll(many.links); } - final Object? _data; + /// Document data. + final Object? data; /// Links related to the primary data. final links = {}; @@ -60,7 +62,7 @@ class OutboundDataDocument extends OutboundDocument { @override Map toJson() => { - 'data': _data, + 'data': data, if (links.isNotEmpty) 'links': links, if (included.isNotEmpty) 'included': included, if (meta.isNotEmpty) 'meta': meta, diff --git a/lib/src/http/http_response.dart b/lib/src/http/http_response.dart index b0b23a3..76c3961 100644 --- a/lib/src/http/http_response.dart +++ b/lib/src/http/http_response.dart @@ -1,4 +1,6 @@ import 'package:json_api/src/http/http_message.dart'; +import 'package:json_api/src/http/media_type.dart'; +import 'package:json_api/src/http/status_code.dart'; /// The response sent by the server and received by the client class HttpResponse extends HttpMessage { @@ -6,4 +8,13 @@ class HttpResponse extends HttpMessage { /// Response status code final int statusCode; + + /// True if the body is not empty and the Content-Type + /// is `application/vnd.api+json` + bool get hasDocument => + body.isNotEmpty && + (headers['Content-Type'] ?? '').toLowerCase().startsWith(mediaType); + + /// Returns true if the [statusCode] represents a failure + bool get isFailed => StatusCode(statusCode).isFailed; } diff --git a/lib/src/http/payload_codec.dart b/lib/src/http/payload_codec.dart index 9c6d272..e3ed74b 100644 --- a/lib/src/http/payload_codec.dart +++ b/lib/src/http/payload_codec.dart @@ -1,14 +1,20 @@ +import 'dart:async'; import 'dart:convert'; -/// Encodes/decodes JSON payload +/// Encodes/decodes JSON payload. +/// +/// The methods are designed to be asynchronous to allow for conversion to be +/// performed in isolates if needed. class PayloadCodec { const PayloadCodec(); - Future decode(String body) async { - final json = jsonDecode(body); - if (json is Map) return json; - throw FormatException('Invalid JSON payload: ${json.runtimeType}'); + /// Decodes a JSON string into a Map + FutureOr decode(String json) { + final decoded = jsonDecode(json); + if (decoded is Map) return decoded; + throw FormatException('Invalid JSON payload: ${decoded.runtimeType}'); } - Future encode(Object document) async => jsonEncode(document); + /// Encodes a JSON:API document into a JSON string. + FutureOr encode(Object document) => jsonEncode(document); } diff --git a/pubspec.yaml b/pubspec.yaml index adefbcf..d147b5e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: json_api -version: 5.0.0-rc.1 +version: 5.0.0-rc.2 homepage: https://github.com/f3ath/json-api-dart description: A framework-agnostic implementations of JSON:API Client and Server. Supports JSON:API v1.0 (https://jsonapi.org) environment: diff --git a/test/contract/errors_test.dart b/test/contract/errors_test.dart index 559e93a..d399bcb 100644 --- a/test/contract/errors_test.dart +++ b/test/contract/errors_test.dart @@ -21,12 +21,8 @@ void main() { Uri.parse('/posts/1/relationships/author'), Request('head')), ]; for (final action in actions) { - try { - await action(); - fail('Exception expected'); - } on RequestFailure catch (response) { - expect(response.http.statusCode, 405); - } + final response = await action(); + expect(response.http.statusCode, 405); } }); test('Bad request when target can not be matched', () async { From e6aaf2fe621361644965e5acf00427875880e8d2 Mon Sep 17 00:00:00 2001 From: f3ath Date: Wed, 21 Apr 2021 22:20:43 -0700 Subject: [PATCH 2/3] Release --- CHANGELOG.md | 74 +++++++++++++++++++++++++--------------------------- pubspec.yaml | 7 ++++- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd15294..41100ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [5.0.0] - 2021-04-21 ### Added - Sound null-safety support. ### Changed - Everything. Again. This is another major **BC-breaking** rework. Please refer to - the API documentation, examples and tests. +the API documentation, examples and tests. + +## [3.2.3] - 2020-08-06 +### Fixed +- Call toJson() on resourceObject when serializing ([\#84](https://github.com/f3ath/json-api-dart/pull/84)) ## [4.3.0] - 2020-07-30 ### Added @@ -34,7 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - The client will not attempt to decode the body of the HTTP response with error status if the correct Content-Type - is missing. Before in such cases a `FormatException` would be thrown ([pr](https://github.com/f3ath/json-api-dart/pull/98)) +is missing. Before in such cases a `FormatException` would be thrown ([pr](https://github.com/f3ath/json-api-dart/pull/98)) ## [4.1.0] - 2020-05-28 ### Changed @@ -44,18 +48,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Everything. This is a major **BC-breaking** rework which affected pretty much all areas. Please refer to the documentation. -## [3.2.3] - 2020-08-06 -### Fixed -- Call toJson() on resourceObject when serializing ([#84](https://github.com/f3ath/json-api-dart/pull/84)) - - ## [3.2.2] - 2020-01-07 ### Fixed -- Can not decode related resource which is null ([#77](https://github.com/f3ath/json-api-dart/issues/77)) +- Can not decode related resource which is null ([\#77](https://github.com/f3ath/json-api-dart/issues/77)) ## [3.2.1] - 2020-01-01 ### Fixed -- Incorrect URL in the example in the Client documentation ([#74](https://github.com/f3ath/json-api-dart/issues/74)) +- Incorrect URL in the example in the Client documentation ([\#74](https://github.com/f3ath/json-api-dart/issues/74)) ## [3.2.0] - 2019-12-30 ### Added @@ -81,13 +80,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [3.0.0] - 2019-12-17 ### Added -- Support for custom non-standard links ([#61](https://github.com/f3ath/json-api-dart/issues/61)) +- Support for custom non-standard links ([\#61](https://github.com/f3ath/json-api-dart/issues/61)) - Client supports `jsonapi` key in outgoing requests. - `Document.contentType` constant. - `IdentifierObject.fromIdentifier` factory method ### Changed -Most of the changes are **BC-BREAKING**. - `URLBuilder` was renamed to `UrlFactory`. - `DocumentBuilder` was split into `ServerDocumentFactory` and `ClientDocumentFactory`. Some methods were renamed. - Static `decodeJson` methods were renamed to `fromJson`. @@ -97,7 +95,7 @@ Most of the changes are **BC-BREAKING**. - The signature of `Controller`. - `Server` was renamed to `JsonApiServer`. - `Pagination` was renamed to `PaginationStrategy`. - + ### Removed - (Server) `ResourceTarget`, `CollectionTarget`, `RelationshipTarget` classes. - `QueryParameters` interface. @@ -106,7 +104,7 @@ Most of the changes are **BC-BREAKING**. ## [2.1.0] - 2019-12-04 ### Added -- `onHttpCall` hook to enable raw http request/response logging ([#60](https://github.com/f3ath/json-api-dart/issues/60)). +- `onHttpCall` hook to enable raw http request/response logging ([\#60](https://github.com/f3ath/json-api-dart/issues/60)). ## [2.0.3] - 2019-09-29 ### Fixed @@ -114,73 +112,71 @@ Most of the changes are **BC-BREAKING**. ## [2.0.2] - 2019-08-01 ### Fixed -- Meta members have incorrect type ([#54](https://github.com/f3ath/json-api-dart/issues/54)). +- Meta members have incorrect type ([\#54](https://github.com/f3ath/json-api-dart/issues/54)). ## [2.0.1] - 2019-07-12 ### Fixed - Readme example was outdated. ## [2.0.0] - 2019-07-12 - ### Changed - This package now consolidates the Client, the Server and the Document in one single library. - It does not depend on `json_api_document` and `json_api_server` anymore, please remove these packages - from your `pubspec.yaml`. + It does not depend on `json_api_document` and `json_api_server` anymore, please remove these packages + from your `pubspec.yaml`. - The min Dart SDK version bumped to `2.3.0` - The Client requires an instance of HttpClient to be passed to the constructor explicitly. - Both the Document and the Server have been refactored with lots of **BREAKING CHANGES**. - See the examples and the functional tests for details. + See the examples and the functional tests for details. - Meta properties are not defensively copied, but set directly. Meta property behavior is unified across - the Document model. + the Document model. ### Removed - `JsonApiParser` is removed. Use the static `decodeJson` methods in the corresponding classes instead. - ## [1.0.1] - 2019-04-05 ### Fixed - Bumped the dependencies versions due to a bug in `json_api_document`. -## [1.0.0] - 2019-03-20 -### Changed -- JSON:API Server moved out - ## [0.6.0] - 2019-03-25 ### Changed - JSON:API Document moved out - Renamed `client.removeToOne(...)` to `client.deleteToOne(...)` ## [0.5.0] - 2019-03-21 +### Added +- Related collection pagination +- Async operations support + ### Changed - More BC-breaking changes in the Server ### Fixed - Location headers were incorrectly generated by Server -### Added -- Related collection pagination -- Async operations support +## [1.0.0] - 2019-03-20 +### Changed +- JSON:API Server moved out ## [0.4.0] - 2019-03-17 +### Added +- Compound documents support in Client (Server-side support is still very limited) + ### Changed - Parsing logic moved out - Some other BC-breaking changes in the Document - Huge changes in the Server -### Added -- Compound documents support in Client (Server-side support is still very limited) - ### Fixed - Server was not setting links for resources and relationships ## [0.3.0] - 2019-03-16 -### Changed -- Huge BC-breaking refactoring in the Document model which propagated everywhere - ### Added - Resource attributes update - Resource relationships update +### Changed +- Huge BC-breaking refactoring in the Document model which propagated everywhere + ## [0.2.0] - 2019-03-01 ### Added - Improved ResourceController error handling @@ -191,15 +187,15 @@ Most of the changes are **BC-BREAKING**. ### Added - Client: fetch resources, collections, related resources and relationships -[Unreleased]: https://github.com/f3ath/json-api-dart/compare/4.3.0..HEAD +[5.0.0]: https://github.com/f3ath/json-api-dart/compare/3.2.3...5.0.0 +[3.2.3]: https://github.com/f3ath/json-api-dart/compare/3.2.2...3.2.3 [4.3.0]: https://github.com/f3ath/json-api-dart/compare/4.2.2...4.3.0 [4.2.2]: https://github.com/f3ath/json-api-dart/compare/4.2.1...4.2.2 [4.2.1]: https://github.com/f3ath/json-api-dart/compare/4.2.0...4.2.1 [4.2.0]: https://github.com/f3ath/json-api-dart/compare/4.1.0...4.2.0 [4.1.0]: https://github.com/f3ath/json-api-dart/compare/4.0.0...4.1.0 [4.0.0]: https://github.com/f3ath/json-api-dart/compare/3.2.2...4.0.0 -[3.2.3]: https://github.com/f3ath/json-api-dart/compare/3.2.2..3.2.3 -[3.2.2]: https://github.com/f3ath/json-api-dart/compare/3.2.1..3.2.2 +[3.2.2]: https://github.com/f3ath/json-api-dart/compare/3.2.1...3.2.2 [3.2.1]: https://github.com/f3ath/json-api-dart/compare/3.2.0...3.2.1 [3.2.0]: https://github.com/f3ath/json-api-dart/compare/3.1.0...3.2.0 [3.1.0]: https://github.com/f3ath/json-api-dart/compare/3.0.0...3.1.0 @@ -210,10 +206,10 @@ Most of the changes are **BC-BREAKING**. [2.0.1]: https://github.com/f3ath/json-api-dart/compare/2.0.0...2.0.1 [2.0.0]: https://github.com/f3ath/json-api-dart/compare/1.0.1...2.0.0 [1.0.1]: https://github.com/f3ath/json-api-dart/compare/1.0.0...1.0.1 -[1.0.0]: https://github.com/f3ath/json-api-dart/compare/0.6.0...1.0.0 [0.6.0]: https://github.com/f3ath/json-api-dart/compare/0.5.0...0.6.0 [0.5.0]: https://github.com/f3ath/json-api-dart/compare/0.4.0...0.5.0 +[1.0.0]: https://github.com/f3ath/json-api-dart/compare/0.6.0...1.0.0 [0.4.0]: https://github.com/f3ath/json-api-dart/compare/0.3.0...0.4.0 [0.3.0]: https://github.com/f3ath/json-api-dart/compare/0.2.0...0.3.0 [0.2.0]: https://github.com/f3ath/json-api-dart/compare/0.1.0...0.2.0 -[0.1.0]: https://github.com/f3ath/json-api-dart/releases/tag/0.1.0 +[0.1.0]: https://github.com/f3ath/json-api-dart/releases/tag/0.1.0 \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index d147b5e..3125a85 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: json_api -version: 5.0.0-rc.2 +version: 5.0.0 homepage: https://github.com/f3ath/json-api-dart description: A framework-agnostic implementations of JSON:API Client and Server. Supports JSON:API v1.0 (https://jsonapi.org) environment: @@ -16,3 +16,8 @@ dev_dependencies: uuid: ^3.0.0 coverage: ^1.0.2 check_coverage: ^0.0.2 + +cider: + link_template: + diff: https://github.com/f3ath/json-api-dart/compare/%from%...%to% + tag: https://github.com/f3ath/json-api-dart/releases/tag/%tag% \ No newline at end of file From 83a5469656c5b81ec54aa71b644275badd1b9788 Mon Sep 17 00:00:00 2001 From: f3ath Date: Wed, 21 Apr 2021 22:23:10 -0700 Subject: [PATCH 3/3] Release --- lib/src/client/response.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/client/response.dart b/lib/src/client/response.dart index 567c0e7..8c51622 100644 --- a/lib/src/client/response.dart +++ b/lib/src/client/response.dart @@ -9,5 +9,4 @@ class Response { /// Decoded JSON document final Map? document; - }