Skip to content

Commit 27779ee

Browse files
authored
v0.1 alpha (#2)
1 parent ed44872 commit 27779ee

38 files changed

+1711
-734
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# See https://www.dartlang.org/guides/libraries/private-files
1+
# See https://www.dartlang.org/guides/libraries
2+
/private-files
23

34
# Files and directories created by pub
45
.dart_tool/

README.md

Lines changed: 13 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,18 @@
1-
# [JSON:API](http://jsonapi.org) v1.0 HTTP Client
1+
# Implementation of [JSON:API v1.0](http://jsonapi.org) in Dart
22

3-
This is a super simple implementation of JSON:API Client.
3+
**This is a work in progress. The API may change.**
44

5-
### Usage example
6-
```dart
7-
import 'package:json_api/json_api.dart';
8-
import 'package:json_api_document/json_api_document.dart';
5+
## General architecture
96

10-
void main() async {
11-
final client = JsonApiClient(baseUrl: 'http://localhost:8888');
12-
final response = await client.fetchResource('/example');
13-
print((response.document as DataDocument).data.resources.first.attributes); // Attributes{message: Hello world!}
14-
}
15-
```
7+
The library consists of three major parts: Document, Server, and Client.
168

17-
### Supported methods
9+
### Document
10+
This is the core part.
11+
It describes JSON:API Document and its components (e.g. Resource Objects, Identifiers, Relationships, Links),
12+
validation rules (naming conventions, full linkage), and service discovery (e.g. fetching pages and related resources).
1813

19-
- `addToMany`
20-
Adds the identifiers to the to-many relationship via POST request to the url.
21-
- `createResource`
22-
Creates a new resource sending a POST request to the url.
23-
- `deleteResource`
24-
Deletes the resource sending a DELETE request to the url.
25-
- `deleteToMany`
26-
Deletes the identifiers from the to-many relationship via DELETE request to the url.
27-
- `deleteToOne`
28-
Removes a to-one relationship sending PATCH request with "null" data to the url.
29-
- `fetchRelationship`
30-
Fetches a Document containing identifier(s) from the given url.
31-
- `fetchResource`
32-
Fetches a Document containing resource(s) from the given url.
33-
- `setToMany`
34-
Updates (replaces!) a to-many relationship sending the identifiers via PATCH request to the url.
35-
- `setToOne`
36-
Creates or updates a to-one relationship sending a corresponding identifier via PATCH request to the url.
37-
- `updateResource`
38-
Updates the resource sending a PATCH request to the url.
14+
### Client
15+
This is a JSON:API client based on Dart's native HttpClient.
16+
17+
### Server
18+
A JSON:API server. Routes requests, builds responses.

example/example.dart

Lines changed: 0 additions & 11 deletions
This file was deleted.

example/server.dart

Lines changed: 0 additions & 21 deletions
This file was deleted.

examples/server.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import 'dart:io';
2+
3+
import 'server/server.dart';
4+
5+
void main() async {
6+
final addr = InternetAddress.loopbackIPv4;
7+
final port = 8080;
8+
await createServer().start(addr, port);
9+
print('Listening on ${addr.host}:$port');
10+
}

examples/server/controller.dart

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import 'package:json_api/document.dart';
2+
import 'package:json_api/server.dart';
3+
4+
import 'dao.dart';
5+
6+
class CarsController implements ResourceController {
7+
final Map<String, DAO> dao;
8+
9+
CarsController(this.dao);
10+
11+
@override
12+
bool supports(String type) => dao.containsKey(type);
13+
14+
Future<Collection<Resource>> fetchCollection(
15+
String type, Map<String, String> queryParameters) async {
16+
final page = NumberedPage.fromQueryParameters(queryParameters,
17+
total: dao[type].length);
18+
return Collection(
19+
dao[type]
20+
.fetchCollection(offset: page.number - 1)
21+
.map(dao[type].toResource),
22+
page: page);
23+
}
24+
25+
@override
26+
Stream<Resource> fetchResources(Iterable<Identifier> ids) async* {
27+
for (final id in ids) {
28+
final obj = dao[id.type].fetchById(id.id);
29+
yield obj == null ? null : dao[id.type].toResource(obj);
30+
}
31+
}
32+
33+
@override
34+
Future createResource(String type, Resource resource) async {
35+
final obj = dao[type].fromResource(resource);
36+
dao[type].insert(obj);
37+
return null;
38+
}
39+
40+
@override
41+
Future mergeToMany(Identifier id, String name, ToMany rel) async {
42+
final obj = dao[id.type].fetchById(id.id);
43+
rel.identifiers
44+
.map((id) => dao[id.type].fetchById(id.id))
45+
.forEach((related) => dao[id.type].addRelationship(obj, name, related));
46+
47+
return null;
48+
}
49+
}

examples/server/dao.dart

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import 'package:json_api/document.dart';
2+
3+
import 'model.dart';
4+
5+
abstract class DAO<T extends HasId> {
6+
final collection = <String, T>{};
7+
8+
int get length => collection.length;
9+
10+
Resource toResource(T t);
11+
12+
Relationship relationship(String name, T t) {
13+
throw ArgumentError();
14+
}
15+
16+
Map<String, Relationship> relationships(List<String> names, T t) =>
17+
Map.fromIterables(names, names.map((_) => relationship(_, t)));
18+
19+
T fetchById(String id) => collection[id];
20+
21+
void insert(T t) => collection[t.id] = t;
22+
23+
Iterable<T> fetchCollection({int offset = 0, int limit = 1}) =>
24+
collection.values.skip(offset).take(limit);
25+
26+
HasId fromResource(Resource r);
27+
28+
addRelationship(T t, String name, HasId related) {}
29+
}
30+
31+
class CarDAO extends DAO<Car> {
32+
Resource toResource(Car _) =>
33+
Resource('cars', _.id, attributes: {'name': _.name});
34+
35+
@override
36+
HasId fromResource(Resource r) => Car(r.id, r.attributes['name']);
37+
}
38+
39+
class CityDAO extends DAO<City> {
40+
Resource toResource(City _) =>
41+
Resource('cities', _.id, attributes: {'name': _.name});
42+
43+
@override
44+
HasId fromResource(Resource r) => City(r.id, r.attributes['name']);
45+
}
46+
47+
class BrandDAO extends DAO<Brand> {
48+
Resource toResource(Brand brand) => Resource('brands', brand.id,
49+
attributes: {'name': brand.name},
50+
relationships: relationships(['headquarters', 'models'], brand));
51+
52+
Relationship relationship(String name, Brand brand) {
53+
switch (name) {
54+
case 'headquarters':
55+
return ToOne(brand.headquarters == null
56+
? null
57+
: Identifier('cities', brand.headquarters));
58+
case 'models':
59+
return ToMany(brand.models.map((_) => Identifier('cars', _)));
60+
}
61+
throw ArgumentError();
62+
}
63+
64+
@override
65+
HasId fromResource(Resource r) => Brand(r.id, r.attributes['name']);
66+
67+
@override
68+
addRelationship(Brand obj, String name, HasId related) {
69+
switch (name) {
70+
case 'models':
71+
obj.models.add(related.id);
72+
break;
73+
default:
74+
throw ArgumentError.value(name, 'name');
75+
}
76+
}
77+
}

examples/server/model.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
abstract class HasId {
2+
String get id;
3+
}
4+
5+
class Brand implements HasId {
6+
final String name;
7+
final String id;
8+
final String headquarters;
9+
final List<String> models;
10+
11+
Brand(this.id, this.name, {this.headquarters, this.models = const []});
12+
}
13+
14+
class City implements HasId {
15+
final String name;
16+
final String id;
17+
18+
City(this.id, this.name);
19+
}
20+
21+
class Car implements HasId {
22+
final String name;
23+
final String id;
24+
25+
Car(this.id, this.name);
26+
}

examples/server/server.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import 'package:json_api/simple_server.dart';
2+
3+
import 'controller.dart';
4+
import 'dao.dart';
5+
import 'model.dart';
6+
7+
SimpleServer createServer() {
8+
final cars = CarDAO();
9+
[
10+
Car('1', 'Roadster'),
11+
Car('2', 'Model S'),
12+
Car('3', 'Model X'),
13+
Car('4', 'Model 3'),
14+
].forEach(cars.insert);
15+
16+
final cities = CityDAO();
17+
[
18+
City('1', 'Munich'),
19+
City('2', 'Palo Alto'),
20+
City('3', 'Ingolstadt'),
21+
].forEach(cities.insert);
22+
23+
final brands = BrandDAO();
24+
[
25+
Brand('1', 'Tesla', headquarters: '2', models: ['1', '2', '3', '4']),
26+
Brand('2', 'BMW', headquarters: '1'),
27+
Brand('3', 'Audi', headquarters: '3'),
28+
Brand('4', 'Ford'),
29+
Brand('5', 'Toyota')
30+
].forEach(brands.insert);
31+
32+
final controller =
33+
CarsController({'brands': brands, 'cities': cities, 'cars': cars});
34+
35+
return SimpleServer(controller);
36+
}

lib/client.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import 'package:json_api/document.dart';
2+
3+
export 'package:json_api/src/client/dart_http_client.dart';
4+
5+
abstract class Client implements ResourceFetcher, CollectionFetcher {
6+
Future<Response<CollectionDocument>> fetchCollection(Uri uri,
7+
{Map<String, String> headers});
8+
9+
Future<Response<ResourceDocument>> fetchResource(Uri uri,
10+
{Map<String, String> headers});
11+
12+
Future<Response<ToOne>> fetchToOne(Uri uri, {Map<String, String> headers});
13+
14+
Future<Response<ToMany>> fetchToMany(Uri uri, {Map<String, String> headers});
15+
16+
Future<Response<ResourceDocument>> createResource(Uri uri, Resource r,
17+
{Map<String, String> headers});
18+
19+
Future<Response<ToMany>> addToMany(Uri uri, Iterable<Identifier> ids,
20+
{Map<String, String> headers});
21+
}

0 commit comments

Comments
 (0)