|
1 | 1 | import 'dart:async'; |
2 | 2 |
|
3 | 3 | import 'package:json_api/document.dart'; |
4 | | -import 'package:json_api/src/server/contracts/controller.dart'; |
5 | | -import 'package:json_api/src/server/numbered_page.dart'; |
| 4 | +import 'package:json_api/server.dart'; |
| 5 | +import 'package:json_api/src/server/request_target.dart'; |
6 | 6 | import 'package:uuid/uuid.dart'; |
7 | 7 |
|
8 | 8 | import 'dao.dart'; |
9 | 9 |
|
10 | 10 | class CarsController implements JsonApiController { |
11 | | - final Map<String, DAO> dao; |
| 11 | + final Map<String, DAO> _dao; |
12 | 12 |
|
13 | | - CarsController(this.dao); |
| 13 | + CarsController(this._dao); |
14 | 14 |
|
15 | 15 | @override |
16 | | - Future fetchCollection(FetchCollectionRequest r) async { |
17 | | - if (!dao.containsKey(r.type)) { |
18 | | - return r.errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
19 | | - } |
20 | | - final page = NumberedPage.fromQueryParameters(r.uri.queryParameters, |
21 | | - total: dao[r.type].length); |
22 | | - return r.sendCollection( |
23 | | - dao[r.type] |
| 16 | + Future<void> fetchCollection( |
| 17 | + ControllerRequest<CollectionTarget, void> request, |
| 18 | + FetchCollectionResponse response) async { |
| 19 | + if (!_dao.containsKey(request.target.type)) { |
| 20 | + return response |
| 21 | + .errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
| 22 | + } |
| 23 | + final page = NumberedPage.fromQueryParameters(request.uri.queryParameters, |
| 24 | + total: _dao[request.target.type].length); |
| 25 | + return response.sendCollection(Collection( |
| 26 | + _dao[request.target.type] |
24 | 27 | .fetchCollection(offset: page.offset) |
25 | | - .map(dao[r.type].toResource), |
26 | | - page: page); |
| 28 | + .map(_dao[request.target.type].toResource), |
| 29 | + page: page)); |
27 | 30 | } |
28 | 31 |
|
29 | 32 | @override |
30 | | - Future fetchRelated(FetchRelatedRequest r) { |
31 | | - if (!dao.containsKey(r.type)) { |
32 | | - return r.errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
33 | | - } |
34 | | - final res = dao[r.type].fetchByIdAsResource(r.id); |
| 33 | + Future<void> fetchRelated(ControllerRequest<RelatedTarget, void> request, |
| 34 | + FetchRelatedResponse response) async { |
| 35 | + if (!_dao.containsKey(request.target.type)) { |
| 36 | + return response |
| 37 | + .errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
| 38 | + } |
| 39 | + final res = |
| 40 | + _dao[request.target.type].fetchByIdAsResource(request.target.id); |
35 | 41 | if (res == null) { |
36 | | - return r.errorNotFound([JsonApiError(detail: 'Resource not found')]); |
| 42 | + return response |
| 43 | + .errorNotFound([JsonApiError(detail: 'Resource not found')]); |
37 | 44 | } |
38 | 45 |
|
39 | | - if (res.toOne.containsKey(r.relationship)) { |
40 | | - final id = res.toOne[r.relationship]; |
41 | | - final resource = dao[id.type].fetchByIdAsResource(id.id); |
42 | | - return r.sendResource(resource); |
| 46 | + if (res.toOne.containsKey(request.target.relationship)) { |
| 47 | + final id = res.toOne[request.target.relationship]; |
| 48 | + final resource = _dao[id.type].fetchByIdAsResource(id.id); |
| 49 | + return response.sendResource(resource); |
43 | 50 | } |
44 | 51 |
|
45 | | - if (res.toMany.containsKey(r.relationship)) { |
46 | | - final resources = res.toMany[r.relationship] |
47 | | - .map((id) => dao[id.type].fetchByIdAsResource(id.id)); |
48 | | - return r.sendCollection(resources); |
| 52 | + if (res.toMany.containsKey(request.target.relationship)) { |
| 53 | + final resources = res.toMany[request.target.relationship] |
| 54 | + .map((id) => _dao[id.type].fetchByIdAsResource(id.id)); |
| 55 | + return response.sendCollection(Collection(resources)); |
49 | 56 | } |
50 | | - return r.errorNotFound([JsonApiError(detail: 'Relationship not found')]); |
| 57 | + return response |
| 58 | + .errorNotFound([JsonApiError(detail: 'Relationship not found')]); |
51 | 59 | } |
52 | 60 |
|
53 | 61 | @override |
54 | | - Future fetchResource(FetchResourceRequest r) { |
55 | | - if (!dao.containsKey(r.type)) { |
56 | | - return r.errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
57 | | - } |
58 | | - final res = dao[r.type].fetchByIdAsResource(r.id); |
| 62 | + Future<void> fetchResource(ControllerRequest<ResourceTarget, void> request, |
| 63 | + FetchResourceResponse response) async { |
| 64 | + if (!_dao.containsKey(request.target.type)) { |
| 65 | + return response |
| 66 | + .errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
| 67 | + } |
| 68 | + final res = |
| 69 | + _dao[request.target.type].fetchByIdAsResource(request.target.id); |
59 | 70 | if (res == null) { |
60 | | - return r.errorNotFound([JsonApiError(detail: 'Resource not found')]); |
| 71 | + return response |
| 72 | + .errorNotFound([JsonApiError(detail: 'Resource not found')]); |
61 | 73 | } |
62 | | - final fetchById = (Identifier _) => dao[_.type].fetchByIdAsResource(_.id); |
| 74 | + final fetchById = (Identifier _) => _dao[_.type].fetchByIdAsResource(_.id); |
63 | 75 |
|
64 | 76 | final children = res.toOne.values |
65 | 77 | .map(fetchById) |
66 | 78 | .followedBy(res.toMany.values.expand((_) => _.map(fetchById))); |
67 | 79 |
|
68 | | - return r.sendResource(res, included: children); |
| 80 | + return response.sendResource(res, included: children); |
69 | 81 | } |
70 | 82 |
|
71 | 83 | @override |
72 | | - Future fetchRelationship(FetchRelationshipRequest r) { |
73 | | - if (!dao.containsKey(r.type)) { |
74 | | - return r.errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
75 | | - } |
76 | | - final res = dao[r.type].fetchByIdAsResource(r.id); |
| 84 | + Future<void> fetchRelationship( |
| 85 | + ControllerRequest<RelationshipTarget, void> request, |
| 86 | + FetchRelationshipResponse response) async { |
| 87 | + if (!_dao.containsKey(request.target.type)) { |
| 88 | + return response |
| 89 | + .errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
| 90 | + } |
| 91 | + final res = |
| 92 | + _dao[request.target.type].fetchByIdAsResource(request.target.id); |
77 | 93 | if (res == null) { |
78 | | - return r.errorNotFound([JsonApiError(detail: 'Resource not found')]); |
| 94 | + return response |
| 95 | + .errorNotFound([JsonApiError(detail: 'Resource not found')]); |
79 | 96 | } |
80 | 97 |
|
81 | | - if (res.toOne.containsKey(r.relationship)) { |
82 | | - final id = res.toOne[r.relationship]; |
83 | | - return r.sendToOne(id); |
| 98 | + if (res.toOne.containsKey(request.target.relationship)) { |
| 99 | + final id = res.toOne[request.target.relationship]; |
| 100 | + return response.sendToOne(id); |
84 | 101 | } |
85 | 102 |
|
86 | | - if (res.toMany.containsKey(r.relationship)) { |
87 | | - final ids = res.toMany[r.relationship]; |
88 | | - return r.sendToMany(ids); |
| 103 | + if (res.toMany.containsKey(request.target.relationship)) { |
| 104 | + final ids = res.toMany[request.target.relationship]; |
| 105 | + return response.sendToMany(ids); |
89 | 106 | } |
90 | | - return r.errorNotFound([JsonApiError(detail: 'Relationship not found')]); |
| 107 | + return response |
| 108 | + .errorNotFound([JsonApiError(detail: 'Relationship not found')]); |
91 | 109 | } |
92 | 110 |
|
93 | 111 | @override |
94 | | - Future deleteResource(DeleteResourceRequest r) { |
95 | | - if (!dao.containsKey(r.type)) { |
96 | | - return r.errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
97 | | - } |
98 | | - final res = dao[r.type].fetchByIdAsResource(r.id); |
| 112 | + Future<void> deleteResource(ControllerRequest<ResourceTarget, void> request, |
| 113 | + DeleteResourceResponse response) async { |
| 114 | + if (!_dao.containsKey(request.target.type)) { |
| 115 | + return response |
| 116 | + .errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
| 117 | + } |
| 118 | + final res = |
| 119 | + _dao[request.target.type].fetchByIdAsResource(request.target.id); |
99 | 120 | if (res == null) { |
100 | | - return r.errorNotFound([JsonApiError(detail: 'Resource not found')]); |
| 121 | + return response |
| 122 | + .errorNotFound([JsonApiError(detail: 'Resource not found')]); |
101 | 123 | } |
102 | | - final dependenciesCount = dao[r.type].deleteById(r.id); |
| 124 | + final dependenciesCount = |
| 125 | + _dao[request.target.type].deleteById(request.target.id); |
103 | 126 | if (dependenciesCount == 0) { |
104 | | - return r.sendNoContent(); |
| 127 | + return response.sendNoContent(); |
105 | 128 | } |
106 | | - return r.sendMeta({'dependenciesCount': dependenciesCount}); |
| 129 | + return response.sendMeta({'dependenciesCount': dependenciesCount}); |
107 | 130 | } |
108 | 131 |
|
109 | | - Future createResource(CreateResourceRequest r) async { |
110 | | - if (!dao.containsKey(r.type)) { |
111 | | - return r.errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
| 132 | + @override |
| 133 | + Future<void> createResource( |
| 134 | + ControllerRequest<CollectionTarget, Resource> request, |
| 135 | + CreateResourceResponse response) async { |
| 136 | + if (!_dao.containsKey(request.target.type)) { |
| 137 | + return response |
| 138 | + .errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
112 | 139 | } |
113 | | - if (r.type != r.resource.type) { |
114 | | - return r.errorConflict([JsonApiError(detail: 'Incompatible type')]); |
| 140 | + if (request.target.type != request.payload.type) { |
| 141 | + return response |
| 142 | + .errorConflict([JsonApiError(detail: 'Incompatible type')]); |
115 | 143 | } |
116 | 144 |
|
117 | | - if (r.resource.hasId) { |
118 | | - if (dao[r.type].fetchById(r.resource.id) != null) { |
119 | | - return r |
| 145 | + if (request.payload.hasId) { |
| 146 | + if (_dao[request.target.type].fetchById(request.payload.id) != null) { |
| 147 | + return response |
120 | 148 | .errorConflict([JsonApiError(detail: 'Resource already exists')]); |
121 | 149 | } |
122 | | - final created = dao[r.type].create(r.resource); |
123 | | - dao[r.type].insert(created); |
124 | | - return r.sendNoContent(); |
125 | | - } |
126 | | - |
127 | | - final created = dao[r.type].create(Resource(r.resource.type, Uuid().v4(), |
128 | | - attributes: r.resource.attributes, |
129 | | - toMany: r.resource.toMany, |
130 | | - toOne: r.resource.toOne)); |
131 | | - dao[r.type].insert(created); |
132 | | - return r.sendCreated(dao[r.type].toResource(created)); |
| 150 | + final created = _dao[request.target.type].create(request.payload); |
| 151 | + _dao[request.target.type].insert(created); |
| 152 | + return response.sendNoContent(); |
| 153 | + } |
| 154 | + |
| 155 | + final created = _dao[request.target.type].create(Resource( |
| 156 | + request.payload.type, Uuid().v4(), |
| 157 | + attributes: request.payload.attributes, |
| 158 | + toMany: request.payload.toMany, |
| 159 | + toOne: request.payload.toOne)); |
| 160 | + _dao[request.target.type].insert(created); |
| 161 | + return response.sendCreated(_dao[request.target.type].toResource(created)); |
133 | 162 | } |
134 | 163 |
|
135 | 164 | @override |
136 | | - Future updateResource(UpdateResourceRequest r) async { |
137 | | - if (!dao.containsKey(r.type)) { |
138 | | - return r.errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
139 | | - } |
140 | | - if (r.type != r.resource.type) { |
141 | | - return r.errorConflict([JsonApiError(detail: 'Incompatible type')]); |
142 | | - } |
143 | | - if (dao[r.type].fetchById(r.id) == null) { |
144 | | - return r.errorNotFound([JsonApiError(detail: 'Resource not found')]); |
145 | | - } |
146 | | - final updated = dao[r.type].update(r.id, r.resource); |
| 165 | + Future<void> updateResource( |
| 166 | + ControllerRequest<ResourceTarget, Resource> request, |
| 167 | + UpdateResourceResponse response) async { |
| 168 | + if (!_dao.containsKey(request.target.type)) { |
| 169 | + return response |
| 170 | + .errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
| 171 | + } |
| 172 | + if (request.target.type != request.payload.type) { |
| 173 | + return response |
| 174 | + .errorConflict([JsonApiError(detail: 'Incompatible type')]); |
| 175 | + } |
| 176 | + if (_dao[request.target.type].fetchById(request.target.id) == null) { |
| 177 | + return response |
| 178 | + .errorNotFound([JsonApiError(detail: 'Resource not found')]); |
| 179 | + } |
| 180 | + final updated = |
| 181 | + _dao[request.target.type].update(request.target.id, request.payload); |
147 | 182 | if (updated == null) { |
148 | | - return r.sendNoContent(); |
| 183 | + return response.sendNoContent(); |
149 | 184 | } |
150 | | - return r.sendUpdated(updated); |
| 185 | + return response.sendUpdated(updated); |
151 | 186 | } |
152 | 187 |
|
153 | 188 | @override |
154 | | - Future replaceToOne(ReplaceToOneRequest r) async { |
155 | | - if (!dao.containsKey(r.type)) { |
156 | | - return r.errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
157 | | - } |
158 | | - dao[r.type].replaceToOne(r.id, r.relationship, r.identifier); |
159 | | - return r.sendNoContent(); |
| 189 | + Future<void> replaceToOne( |
| 190 | + ControllerRequest<RelationshipTarget, Identifier> request, |
| 191 | + ReplaceToOneResponse response) async { |
| 192 | + if (!_dao.containsKey(request.target.type)) { |
| 193 | + return response |
| 194 | + .errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
| 195 | + } |
| 196 | + _dao[request.target.type].replaceToOne( |
| 197 | + request.target.id, request.target.relationship, request.payload); |
| 198 | + return response.sendNoContent(); |
160 | 199 | } |
161 | 200 |
|
162 | 201 | @override |
163 | | - Future replaceToMany(ReplaceToManyRequest r) async { |
164 | | - if (!dao.containsKey(r.type)) { |
165 | | - return r.errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
166 | | - } |
167 | | - dao[r.type].replaceToMany(r.id, r.relationship, r.identifiers); |
168 | | - return r.sendNoContent(); |
| 202 | + Future<void> replaceToMany( |
| 203 | + ControllerRequest<RelationshipTarget, Iterable<Identifier>> request, |
| 204 | + ReplaceToManyResponse response) async { |
| 205 | + if (!_dao.containsKey(request.target.type)) { |
| 206 | + return response |
| 207 | + .errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
| 208 | + } |
| 209 | + _dao[request.target.type].replaceToMany( |
| 210 | + request.target.id, request.target.relationship, request.payload); |
| 211 | + return response.sendNoContent(); |
169 | 212 | } |
170 | 213 |
|
171 | 214 | @override |
172 | | - Future addToMany(AddToManyRequest r) async { |
173 | | - if (!dao.containsKey(r.type)) { |
174 | | - return r.errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
175 | | - } |
176 | | - final result = dao[r.type].addToMany(r.id, r.relationship, r.identifiers); |
177 | | - return r.sendToMany(result); |
| 215 | + Future<void> addToMany( |
| 216 | + ControllerRequest<RelationshipTarget, Iterable<Identifier>> request, |
| 217 | + AddToManyResponse response) async { |
| 218 | + if (!_dao.containsKey(request.target.type)) { |
| 219 | + return response |
| 220 | + .errorNotFound([JsonApiError(detail: 'Unknown resource type')]); |
| 221 | + } |
| 222 | + final result = _dao[request.target.type].addToMany( |
| 223 | + request.target.id, request.target.relationship, request.payload); |
| 224 | + return response.sendToMany(result); |
178 | 225 | } |
179 | 226 | } |
0 commit comments