Skip to content

Commit 13a14df

Browse files
committed
alpha.4
1 parent 9a7c6af commit 13a14df

File tree

4 files changed

+69
-3
lines changed

4 files changed

+69
-3
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
- URL Design matching now respects the base URL
1010
- Allo null to be returned by error interceptors
1111

12+
### Fixed
13+
- StandardUriDesign working incosistently depending on the trailing slash in the path
14+
1215
## [8.0.0] - 2024-07-01
1316
### Added
1417
- CORS middleware

lib/src/routing/standard_uri_design.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import 'package:json_api/routing.dart';
55
class StandardUriDesign implements UriDesign {
66
/// Creates an instance of [UriDesign] recommended by JSON:API standard.
77
/// The [base] URI will be used as a prefix for the generated URIs.
8-
const StandardUriDesign(this.base);
8+
StandardUriDesign(Uri base)
9+
: base = base.path.endsWith('/')
10+
? base
11+
: base.replace(path: '${base.path}/');
912

1013
/// A "path only" version of the recommended URL design, e.g.
1114
/// `/books`, `/books/42`, `/books/42/authors`
@@ -17,7 +20,8 @@ class StandardUriDesign implements UriDesign {
1720
(base.host.isNotEmpty && uri.host != base.host) ||
1821
(base.port != 0 && uri.port != base.port)
1922
? null
20-
: switch (uri.pathSegments.sublist(base.pathSegments.length)) {
23+
: switch (uri.pathSegments
24+
.sublist(base.pathSegments.where((it) => it.isNotEmpty).length)) {
2125
[var type] => Target(type),
2226
[var type, var id] => ResourceTarget(type, id),
2327
[var type, var id, var rel] => RelatedTarget(type, id, rel),

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: json_api
2-
version: 9.0.0-alpha.3
2+
version: 9.0.0-alpha.4
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:

test/unit/routing/url_test.dart

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,38 @@ import 'package:test/test.dart';
44
void main() {
55
test('uri generation', () {
66
final d = StandardUriDesign.pathOnly;
7+
expect(d.base.path, equals('/'));
78
expect(d.collection('books').toString(), '/books');
89
expect(d.resource('books', '42').toString(), '/books/42');
910
expect(d.related('books', '42', 'author').toString(), '/books/42/author');
1011
expect(d.relationship('books', '42', 'author').toString(),
1112
'/books/42/relationships/author');
1213
});
1314

15+
test('uri generation with base, trailing slash', () {
16+
final d = StandardUriDesign(Uri.parse('https://example.com/api/'));
17+
expect(d.base.path, equals('/api/'));
18+
expect(d.collection('books').toString(), 'https://example.com/api/books');
19+
expect(d.resource('books', '42').toString(),
20+
'https://example.com/api/books/42');
21+
expect(d.related('books', '42', 'author').toString(),
22+
'https://example.com/api/books/42/author');
23+
expect(d.relationship('books', '42', 'author').toString(),
24+
'https://example.com/api/books/42/relationships/author');
25+
});
26+
27+
test('uri generation with base, no trailing slash', () {
28+
final d = StandardUriDesign(Uri.parse('https://example.com/api'));
29+
expect(d.base.path, equals('/api/'));
30+
expect(d.collection('books').toString(), 'https://example.com/api/books');
31+
expect(d.resource('books', '42').toString(),
32+
'https://example.com/api/books/42');
33+
expect(d.related('books', '42', 'author').toString(),
34+
'https://example.com/api/books/42/author');
35+
expect(d.relationship('books', '42', 'author').toString(),
36+
'https://example.com/api/books/42/relationships/author');
37+
});
38+
1439
test('Authority is retained if exists in base', () {
1540
final d = StandardUriDesign(Uri.parse('https://example.com:8080'));
1641
expect(d.collection('books').toString(), 'https://example.com:8080/books');
@@ -110,5 +135,39 @@ void main() {
110135
expect(d.matchTarget(Uri.parse('https://example.com:8080/foo/books')),
111136
isNull);
112137
});
138+
139+
test('Authority and path, trailing slash', () {
140+
final d = StandardUriDesign(Uri.parse('https://example.com:8080/api/'));
141+
expect(d.matchTarget(Uri.parse('https://example.com:8080/api/books')),
142+
isA<Target>().having((it) => it.type, 'type', equals('books')));
143+
expect(
144+
d.matchTarget(Uri.parse('https://example.com:8080/api/books/42')),
145+
isA<ResourceTarget>()
146+
.having((it) => it.type, 'type', equals('books'))
147+
.having((it) => it.id, 'id', equals('42')));
148+
expect(
149+
d.matchTarget(
150+
Uri.parse('https://example.com:8080/api/books/42/authors')),
151+
isA<RelatedTarget>()
152+
.having((it) => it.type, 'type', equals('books'))
153+
.having((it) => it.id, 'id', equals('42'))
154+
.having(
155+
(it) => it.relationship, 'relationship', equals('authors')));
156+
expect(
157+
d.matchTarget(Uri.parse(
158+
'https://example.com:8080/api/books/42/relationships/authors')),
159+
isA<RelationshipTarget>()
160+
.having((it) => it.type, 'type', equals('books'))
161+
.having((it) => it.id, 'id', equals('42'))
162+
.having(
163+
(it) => it.relationship, 'relationship', equals('authors')));
164+
165+
expect(
166+
d.matchTarget(Uri.parse('https://example.com:8080/a/b/c/d')), isNull);
167+
expect(d.matchTarget(Uri.parse('http://example.com:8080/books')), isNull);
168+
expect(d.matchTarget(Uri.parse('https://foo.net:8080/books')), isNull);
169+
expect(d.matchTarget(Uri.parse('https://example.com:8080/foo/books')),
170+
isNull);
171+
});
113172
});
114173
}

0 commit comments

Comments
 (0)