Skip to content

Commit f46e34c

Browse files
committed
alpha.2
1 parent ae8fb36 commit f46e34c

File tree

6 files changed

+121
-31
lines changed

6 files changed

+121
-31
lines changed

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+
## [Unreleased]
8+
### Changed
9+
- URL Design matching now respects the base URL
10+
711
## [8.0.0] - 2024-07-01
812
### Added
913
- CORS middleware
@@ -257,6 +261,7 @@ the Document model.
257261
### Added
258262
- Client: fetch resources, collections, related resources and relationships
259263

264+
[Unreleased]: https://github.com/f3ath/json-api-dart/compare/8.0.0...HEAD
260265
[8.0.0]: https://github.com/f3ath/json-api-dart/compare/7.0.1...8.0.0
261266
[7.0.1]: https://github.com/f3ath/json-api-dart/compare/7.0.0...7.0.1
262267
[7.0.0]: https://github.com/f3ath/json-api-dart/compare/6.0.1...7.0.0

example/server.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Future<void> main() async {
2020

2121
final handler = errorConverter()
2222
.add(loggerMiddleware)
23-
.call(router(controller, StandardUriDesign.matchTarget));
23+
.call(router(controller, StandardUriDesign.pathOnly.matchTarget));
2424

2525
final server = JsonApiServer(handler, host: host, port: port);
2626

lib/src/routing/standard_uri_design.dart

+14-8
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,20 @@ 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) => 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-
};
14+
/// Matches a [uri] to a [Target] object.
15+
Target? matchTarget(Uri uri) => !uri.path.startsWith(base.path) ||
16+
(base.scheme.isNotEmpty && uri.scheme != base.scheme) ||
17+
(base.host.isNotEmpty && uri.host != base.host) ||
18+
(base.port != 0 && uri.port != base.port)
19+
? null
20+
: switch (uri.pathSegments.sublist(base.pathSegments.length)) {
21+
[var type] => Target(type),
22+
[var type, var id] => ResourceTarget(type, id),
23+
[var type, var id, var rel] => RelatedTarget(type, id, rel),
24+
[var type, var id, 'relationships', var rel] =>
25+
RelationshipTarget(type, id, rel),
26+
_ => null
27+
};
2228

2329
final Uri base;
2430

pubspec.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
name: json_api
2-
version: 9.0.0-alpha.1
2+
version: 9.0.0-alpha.2
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.4.0 <4.0.0'
6+
sdk: '>=3.5.0 <4.0.0'
77

88
dependencies:
99
http_parser: ^4.0.0

test/test_handler.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Handler testHandler(
1212
Function(Response response)? onResponse}) =>
1313
corsMiddleware.add(requestValidator).add(errorConverter()).call(router(
1414
RepositoryController(InMemoryRepo(types), _nextId),
15-
StandardUriDesign.matchTarget));
15+
StandardUriDesign.pathOnly.matchTarget));
1616

1717
String _nextId() => (_counter++).toString();
1818
int _counter = 0;

test/unit/routing/url_test.dart

+98-19
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,112 @@ import 'package:test/test.dart';
33

