Skip to content

Commit faae2e5

Browse files
authored
v7 (#138)
1 parent e5d1aed commit faae2e5

24 files changed

+238
-175
lines changed

.github/workflows/dart.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ jobs:
2525
- name: Tests
2626
run: dart test --coverage=.coverage -j1
2727
- name: Coverage
28-
run: dart run coverage:format_coverage -l -c -i .coverage --report-on=lib | dart run check_coverage:check_coverage 98
28+
run: dart run coverage:format_coverage -l -c -i .coverage --report-on=lib | dart run check_coverage:check_coverage

.github/workflows/publish.yml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: Publish to pub.dev
2+
3+
on:
4+
push:
5+
tags:
6+
- '[0-9]+.[0-9]+.[0-9]+*'
7+
8+
jobs:
9+
publish:
10+
permissions:
11+
id-token: write # Required for authentication using OIDC
12+
uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1
13+
# with:
14+
# working-directory: path/to/package/within/repository

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ 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+
## [7.0.0] - 2023-11-12
8+
### Changed
9+
- Migrated to `http_interop` v1.
10+
711
## [6.0.1] - 2023-09-11
812
### Fixed
913
- `NewRelationship` was not exported
@@ -242,6 +246,7 @@ the Document model.
242246
### Added
243247
- Client: fetch resources, collections, related resources and relationships
244248

249+
[7.0.0]: https://github.com/f3ath/json-api-dart/compare/6.0.1...7.0.0
245250
[6.0.1]: https://github.com/f3ath/json-api-dart/compare/6.0.0...6.0.1
246251
[6.0.0]: https://github.com/f3ath/json-api-dart/compare/5.4.0...6.0.0
247252
[5.4.0]: https://github.com/f3ath/json-api-dart/compare/5.3.0...5.4.0

example/server.dart

+5-5
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ Future<void> main() async {
2222
ControllerRouter(controller, StandardUriDesign.matchTarget);
2323
handler = TryCatchHandler(handler,
2424
onError: ErrorConverter(onError: (e, stack) async {
25-
stderr.writeln(e);
26-
return Response(500,
27-
document: OutboundErrorDocument(
28-
[ErrorObject(title: 'Internal Server Error')]));
29-
}));
25+
stderr.writeln(e);
26+
return Response(500,
27+
document: OutboundErrorDocument(
28+
[ErrorObject(title: 'Internal Server Error')]));
29+
}).call);
3030
handler = LoggingHandler(handler,
3131
onRequest: (r) => print('${r.method} ${r.uri}'),
3232
onResponse: (r) => print('${r.statusCode}'));

example/server/cors_handler.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ class CorsHandler implements Handler {
1313
'Access-Control-Expose-Headers': ['Location'],
1414
};
1515

