Skip to content

Commit 61b6dfe

Browse files
authored
v3 (#59)
1 parent 84d4359 commit 61b6dfe

File tree

82 files changed

+1623
-1029
lines changed

Some content is hidden

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

82 files changed

+1623
-1029
lines changed

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
language: dart
22
dart:
33
- stable
4+
- dev
5+
- "2.6.0"
6+
- "2.6.1"
47
dart_task:
58
- test: --platform vm
69
- test: --platform chrome

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,25 @@ 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

77
## [Unreleased]
8+
### Added
9+
- Support for custom non-standard links ([#61](https://github.com/f3ath/json-api-dart/issues/61))
10+
- Client supports `jsonapi` key in outgoing requests.
11+
- `Document.contentType` constant.
12+
- `IdentifierObject.fromIdentifier` factory method
13+
14+
### Changed
15+
Most of this changes are **BC-BREAKING**.
16+
- `URLBuilder` was renamed to `UrlFactory`.
17+
- `DocumentBuilder` was split into `ServerDocumentFactory` and `ClientDocumentFactory`. Some methods were renamed.
18+
- Static `decodeJson` methods were renamed to `fromJson`.
19+
- `Identifier.equals` now requires the runtime type to be exactly the same.
20+
- `Link.decodeJsonMap` was renamed to `mapFromJson`
21+
- `TargetMatcher` changed its signature.
22+
23+
### Removed
24+
- (Server) `ResourceTarget`, `CollectionTarget`, `RelationshipTarget` classes.
25+
- `QueryParameters` interface.
26+
- `Router` class.
827

928
## [2.1.0] - 2019-12-04
1029
### Added

README.md

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,22 @@ Also in this case the [Response.contentLocation]
100100
will point to the job queue resource. You can fetch the job queue resource periodically and check
101101
the type of the returned resource. Once the operation is complete, the request will return the created resource.
102102

103+
#### Adding JSON:API Object
104+
It is possible to add the [JSON:API Object] to all documents sent by the [JsonApiClient]. To do so, pass the
105+
pre-configured [DocumentFactory] to the [JsonApiClient]:
106+
```dart
107+
import 'package:http/http.dart';
108+
import 'package:json_api/json_api.dart';
109+
110+
void main() async {
111+
final api = Api(version: "1.0");
112+
final httpClient = Client();
113+
final jsonApiClient = JsonApiClient(httpClient, documentFactory: DocumentFactory(api: api));
114+
}
115+
116+
```
117+
118+
103119
# Server
104120
The server included in this package is still under development. It is not yet suitable for real production environment
105121
except maybe for really simple demo or testing cases.
@@ -112,14 +128,19 @@ possible request targets:
112128
- Related resources and collections (parameterized by the resource type, resource id and the relation name)
113129
- Relationships (parameterized by the resource type, resource id and the relation name)
114130

115-
The [URLBuilder] builds those 4 kinds of URLs by the given parameters. The [TargetMatcher] does the opposite,
131+
The [UrlFactory] makes those 4 kinds of URLs by the given parameters. The [TargetMatcher] does the opposite,
116132
it determines the target of the given URL (if possible). Together they form the [UrlDesign].
117133

118134
This package provides one built-in implementation of [UrlDesign] which is called [PathBasedUrlDesign].
119135
The [PathBasedUrlDesign] implements the [Recommended URL Design] allowing you to specify the a common prefix
120136
for all your JSON:API endpoints.
121137

122138

139+
[DocumentFactory]: https://pub.dev/documentation/json_api/latest/document_factory/DocumentFactory-class.html
140+
[Document.errors]: https://pub.dev/documentation/json_api/latest/document/Document/errors.html
141+
[JsonApiClient]: https://pub.dev/documentation/json_api/latest/client/JsonApiClient-class.html
142+
[PathBasedUrlDesign]: https://pub.dev/documentation/json_api/latest/url_design/PathBasedUrlDesign-class.html
143+
[PrimaryData.included]: https://pub.dev/documentation/json_api/latest/document/PrimaryData/included.html
123144
[Response]: https://pub.dev/documentation/json_api/latest/client/Response-class.html
124145
[Response.data]: https://pub.dev/documentation/json_api/latest/client/Response/data.html
125146
[Response.document]: https://pub.dev/documentation/json_api/latest/client/Response/document.html
@@ -131,14 +152,11 @@ for all your JSON:API endpoints.
131152
[Response.status]: https://pub.dev/documentation/json_api/latest/client/Response/status.html
132153
[Response.asyncDocument]: https://pub.dev/documentation/json_api/latest/client/Response/asyncDocument.html
133154
[Response.asyncData]: https://pub.dev/documentation/json_api/latest/client/Response/asyncData.html
134-
135-
[PrimaryData.included]: https://pub.dev/documentation/json_api/latest/document/PrimaryData/included.html
136-
[Document.errors]: https://pub.dev/documentation/json_api/latest/document/Document/errors.html
137-
[URLBuilder]: https://pub.dev/documentation/json_api/latest/url_design/UrlBuilder-class.html
138155
[TargetMatcher]: https://pub.dev/documentation/json_api/latest/url_design/TargetMatcher-class.html
156+
[UrlFactory]: https://pub.dev/documentation/json_api/latest/url_design/UrlFactory-class.html
139157
[UrlDesign]: https://pub.dev/documentation/json_api/latest/url_design/UrlDesign-class.html
140-
[PathBasedUrlDesign]: https://pub.dev/documentation/json_api/latest/url_design/PathBasedUrlDesign-class.html
141158

142159
[Asynchronous Processing]: https://jsonapi.org/recommendations/#asynchronous-processing
143160
[Compound Documents]: https://jsonapi.org/format/#document-compound-documents
161+
[JSON:API Object]: https://jsonapi.org/format/#document-jsonapi-object
144162
[Recommended URL Design]: https://jsonapi.org/recommendations/#urls

example/cars_server.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import 'dart:async';
22
import 'dart:io';
33

44
import 'package:json_api/server.dart';
5-
import 'package:json_api/src/document_builder.dart';
5+
import 'package:json_api/src/server/server_document_factory.dart';
66
import 'package:json_api/url_design.dart';
77

88
import 'cars_server/controller.dart';
@@ -59,9 +59,9 @@ Future<HttpServer> createServer(InternetAddress addr, int port) async {
5959

6060
final httpServer = await HttpServer.bind(addr, port);
6161
final urlDesign = PathBasedUrlDesign(Uri.parse('http://localhost:$port'));
62-
final documentBuilder =
63-
DocumentBuilder(urlBuilder: urlDesign, pagination: pagination);
64-
final jsonApiServer = Server(urlDesign, controller, documentBuilder);
62+
final documentFactory =
63+
ServerDocumentFactory(urlDesign, pagination: pagination);
64+
final jsonApiServer = Server(urlDesign, controller, documentFactory);
6565

6666
httpServer.forEach(jsonApiServer.serve);
6767
return httpServer;

example/cars_server/controller.dart

Lines changed: 53 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import 'dart:async';
22

3+
import 'package:json_api/document.dart';
34
import 'package:json_api/server.dart';
4-
import 'package:json_api/src/document/identifier.dart';
5-
import 'package:json_api/src/document/json_api_error.dart';
6-
import 'package:json_api/src/document/resource.dart';
7-
import 'package:json_api/src/pagination/pagination.dart';
85
import 'package:uuid/uuid.dart';
96

107
import 'dao.dart';
@@ -18,26 +15,27 @@ class CarsController implements Controller {
1815
CarsController(this._dao, this._pagination);
1916

2017
@override
21-
Response fetchCollection(CollectionTarget target, Query query) {
22-
final dao = _getDaoOrThrow(target);
18+
Response fetchCollection(String type, Query query) {
19+
final dao = _getDaoOrThrow(type);
2320
final collection = dao.fetchCollection(
2421
_pagination.limit(query.page), _pagination.offset(query.page));
2522
return CollectionResponse(collection.elements.map(dao.toResource),
2623
included: const [], total: collection.totalCount);
2724
}
2825

2926
@override
30-
Response fetchRelated(RelationshipTarget target, Query query) {
31-
final res = _fetchResourceOrThrow(target);
27+
Response fetchRelated(
28+
String type, String id, String relationship, Query query) {
29+
final res = _fetchResourceOrThrow(type, id);
3230

33-
if (res.toOne.containsKey(target.relationship)) {
34-
final id = res.toOne[target.relationship];
31+
if (res.toOne.containsKey(relationship)) {
32+
final id = res.toOne[relationship];
3533
final resource = _dao[id.type].fetchByIdAsResource(id.id);
3634
return RelatedResourceResponse(resource);
3735
}
3836

39-
if (res.toMany.containsKey(target.relationship)) {
40-
final relationships = res.toMany[target.relationship];
37+
if (res.toMany.containsKey(relationship)) {
38+
final relationships = res.toMany[relationship];
4139
final resources = relationships
4240
.skip(_pagination.offset(query.page))
4341
.take(_pagination.limit(query.page))
@@ -50,10 +48,10 @@ class CarsController implements Controller {
5048
}
5149

5250
@override
53-
Response fetchResource(ResourceTarget target, Query query) {
54-
final dao = _getDaoOrThrow(target);
51+
Response fetchResource(String type, String id, Query query) {
52+
final dao = _getDaoOrThrow(type);
5553

56-
final obj = dao.fetchById(target.id);
54+
final obj = dao.fetchById(id);
5755

5856
if (obj == null) {
5957
return ErrorResponse.notFound(
@@ -74,43 +72,42 @@ class CarsController implements Controller {
7472
}
7573

7674
@override
77-
Response fetchRelationship(RelationshipTarget target, Query query) {
78-
final res = _fetchResourceOrThrow(target);
75+
Response fetchRelationship(
76+
String type, String id, String relationship, Query query) {
77+
final res = _fetchResourceOrThrow(type, id);
7978

80-
if (res.toOne.containsKey(target.relationship)) {
81-
final id = res.toOne[target.relationship];
82-
return ToOneResponse(target, id);
79+
if (res.toOne.containsKey(relationship)) {
80+
return ToOneResponse(type, id, relationship, res.toOne[relationship]);
8381
}
8482

85-
if (res.toMany.containsKey(target.relationship)) {
86-
final ids = res.toMany[target.relationship];
87-
return ToManyResponse(target, ids);
83+
if (res.toMany.containsKey(relationship)) {
84+
return ToManyResponse(type, id, relationship, res.toMany[relationship]);
8885
}
8986
return ErrorResponse.notFound(
9087
[JsonApiError(detail: 'Relationship not found')]);
9188
}
9289

9390
@override
94-
Response deleteResource(ResourceTarget target) {
95-
final dao = _getDaoOrThrow(target);
91+
Response deleteResource(String type, String id) {
92+
final dao = _getDaoOrThrow(type);
9693

97-
final res = dao.fetchByIdAsResource(target.id);
94+
final res = dao.fetchByIdAsResource(id);
9895
if (res == null) {
9996
throw ErrorResponse.notFound(
10097
[JsonApiError(detail: 'Resource not found')]);
10198
}
102-
final dependenciesCount = dao.deleteById(target.id);
99+
final dependenciesCount = dao.deleteById(id);
103100
if (dependenciesCount == 0) {
104101
return NoContentResponse();
105102
}
106103
return MetaResponse({'dependenciesCount': dependenciesCount});
107104
}
108105

109106
@override
110-
Response createResource(CollectionTarget target, Resource resource) {
111-
final dao = _getDaoOrThrow(target);
107+
Response createResource(String type, Resource resource) {
108+
final dao = _getDaoOrThrow(type);
112109

113-
_throwIfIncompatibleTypes(target, resource);
110+
_throwIfIncompatibleTypes(type, resource);
114111

115112
if (resource.id != null) {
116113
if (dao.fetchById(resource.id) != null) {
@@ -126,7 +123,7 @@ class CarsController implements Controller {
126123
toMany: resource.toMany,
127124
toOne: resource.toOne));
128125

129-
if (target.type == 'models') {
126+
if (type == 'models') {
130127
// Insertion is artificially delayed
131128
final job = Job(Future.delayed(Duration(milliseconds: 100), () {
132129
dao.insert(created);
@@ -142,62 +139,64 @@ class CarsController implements Controller {
142139
}
143140

144141
@override
145-
Response updateResource(ResourceTarget target, Resource resource) {
146-
final dao = _getDaoOrThrow(target);
142+
Response updateResource(String type, String id, Resource resource) {
143+
final dao = _getDaoOrThrow(type);
147144

148-
_throwIfIncompatibleTypes(target, resource);
149-
if (dao.fetchById(target.id) == null) {
145+
_throwIfIncompatibleTypes(type, resource);
146+
if (dao.fetchById(id) == null) {
150147
return ErrorResponse.notFound(
151148
[JsonApiError(detail: 'Resource not found')]);
152149
}
153-
final updated = dao.update(target.id, resource);
150+
final updated = dao.update(id, resource);
154151
if (updated == null) {
155152
return NoContentResponse();
156153
}
157154
return ResourceUpdatedResponse(updated);
158155
}
159156

160157
@override
161-
Response replaceToOne(RelationshipTarget target, Identifier identifier) {
162-
final dao = _getDaoOrThrow(target);
158+
Response replaceToOne(
159+
String type, String id, String relationship, Identifier identifier) {
160+
final dao = _getDaoOrThrow(type);
163161

164-
dao.replaceToOne(target.id, target.relationship, identifier);
162+
dao.replaceToOne(id, relationship, identifier);
165163
return NoContentResponse();
166164
}
167165

168166
@override
169-
Response replaceToMany(
170-
RelationshipTarget target, List<Identifier> identifiers) {
171-
final dao = _getDaoOrThrow(target);
167+
Response replaceToMany(String type, String id, String relationship,
168+
List<Identifier> identifiers) {
169+
final dao = _getDaoOrThrow(type);
172170

173-
dao.replaceToMany(target.id, target.relationship, identifiers);
171+
dao.replaceToMany(id, relationship, identifiers);
174172
return NoContentResponse();
175173
}
176174

177175
@override
178-
Response addToMany(RelationshipTarget target, List<Identifier> identifiers) {
179-
final dao = _getDaoOrThrow(target);
176+
Response addToMany(String type, String id, String relationship,
177+
List<Identifier> identifiers) {
178+
final dao = _getDaoOrThrow(type);
180179

181180
return ToManyResponse(
182-
target, dao.addToMany(target.id, target.relationship, identifiers));
181+
type, id, relationship, dao.addToMany(id, relationship, identifiers));
183182
}
184183

185-
void _throwIfIncompatibleTypes(CollectionTarget target, Resource resource) {
186-
if (target.type != resource.type) {
184+
void _throwIfIncompatibleTypes(String type, Resource resource) {
185+
if (type != resource.type) {
187186
throw ErrorResponse.conflict([JsonApiError(detail: 'Incompatible type')]);
188187
}
189188
}
190189

191-
DAO _getDaoOrThrow(CollectionTarget target) {
192-
if (_dao.containsKey(target.type)) return _dao[target.type];
190+
DAO _getDaoOrThrow(String type) {
191+
if (_dao.containsKey(type)) return _dao[type];
193192

194193
throw ErrorResponse.notFound(
195-
[JsonApiError(detail: 'Unknown resource type ${target.type}')]);
194+
[JsonApiError(detail: 'Unknown resource type ${type}')]);
196195
}
197196

198-
Resource _fetchResourceOrThrow(ResourceTarget target) {
199-
final dao = _getDaoOrThrow(target);
200-
final resource = dao.fetchByIdAsResource(target.id);
197+
Resource _fetchResourceOrThrow(String type, String id) {
198+
final dao = _getDaoOrThrow(type);
199+
final resource = dao.fetchByIdAsResource(id);
201200
if (resource == null) {
202201
throw ErrorResponse.notFound(
203202
[JsonApiError(detail: 'Resource not found')]);

example/cars_server/dao.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'package:json_api/json_api.dart';
2-
import 'package:json_api/src/nullable.dart';
32

43
import 'collection.dart';
54
import 'job_queue.dart';
@@ -17,7 +16,7 @@ abstract class DAO<T> {
1716
T fetchById(String id) => _collection[id];
1817

1918
Resource fetchByIdAsResource(String id) =>
20-
nullable(toResource)(_collection[id]);
19+
_collection.containsKey(id) ? toResource(_collection[id]) : null;
2120

2221
void insert(T t); // => collection[t.id] = t;
2322

lib/client.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export 'package:json_api/src/client/client.dart';
2+
export 'package:json_api/src/client/client_document_factory.dart';
23
export 'package:json_api/src/client/response.dart';
3-
export 'package:json_api/src/client/simple_document_builder.dart';
44
export 'package:json_api/src/client/status_code.dart';

lib/query.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export 'package:json_api/src/query/fields.dart';
22
export 'package:json_api/src/query/include.dart';
33
export 'package:json_api/src/query/page.dart';
44
export 'package:json_api/src/query/query.dart';
5+
export 'package:json_api/src/query/sort.dart';

lib/server.dart

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
export 'package:json_api/query.dart';
22
export 'package:json_api/src/pagination/fixed_size_page.dart';
3+
export 'package:json_api/src/pagination/pagination.dart';
34
export 'package:json_api/src/server/controller.dart';
4-
export 'package:json_api/src/server/response.dart';
5+
export 'package:json_api/src/server/response/accepted_response.dart';
6+
export 'package:json_api/src/server/response/collection_response.dart';
7+
export 'package:json_api/src/server/response/error_response.dart';
8+
export 'package:json_api/src/server/response/meta_response.dart';
9+
export 'package:json_api/src/server/response/no_content_response.dart';
10+
export 'package:json_api/src/server/response/related_collection_response.dart';
11+
export 'package:json_api/src/server/response/related_resource_response.dart';
12+
export 'package:json_api/src/server/response/resource_created_response.dart';
13+
export 'package:json_api/src/server/response/resource_response.dart';
14+
export 'package:json_api/src/server/response/resource_updated_response.dart';
15+
export 'package:json_api/src/server/response/response.dart';
16+
export 'package:json_api/src/server/response/see_other_response.dart';
17+
export 'package:json_api/src/server/response/to_many_response.dart';
18+
export 'package:json_api/src/server/response/to_one_response.dart';
519
export 'package:json_api/src/server/server.dart';
620
export 'package:json_api/src/target.dart';
721
export 'package:json_api/url_design.dart';

0 commit comments

Comments
 (0)