44
void main() {
55
test('uri generation', () {
6-
final url = StandardUriDesign.pathOnly;
7-
expect(url.collection('books').toString(), '/books');
8-
expect(url.resource('books', '42').toString(), '/books/42');
9-
expect(url.related('books', '42', 'author').toString(), '/books/42/author');
10-
expect(url.relationship('books', '42', 'author').toString(),
6+
final d = StandardUriDesign.pathOnly;
7+
expect(d.collection('books').toString(), '/books');
8+
expect(d.resource('books', '42').toString(), '/books/42');
9+
expect(d.related('books', '42', 'author').toString(), '/books/42/author');
10+
expect(d.relationship('books', '42', 'author').toString(),
1111
'/books/42/relationships/author');
1212
});
1313

1414
test('Authority is retained if exists in base', () {
15-
final url = StandardUriDesign(Uri.parse('https://example.com'));
16-
expect(url.collection('books').toString(), 'https://example.com/books');
17-
expect(
18-
url.resource('books', '42').toString(), 'https://example.com/books/42');
19-
expect(url.related('books', '42', 'author').toString(),
20-
'https://example.com/books/42/author');
21-
expect(url.relationship('books', '42', 'author').toString(),
22-
'https://example.com/books/42/relationships/author');
15+
final d = StandardUriDesign(Uri.parse('https://example.com:8080'));
16+
expect(d.collection('books').toString(), 'https://example.com:8080/books');
17+
expect(d.resource('books', '42').toString(),
18+
'https://example.com:8080/books/42');
19+
expect(d.related('books', '42', 'author').toString(),
20+
'https://example.com:8080/books/42/author');
21+
expect(d.relationship('books', '42', 'author').toString(),
22+
'https://example.com:8080/books/42/relationships/author');
2323
});
2424

25-
test('Authority and path is retained if exists in base (directory path)', () {
26-
final url = StandardUriDesign(Uri.parse('https://example.com/foo/'));
27-
expect(url.collection('books').toString(), 'https://example.com/foo/books');
28-
expect(url.resource('books', '42').toString(),
25+
test('Host and path is retained if exists in base (directory path)', () {
26+
final d = StandardUriDesign(Uri.parse('https://example.com/foo/'));
27+
expect(d.collection('books').toString(), 'https://example.com/foo/books');
28+
expect(d.resource('books', '42').toString(),
2929
'https://example.com/foo/books/42');
30-
expect(url.related('books', '42', 'author').toString(),
30+
expect(d.related('books', '42', 'author').toString(),
3131
'https://example.com/foo/books/42/author');
32-
expect(url.relationship('books', '42', 'author').toString(),
32+
expect(d.relationship('books', '42', 'author').toString(),
3333
'https://example.com/foo/books/42/relationships/author');
3434
});
35+
group('Target matching', () {
36+
test('Path only', () {
37+
final d = StandardUriDesign.pathOnly;
38+
expect(d.matchTarget(Uri.parse('/books')), isA<Target>());
39+
expect(d.matchTarget(Uri.parse('/books/42')), isA<ResourceTarget>());
40+
expect(
41+
d.matchTarget(Uri.parse('/books/42/authors')), isA<RelatedTarget>());
42+
expect(d.matchTarget(Uri.parse('/books/42/relationships/authors')),
43+
isA<RelationshipTarget>());
44+
expect(d.matchTarget(Uri.parse('/a/b/c/d')), isNull);
45+
});
46+
test('Path only, full url', () {
47+
final d = StandardUriDesign.pathOnly;
48+
expect(
49+
d.matchTarget(Uri.parse('https://example.com/books')), isA<Target>());
50+
expect(d.matchTarget(Uri.parse('https://example.com/books/42')),
51+
isA<ResourceTarget>());
52+
expect(d.matchTarget(Uri.parse('https://example.com/books/42/authors')),
53+
isA<RelatedTarget>());
54+
expect(
55+
d.matchTarget(
56+
Uri.parse('https://example.com/books/42/relationships/authors')),
57+
isA<RelationshipTarget>());
58+
expect(d.matchTarget(Uri.parse('https://example.com/a/b/c/d')), isNull);
59+
});
60+
test('Authority', () {
61+
final d = StandardUriDesign(Uri.parse('https://example.com:8080'));
62+
expect(d.matchTarget(Uri.parse('https://example.com:8080/books')),
63+
isA<Target>());
64+
expect(d.matchTarget(Uri.parse('https://example.com:8080/books/42')),
65+
isA<ResourceTarget>());
66+
expect(
67+
d.matchTarget(Uri.parse('https://example.com:8080/books/42/authors')),
68+
isA<RelatedTarget>());
69+
expect(
70+
d.matchTarget(Uri.parse(
71+
'https://example.com:8080/books/42/relationships/authors')),
72+
isA<RelationshipTarget>());
73+
74+
expect(
75+
d.matchTarget(Uri.parse('https://example.com:8080/a/b/c/d')), isNull);
76+
expect(d.matchTarget(Uri.parse('http://example.com:8080/books')), isNull);
77+
expect(d.matchTarget(Uri.parse('https://foo.net:8080/books')), isNull);
78+
});
79+
80+
test('Authority and path', () {
81+
final d = StandardUriDesign(Uri.parse('https://example.com:8080/api'));
82+
expect(d.matchTarget(Uri.parse('https://example.com:8080/api/books')),
83+
isA<Target>().having((it) => it.type, 'type', equals('books')));
84+
expect(
85+
d.matchTarget(Uri.parse('https://example.com:8080/api/books/42')),
86+
isA<ResourceTarget>()
87+
.having((it) => it.type, 'type', equals('books'))
88+
.having((it) => it.id, 'id', equals('42')));
89+
expect(
90+
d.matchTarget(
91+
Uri.parse('https://example.com:8080/api/books/42/authors')),
92+
isA<RelatedTarget>()
93+
.having((it) => it.type, 'type', equals('books'))
94+
.having((it) => it.id, 'id', equals('42'))
95+
.having(
96+
(it) => it.relationship, 'relationship', equals('authors')));
97+
expect(
98+
d.matchTarget(Uri.parse(
99+
'https://example.com:8080/api/books/42/relationships/authors')),
100+
isA<RelationshipTarget>()
101+
.having((it) => it.type, 'type', equals('books'))
102+
.having((it) => it.id, 'id', equals('42'))
103+
.having(
104+
(it) => it.relationship, 'relationship', equals('authors')));
105+
106+
expect(
107+
d.matchTarget(Uri.parse('https://example.com:8080/a/b/c/d')), isNull);
108+
expect(d.matchTarget(Uri.parse('http://example.com:8080/books')), isNull);
109+
expect(d.matchTarget(Uri.parse('https://foo.net:8080/books')), isNull);
110+
expect(d.matchTarget(Uri.parse('https://example.com:8080/foo/books')),
111+
isNull);
112+
});
113+
});
35114
}

0 commit comments

Comments
 (0)