Skip to content

Commit 2cb8402

Browse files
authored
Release v6 (#135)
1 parent 8695d7d commit 2cb8402

File tree

94 files changed

+1751
-1374
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+1751
-1374
lines changed

.github/workflows/dart.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@ jobs:
1818
- uses: actions/checkout@v2
1919
- name: Install dependencies
2020
run: dart pub get
21-
- name: Print Dart version
22-
run: dart --version
2321
- name: Format
2422
run: dart format --output none --set-exit-if-changed example lib test
2523
- name: Analyzer
2624
run: dart analyze --fatal-infos --fatal-warnings
2725
- name: Tests
2826
run: dart test --coverage=.coverage -j1
2927
- name: Coverage
30-
run: dart run coverage:format_coverage -l -c -i .coverage --report-on=lib | dart run check_coverage:check_coverage
28+
run: dart run coverage:format_coverage -l -c -i .coverage --report-on=lib | dart run check_coverage:check_coverage 98

CHANGELOG.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [6.0.0] - 2023-09-07
8+
### Added
9+
- Partial support for JSON:API v1.1
10+
11+
### Changed
12+
- A bunch of BC-breaking changes. Please refer to the tests.
13+
- Min SDK version is 3.0.0
14+
- Migrated to `http_interop`. You'll have to install `http_interop_http` or another implementation to get the HTTP client.
15+
16+
### Removed
17+
- Query filter.
18+
719
## [5.4.0] - 2023-04-30
820
### Changed
921
- Switch to http\_interop packages.
@@ -13,9 +25,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1325
### Added
1426
- Client MessageConverter class to control HTTP request/response conversion.
1527

16-
### Fixed
17-
- Content-Type being set for GET/OPTIONS requests ([issue](https://github.com/f3ath/json-api-dart/issues/129)).
18-
1928
## [5.2.0] - 2022-06-01
2029
### Added
2130
- Support for included resources in create and update methods. Author: @kszczek
@@ -229,6 +238,7 @@ the Document model.
229238
### Added
230239
- Client: fetch resources, collections, related resources and relationships
231240

241+
[6.0.0]: https://github.com/f3ath/json-api-dart/compare/5.4.0...6.0.0
232242
[5.4.0]: https://github.com/f3ath/json-api-dart/compare/5.3.0...5.4.0
233243
[5.3.0]: https://github.com/f3ath/json-api-dart/compare/5.2.0...5.3.0
234244
[5.2.0]: https://github.com/f3ath/json-api-dart/compare/5.1.0...5.2.0
@@ -263,4 +273,4 @@ the Document model.
263273
[0.4.0]: https://github.com/f3ath/json-api-dart/compare/0.3.0...0.4.0
264274
[0.3.0]: https://github.com/f3ath/json-api-dart/compare/0.2.0...0.3.0
265275
[0.2.0]: https://github.com/f3ath/json-api-dart/compare/0.1.0...0.2.0
266-
[0.1.0]: https://github.com/f3ath/json-api-dart/releases/tag/0.1.0
276+
[0.1.0]: https://github.com/f3ath/json-api-dart/releases/tag/0.1.0

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
TL;DR:
44
```dart
5+
import 'package:http_interop_http/http_interop_http.dart';
56
import 'package:json_api/client.dart';
67
import 'package:json_api/routing.dart';
78
@@ -14,7 +15,7 @@ void main() async {
1415
1516
/// The [RoutingClient] is most likely the right choice.
1617
/// It has methods covering many standard use cases.
17-
final client = RoutingClient(uriDesign);
18+
final client = RoutingClient(uriDesign, Client(OneOffHandler()));
1819
1920
try {
2021
/// Fetch the collection.
@@ -31,14 +32,13 @@ void main() async {
3132
});
3233
} on RequestFailure catch (e) {
3334
/// Catch error response
34-
e.errors.forEach((error) => print('${error.title}'));
35+
for (var error in e.errors) {
36+
print(error.title);
37+
}
3538
}
3639
}
3740
```
3841
This is a work-in-progress. You can help it by submitting a PR with a feature or documentation improvements.
3942

4043

41-
42-
43-
4444
[JSON:API]: https://jsonapi.org

example/client.dart

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'package:http/http.dart' as http;
2+
import 'package:http_interop_http/http_interop_http.dart';
13
import 'package:json_api/client.dart';
24
import 'package:json_api/routing.dart';
35

@@ -8,27 +10,50 @@ void main() async {
810
/// Use the standard recommended URL structure or implement your own
911
final uriDesign = StandardUriDesign(Uri.parse(baseUri));
1012

13+
/// This is the Dart's standard HTTP client.
14+
/// Do not forget to close it in the end.
15+
final httpClient = http.Client();
16+
17+
/// This is the adapter which decouples this JSON:API implementation
18+
/// from the HTTP client.
19+
/// Learn more: https://pub.dev/packages/http_interop
20+
final httpHandler = ClientWrapper(httpClient);
21+
22+
/// This is the basic JSON:API client. It is flexible but not very convenient
23+
/// to use, because you would need to remember a lot of JSON:API protocol details.
24+
/// We will use another wrapper on top of it.
25+
final jsonApiClient = Client(httpHandler);
26+
1127
/// The [RoutingClient] is most likely the right choice.
12-
/// It has methods covering many standard use cases.
13-
final client = RoutingClient(uriDesign);
28+
/// It is called routing because it routes the calls to the correct
29+
/// URLs depending on the use case. Take a look at its methods, they cover
30+
/// all the standard scenarios specified by the JSON:API standard.
31+
final client = RoutingClient(uriDesign, jsonApiClient);
1432

1533
try {
1634
/// Fetch the collection.
1735
/// See other methods to query and manipulate resources.
1836
final response = await client.fetchCollection('colors');
1937

20-
final resources = response.collection;
21-
resources.map((resource) => resource.attributes).forEach((attr) {
22-
final name = attr['name'];
23-
final red = attr['red'];
24-
final green = attr['green'];
25-
final blue = attr['blue'];
38+
/// The fetched collection allows us to iterate over the resources
39+
/// and to look into their attributes
40+
for (final resource in response.collection) {
41+
final {
42+
'name': name,
43+
'red': red,
44+
'green': green,
45+
'blue': blue,
46+
} = resource.attributes;
47+
print('${resource.type}:${resource.id}');
2648
print('$name - $red:$green:$blue');
27-
});
49+
}
2850
} on RequestFailure catch (e) {
2951
/// Catch error response
30-
for (var error in e.errors) {
52+
for (final error in e.errors) {
3153
print(error.title);
3254
}
3355
}
56+
57+
/// Free up the resources before exit.
58+
httpClient.close();
3459
}

example/server.dart

Lines changed: 15 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'dart:io';
22

3+
import 'package:http_interop/http_interop.dart' as interop;
34
import 'package:json_api/document.dart';
45
import 'package:json_api/http.dart';
56
import 'package:json_api/routing.dart';
@@ -10,19 +11,24 @@ import 'server/in_memory_repo.dart';
1011
import 'server/json_api_server.dart';
1112
import 'server/repository.dart';
1213
import 'server/repository_controller.dart';
13-
import 'server/try_catch_handler.dart';
1414

1515
Future<void> main() async {
1616
final host = 'localhost';
1717
final port = 8080;
18-
final resources = ['colors'];
19-
final repo = InMemoryRepo(resources);
20-
await addColors(repo);
18+
final repo = InMemoryRepo(['colors']);
19+
await initRepo(repo);
2120
final controller = RepositoryController(repo, Uuid().v4);
22-
HttpHandler handler = Router(controller, StandardUriDesign.matchTarget);
23-
handler = TryCatchHandler(handler, onError: convertError);
21+
interop.Handler handler =
22+
ControllerRouter(controller, StandardUriDesign.matchTarget);
23+
handler = TryCatchHandler(handler,
24+
onError: ErrorConverter(onError: (e, stack) async {
25+
stderr.writeln(e);
26+
return Response(500,
27+
document: OutboundErrorDocument(
28+
[ErrorObject(title: 'Internal Server Error')]));
29+
}));
2430
handler = LoggingHandler(handler,
25-
onRequest: (r) => print('${r.method.toUpperCase()} ${r.uri}'),
31+
onRequest: (r) => print('${r.method} ${r.uri}'),
2632
onResponse: (r) => print('${r.statusCode}'));
2733
final server = JsonApiServer(handler, host: host, port: port);
2834

@@ -33,14 +39,10 @@ Future<void> main() async {
3339

3440
await server.start();
3541

36-
print('The server is listening at $host:$port.'
37-
' Try opening the following URL(s) in your browser:');
38-
for (var resource in resources) {
39-
print('http://$host:$port/$resource');
40-
}
42+
print('The server is listening at $host:$port.');
4143
}
4244

43-
Future addColors(Repository repo) async {
45+
Future initRepo(Repository repo) async {
4446
final models = {
4547
{'name': 'Salmon', 'r': 250, 'g': 128, 'b': 114},
4648
{'name': 'Pink', 'r': 255, 'g': 192, 'b': 203},
@@ -55,29 +57,3 @@ Future addColors(Repository repo) async {
5557
await repo.persist('colors', model);
5658
}
5759
}
58-
59-
Future<HttpResponse> convertError(dynamic error) async {
60-
if (error is MethodNotAllowed) {
61-
return Response.methodNotAllowed();
62-
}
63-
if (error is UnmatchedTarget) {
64-
return Response.badRequest();
65-
}
66-
if (error is CollectionNotFound) {
67-
return Response.notFound(
68-
OutboundErrorDocument([ErrorObject(title: 'CollectionNotFound')]));
69-
}
70-
if (error is ResourceNotFound) {
71-
return Response.notFound(
72-
OutboundErrorDocument([ErrorObject(title: 'ResourceNotFound')]));
73-
}
74-
if (error is RelationshipNotFound) {
75-
return Response.notFound(
76-
OutboundErrorDocument([ErrorObject(title: 'RelationshipNotFound')]));
77-
}
78-
return Response(500,
79-
document: OutboundErrorDocument([
80-
ErrorObject(
81-
title: 'Error: ${error.runtimeType}', detail: error.toString())
82-
]));
83-
}

example/server/cors_handler.dart

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
1+
import 'package:http_interop/http_interop.dart';
12
import 'package:json_api/http.dart';
23

3-
class CorsHandler implements HttpHandler {
4+
class CorsHandler implements Handler {
45
CorsHandler(this._inner);
56

6-
final HttpHandler _inner;
7+
final Handler _inner;
78

89
@override
9-
Future<HttpResponse> handle(HttpRequest request) async {
10+
Future<Response> handle(Request request) async {
1011
final headers = {
11-
'Access-Control-Allow-Origin': request.headers['origin'] ?? '*',
12-
'Access-Control-Expose-Headers': 'Location',
12+
'Access-Control-Allow-Origin': [request.headers.last('origin') ?? '*'],
13+
'Access-Control-Expose-Headers': ['Location'],
1314
};
1415

15-
if (request.isOptions) {
16+
if (request.method.equals('OPTIONS')) {
1617
const methods = ['POST', 'GET', 'DELETE', 'PATCH', 'OPTIONS'];
17-
return HttpResponse(204)
18-
..headers.addAll({
19-
...headers,
20-
'Access-Control-Allow-Methods':
21-
// TODO: Make it work for all browsers. Why is toUpperCase() needed?
22-
request.headers['Access-Control-Request-Method']?.toUpperCase() ??
23-
methods.join(', '),
24-
'Access-Control-Allow-Headers':
25-
request.headers['Access-Control-Request-Headers'] ?? '*',
26-
});
18+
return Response(
19+
204,
20+
Body.empty(),
21+
Headers({
22+
...headers,
23+
'Access-Control-Allow-Methods':
24+
request.headers['Access-Control-Request-Method'] ?? methods,
25+
'Access-Control-Allow-Headers':
26+
request.headers['Access-Control-Request-Headers'] ?? ['*'],
27+
}));
2728
}
28-
return await _inner.handle(request)
29-
..headers.addAll(headers);
29+
return await _inner.handle(request..headers.addAll(headers));
3030
}
3131
}

example/server/demo_handler.dart

Lines changed: 0 additions & 53 deletions
This file was deleted.

0 commit comments

Comments
 (0)