Skip to content

Commit ea3563e

Browse files
authored
0.1.0 (#24)
1 parent 2c0790e commit ea3563e

Some content is hidden

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

60 files changed

+1398
-1517
lines changed

.travis.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ dart:
33
- stable
44
dart_task:
55
- test: --platform vm
6-
- test: --platform chrome --exclude-tags vm-only
6+
- test: --platform chrome
7+
- test: --platform firefox
78
- dartfmt: true
8-
- dartanalyzer: --fatal-infos --fatal-warnings lib test
9+
- dartanalyzer: --fatal-infos --fatal-warnings lib test example

CHANGELOG.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# Changelog
22
All notable changes to this project will be documented in this file.
33

4-
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5-
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
4+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
88

9-
## 0.0.1 - 2019-01-08
9+
## 0.1.0 - 2019-02-27
1010
### Added
11-
- Initial client implementation
11+
- Client: fetch resources, collections, related resources and relationships
1212

13-
[Unreleased]: https://github.com/f3ath/json-api-dart/compare/0.0.1...HEAD
13+
[Unreleased]: https://github.com/f3ath/json-api-dart/compare/0.1.0...HEAD

README.md

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

3-
**This is a work in progress. The API may change.**
3+
#### Feature roadmap
4+
##### Client
5+
- [x] Fetching single resources and resource collections
6+
- [x] Fetching relationships and related resources and collections
7+
- [x] Fetching single resources
8+
- [ ] Creating resources
9+
- [ ] Updating resource's attributes
10+
- [ ] Updating resource's relationships
11+
- [ ] Updating relationships
12+
- [ ] Deleting resources
13+
- [ ] Asynchronous processing
414

5-
## General architecture
15+
##### Server (The Server API is not stable yet!)
16+
- [x] Fetching single resources and resource collections
17+
- [x] Fetching relationships and related resources and collections
18+
- [x] Fetching single resources
19+
- [ ] Creating resources
20+
- [ ] Updating resource's attributes
21+
- [ ] Updating resource's relationships
22+
- [ ] Updating relationships
23+
- [ ] Deleting resources
24+
- [ ] Inclusion of related resources
25+
- [ ] Sparse fieldsets
26+
- [ ] Sorting, pagination, filtering
27+
- [ ] Asynchronous processing
628

7-
The library consists of three major parts: Document, Server, and Client.
29+
##### Document
30+
- [ ] Support `meta` and `jsonapi` members
31+
- [ ] Structure Validation
32+
- [ ] Naming Validation
33+
- [ ] JSON:API v1.1 features
834

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).
35+
### Usage
36+
In the VM:
37+
```dart
38+
import 'package:json_api/client.dart';
1339
14-
### Client
15-
This is a JSON:API client based on Dart's native HttpClient.
40+
final client = JsonApiClient();
41+
```
1642

17-
### Server
18-
A JSON:API server. Routes requests, builds responses.
43+
In a browser:
44+
```dart
45+
import 'package:json_api/client.dart';
46+
import 'package:http/browser_client.dart';
47+
48+
final client = JsonApiClient(factory: () => BrowserClient());
49+
```
50+
51+
For usage examples see a corresponding test in `test/functional`.

dart_test.yaml

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

example/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# JSON:API examples
2+
3+
## [Cars Server](./cars_server)
4+
This is a simple JSON:API server which is used in the tests. It provides an API to a collection to car brands ad models.
5+
You can run it locally to play around.
6+
7+
- In you console run `dart example/cars_server.dart`, this will start the server at port 8080.
8+
- Open http://localhost:8080/brands in the browser.
9+
10+
**Warning: Server API is not stable yet!**
11+
12+
## [Cars Client](./cars_client.dart)
13+
A simple client for Cars Server API. It is also used in the tests.

example/cars_client.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import 'dart:async';
2+
3+
import 'package:json_api/client.dart';
4+
import 'package:json_api/src/transport/collection_document.dart';
5+
import 'package:json_api/src/transport/relationship.dart';
6+
import 'package:json_api/src/transport/resource_object.dart';
7+
8+
class CarsClient {
9+
final JsonApiClient c;
10+
final _base = Uri.parse('http://localhost:8080');
11+
12+
CarsClient(this.c);
13+
14+
Future<CollectionDocument> fetchCollection(String type) async {
15+
final response = await c.fetchCollection(_base.replace(path: '/$type'));
16+
return response.document;
17+
}
18+
19+
Future<ToOne> fetchToOne(String type, String id, String name) async {
20+
final response = await c
21+
.fetchToOne(_base.replace(path: '/$type/$id/relationships/$name'));
22+
return response.document;
23+
}
24+
25+
Future<ToMany> fetchToMany(String type, String id, String name) async {
26+
final response = await c
27+
.fetchToMany(_base.replace(path: '/$type/$id/relationships/$name'));
28+
return response.document;
29+
}
30+
31+
Future<ResourceObject> fetchResource(String type, String id) async {
32+
final response = await c.fetchResource(_base.replace(path: '/$type/$id'));
33+
return response.document.resourceObject;
34+
}
35+
}