16-
if (request.method.equals('OPTIONS')) {
16+
if (request.method == 'options') {
1717
const methods = ['POST', 'GET', 'DELETE', 'PATCH', 'OPTIONS'];
1818
return Response(
1919
204,
20-
Body.empty(),
21-
Headers({
20+
Body(),
21+
Headers.from({
2222
...headers,
2323
'Access-Control-Allow-Methods':
2424
request.headers['Access-Control-Request-Method'] ?? methods,

lib/http.dart

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import 'dart:convert';
2-
31
import 'package:http_interop/http_interop.dart';
42

53
class StatusCode {
@@ -29,7 +27,7 @@ class StatusCode {
2927
}
3028

3129
class Json extends Body {
32-
Json(Map<String, Object?> json) : super(jsonEncode(json), utf8);
30+
Json(Map<String, Object?> super.object) : super.json();
3331
}
3432

3533
class LoggingHandler implements Handler {

lib/src/client/client.dart

+4-4
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ class Client {
2323
/// Sends the [request] to the given [uri].
2424
Future<Response> send(Uri uri, Request request) async {
2525
final json = await _encode(request.document);
26-
final body = http.Body(json, utf8);
27-
final headers = http.Headers({
26+
final body = http.Body.text(json, utf8);
27+
final headers = http.Headers.from({
2828
'Accept': [mediaType],
2929
if (json.isNotEmpty) 'Content-Type': [mediaType],
3030
...request.headers
3131
});
3232
final url = request.query.isEmpty
3333
? uri
3434
: uri.replace(queryParameters: request.query.toQuery());
35-
final response = await _handler
36-
.handle(http.Request(http.Method(request.method), url, body, headers));
35+
final response =
36+
await _handler.handle(http.Request(request.method, url, body, headers));
3737

3838
final document = await _decode(response);
3939
return Response(response, document);

lib/src/client/request.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@ class Request {
3131
final query = Query();
3232

3333
/// HTTP headers.
34-
final headers = Headers({});
34+
final headers = Headers();
3535
}

lib/src/document/inbound_document.dart

+18-26
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,11 @@ class _Parser {
147147

148148
/// Decodes Link from [json]. Returns the decoded object.
149149
/// If the [json] has incorrect format, throws [FormatException].
150-
Link _link(Object json) {
151-
if (json is String) return Link(Uri.parse(json));
152-
if (json is Map) {
153-
return Link(Uri.parse(json['href']))..meta.addAll(meta(json));
154-
}
155-
throw FormatException('Invalid JSON');
156-
}
150+
Link _link(Object json) => switch (json) {
151+
String() => Link(Uri.parse(json)),
152+
Map() => Link(Uri.parse(json['href']))..meta.addAll(meta(json)),
153+
_ => throw FormatException('Invalid JSON')
154+
};
157155

158156
Map<String, Object?> _getAttributes(Map json) =>
159157
json.get<Map<String, Object?>>('attributes', orGet: () => {});
@@ -166,25 +164,19 @@ class _Parser {
166164
.get<Map>('relationships', orGet: () => {})
167165
.map((key, value) => MapEntry(key, newRelationship(value)));
168166

169-
Relationship _rel(data) {
170-
if (data == null) return ToOne.empty();
171-
if (data is Map) return ToOne(identifier(data));
172-
if (data is List) return ToMany(data.whereType<Map>().map(identifier));
173-
throw FormatException('Invalid relationship object');
174-
}
175-
176-
NewRelationship _newRel(data) {
177-
if (data == null) {
178-
return NewToOne.empty();
179-
}
180-
if (data is Map) {
181-
return NewToOne(newIdentifier(data));
182-
}
183-
if (data is List) {
184-
return NewToMany(data.whereType<Map>().map(newIdentifier));
185-
}
186-
throw FormatException('Invalid relationship object');
187-
}
167+
Relationship _rel(data) => switch (data) {
168+
null => ToOne.empty(),
169+
Map() => ToOne(identifier(data)),
170+
List() => ToMany(data.whereType<Map>().map(identifier)),
171+
_ => throw FormatException('Invalid relationship object')
172+
};
173+
174+
NewRelationship _newRel(data) => switch (data) {
175+
null => NewToOne.empty(),
176+
Map() => NewToOne(newIdentifier(data)),
177+
List() => NewToMany(data.whereType<Map>().map(newIdentifier)),
178+
_ => throw FormatException('Invalid relationship object')
179+
};
188180
}
189181

190182
extension _TypedGeter on Map {

lib/src/document/new_relationship.dart

-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ class NewRelationship with IterableMixin<NewIdentifier> {
1212
if (meta.isNotEmpty) 'meta': meta,
1313
};
1414

15-
// coverage:ignore-start
1615
@override
1716
Iterator<NewIdentifier> get iterator => <NewIdentifier>[].iterator;
18-
// coverage:ignore-end
1917
}

lib/src/document/new_resource.dart

+8-13
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ class NewResource {
6161
if (r is NewToMany) {
6262
return ToMany(r.map((identifier) => _toIdentifier(identifier, id)));
6363
}
64-
// coverage:ignore-line
6564
throw StateError('Unexpected relationship type: ${r.runtimeType}');
6665
}
6766

@@ -70,16 +69,12 @@ class NewResource {
7069
return _toIdentifier(identifier, id);
7170
}
7271

73-
Identifier _toIdentifier(NewIdentifier identifier, String id) {
74-
switch (identifier) {
75-
case Identifier():
76-
return identifier;
77-
case LocalIdentifier():
78-
if (identifier.type == type && identifier.lid == lid) {
79-
return identifier.toIdentifier(id);
80-
}
81-
throw StateError(
82-
'Unmatched local id: "${identifier.lid}". Expected "$lid".');
83-
}
84-
}
72+
Identifier _toIdentifier(NewIdentifier identifier, String id) =>
73+
switch (identifier) {
74+
Identifier() => identifier,
75+
LocalIdentifier() => (identifier.type == type && identifier.lid == lid)
76+
? identifier.toIdentifier(id)
77+
: throw StateError(
78+
'Unmatched local id: "${identifier.lid}". Expected "$lid".')
79+
};
8580
}

lib/src/routing/standard_uri_design.dart

+8-16
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,14 @@ class StandardUriDesign implements UriDesign {
1111
/// `/books`, `/books/42`, `/books/42/authors`
1212
static final pathOnly = StandardUriDesign(Uri(path: '/'));
1313

14-
static Target? matchTarget(Uri uri) {
15-
final s = uri.pathSegments;
16-
if (s.length == 1) {
17-
return Target(s.first);
18-
}
19-
if (s.length == 2) {
20-
return ResourceTarget(s.first, s.last);
21-
}
22-
if (s.length == 3) {
23-
return RelatedTarget(s.first, s[1], s.last);
24-
}
25-
if (s.length == 4 && s[2] == 'relationships') {
26-
return RelationshipTarget(s.first, s[1], s.last);
27-
}
28-
return null;
29-
}
14+
static Target? matchTarget(Uri uri) => switch ((uri.pathSegments)) {
15+
[var type] => Target(type),
16+
[var type, var id] => ResourceTarget(type, id),
17+
[var type, var id, var rel] => RelatedTarget(type, id, rel),
18+
[var type, var id, 'relationships', var rel] =>
19+
RelationshipTarget(type, id, rel),
20+
_ => null
21+
};
3022

3123
final Uri base;
3224

lib/src/server/controller_router.dart

+25-43
Original file line numberDiff line numberDiff line change
@@ -18,49 +18,31 @@ class ControllerRouter implements Handler {
1818
Future<Response> handle(Request request) async {
1919
_validate(request);
2020
final target = _matchTarget(request.uri);
21-
if (target is RelationshipTarget) {
22-
if (request.method.equals('GET')) {
23-
return await _controller.fetchRelationship(request, target);
24-
}
25-
if (request.method.equals('POST')) {
26-
return await _controller.addMany(request, target);
27-
}
28-
if (request.method.equals('PATCH')) {
29-
return await _controller.replaceRelationship(request, target);
30-
}
31-
if (request.method.equals('DELETE')) {
32-
return await _controller.deleteMany(request, target);
33-
}
34-
throw MethodNotAllowed(request.method.value);
35-
}
36-
if (target is RelatedTarget) {
37-
if (request.method.equals('GET')) {
38-
return await _controller.fetchRelated(request, target);
39-
}
40-
throw MethodNotAllowed(request.method.value);
41-
}
42-
if (target is ResourceTarget) {
43-
if (request.method.equals('GET')) {
44-
return await _controller.fetchResource(request, target);
45-
}
46-
if (request.method.equals('PATCH')) {
47-
return await _controller.updateResource(request, target);
48-
}
49-
if (request.method.equals('DELETE')) {
50-
return await _controller.deleteResource(request, target);
51-
}
52-
throw MethodNotAllowed(request.method.value);
53-
}
54-
if (target is Target) {
55-
if (request.method.equals('GET')) {
56-
return await _controller.fetchCollection(request, target);
57-
}
58-
if (request.method.equals('POST')) {
59-
return await _controller.createResource(request, target);
60-
}
61-
throw MethodNotAllowed(request.method.value);
62-
}
63-
throw UnmatchedTarget(request.uri);
21+
return await switch (target) {
22+
RelationshipTarget() => switch (request.method) {
23+
'get' => _controller.fetchRelationship(request, target),
24+
'post' => _controller.addMany(request, target),
25+
'patch' => _controller.replaceRelationship(request, target),
26+
'delete' => _controller.deleteMany(request, target),
27+
_ => throw MethodNotAllowed(request.method)
28+
},
29+
RelatedTarget() => switch (request.method) {
30+
'get' => _controller.fetchRelated(request, target),
31+
_ => throw MethodNotAllowed(request.method)
32+
},
33+
ResourceTarget() => switch (request.method) {
34+
'get' => _controller.fetchResource(request, target),
35+
'patch' => _controller.updateResource(request, target),
36+
'delete' => _controller.deleteResource(request, target),
37+
_ => throw MethodNotAllowed(request.method)
38+
},
39+
Target() => switch (request.method) {
40+
'get' => _controller.fetchCollection(request, target),
41+
'post' => _controller.createResource(request, target),
42+
_ => throw MethodNotAllowed(request.method)
43+
},
44+
_ => throw UnmatchedTarget(request.uri)
45+
};
6446
}
6547

6648
void _validate(Request request) {

lib/src/server/response.dart

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import 'dart:convert';
2-
31
import 'package:http_interop/http_interop.dart' as http;
42
import 'package:json_api/document.dart';
53
import 'package:json_api/http.dart';
@@ -10,10 +8,8 @@ class Response<D extends OutboundDocument> extends http.Response {
108
Response(int statusCode, {D? document})
119
: super(
1210
statusCode,
13-
document != null
14-
? http.Body(jsonEncode(document), utf8)
15-
: http.Body.empty(),
16-
http.Headers({})) {
11+
document != null ? http.Body.json(document) : http.Body(),
12+
http.Headers()) {
1713
if (document != null) {
1814
headers['Content-Type'] = [mediaType];
1915
}

pubspec.yaml

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
name: json_api
2-
version: 6.0.1
2+
version: 7.0.0
33
homepage: https://github.com/f3ath/json-api-dart
44
description: A framework-agnostic implementations of JSON:API Client and Server. Supports JSON:API v1.0 (https://jsonapi.org)
55
environment:
6-
sdk: '>=3.0.0 <4.0.0'
6+
sdk: '>=3.1.0 <4.0.0'
77

88
dependencies:
99
http_parser: ^4.0.0
10-
http_interop: ^0.8.0
10+
http_interop: ^1.0.0
1111

1212
dev_dependencies:
13-
lints: ^2.1.1
13+
lints: ^3.0.0
1414
test: ^1.21.1
1515
stream_channel: ^2.1.0
16-
uuid: ^3.0.0
16+
uuid: ^4.2.0
1717
coverage: ^1.3.0
1818
check_coverage: ^0.0.4
1919
http: ^1.1.0
20-
http_interop_http: ^0.6.0
21-
http_interop_io: ^0.6.0
20+
http_interop_http: ^0.7.0
21+
http_interop_io: ^0.7.0
2222

2323

2424
cider:

0 commit comments

Comments
 (0)