example/server/server.dart renamed to example/cars_server.dart

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
import 'package:json_api/simple_server.dart';
1+
import 'dart:io';
22

3-
import 'controller.dart';
4-
import 'dao.dart';
5-
import 'model.dart';
3+
import 'package:json_api/src/server/simple_server.dart';
4+
5+
import 'cars_server/controller.dart';
6+
import 'cars_server/dao.dart';
7+
import 'cars_server/model.dart';
8+
9+
void main() async {
10+
final addr = InternetAddress.loopbackIPv4;
11+
final port = 8080;
12+
await createServer().start(addr, port);
13+
print('Listening on ${addr.host}:$port');
14+
}
615

716
SimpleServer createServer() {
817
final cars = CarDAO();
@@ -29,8 +38,6 @@ SimpleServer createServer() {
2938
Brand('5', 'Toyota')
3039
].forEach(brands.insert);
3140

32-
final controller =
33-
CarsController({'brands': brands, 'cities': cities, 'cars': cars});
34-
35-
return SimpleServer(controller);
41+
return SimpleServer(
42+
CarsController({'brands': brands, 'cities': cities, 'cars': cars}));
3643
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import 'dart:async';
2+
3+
import 'package:json_api/src/identifier.dart';
4+
import 'package:json_api/src/resource.dart';
5+
import 'package:json_api/src/server/numbered_page.dart';
6+
import 'package:json_api/src/server/resource_controller.dart';
7+
8+
import 'dao.dart';
9+
10+
class CarsController implements ResourceController {
11+
final Map<String, DAO> dao;
12+
13+
CarsController(this.dao);
14+
15+
@override
16+
bool supports(String type) => dao.containsKey(type);
17+
18+
Future<Collection<Resource>> fetchCollection(
19+
String type, Map<String, String> params) async {
20+
final page =
21+
NumberedPage.fromQueryParameters(params, total: dao[type].length);
22+
return Collection(
23+
dao[type]
24+
.fetchCollection(offset: page.number - 1)
25+
.map(dao[type].toResource),
26+
page: page);
27+
}
28+
29+
@override
30+
Stream<Resource> fetchResources(Iterable<Identifier> ids) async* {
31+
for (final id in ids) {
32+
final obj = dao[id.type].fetchById(id.id);
33+
yield obj == null ? null : dao[id.type].toResource(obj);
34+
}
35+
}
36+
}

example/cars_server/dao.dart

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import 'package:json_api/src/identifier.dart';
2+
import 'package:json_api/src/resource.dart';
3+
4+
import 'model.dart';
5+
6+
abstract class DAO<T> {
7+
final _collection = <String, T>{};
8+
9+
int get length => _collection.length;
10+
11+
Resource toResource(T t);
12+
13+
T fetchById(String id) => _collection[id];
14+
15+
void insert(T t); // => collection[t.id] = t;
16+
17+
Iterable<T> fetchCollection({int offset = 0, int limit = 1}) =>
18+
_collection.values.skip(offset).take(limit);
19+
}
20+
21+
class CarDAO extends DAO<Car> {
22+
Resource toResource(Car _) =>
23+
Resource('cars', _.id, attributes: {'name': _.name});
24+
25+
void insert(Car car) => _collection[car.id] = car;
26+
}
27+
28+
class CityDAO extends DAO<City> {
29+
Resource toResource(City _) =>
30+
Resource('cities', _.id, attributes: {'name': _.name});
31+
32+
void insert(City city) => _collection[city.id] = city;
33+
}
34+
35+
class BrandDAO extends DAO<Brand> {
36+
Resource toResource(Brand brand) => Resource('brands', brand.id, attributes: {
37+
'name': brand.name
38+
}, toOne: {
39+
'hq': brand.headquarters == null
40+
? null
41+
: Identifier('cities', brand.headquarters)
42+
}, toMany: {
43+
'models': brand.models.map((_) => Identifier('cars', _)).toList()
44+
});
45+
46+
void insert(Brand brand) => _collection[brand.id] = brand;
47+
}
Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
1-
abstract class HasId {
2-
String get id;
3-
}
4-
5-
class Brand implements HasId {
6-
final String name;
1+
class Brand {
72
final String id;
83
final String headquarters;
94
final List<String> models;
5+
String name;
106

117
Brand(this.id, this.name, {this.headquarters, this.models = const []});
128
}
139

14-
class City implements HasId {
15-
final String name;
10+
class City {
1611
final String id;
12+
String name;
1713

1814
City(this.id, this.name);
1915
}
2016

21-
class Car implements HasId {
22-
final String name;
17+
class Car {
2318
final String id;
19+
String name;
2420

2521
Car(this.id, this.name);
2622
}

0 commit comments

Comments
 (0)