Skip to content

Commit

Permalink
0.1.0 (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
f3ath authored Feb 27, 2019
1 parent 2c0790e commit ea3563e
Show file tree
Hide file tree
Showing 60 changed files with 1,398 additions and 1,517 deletions.
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ dart:
- stable
dart_task:
- test: --platform vm
- test: --platform chrome --exclude-tags vm-only
- test: --platform chrome
- test: --platform firefox
- dartfmt: true
- dartanalyzer: --fatal-infos --fatal-warnings lib test
- dartanalyzer: --fatal-infos --fatal-warnings lib test example
10 changes: 5 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Changelog
All notable changes to this project will be documented in this file.

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

## [Unreleased]

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

[Unreleased]: https://github.com/f3ath/json-api-dart/compare/0.0.1...HEAD
[Unreleased]: https://github.com/f3ath/json-api-dart/compare/0.1.0...HEAD
55 changes: 44 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,51 @@
# Implementation of [JSON:API v1.0](http://jsonapi.org) in Dart

**This is a work in progress. The API may change.**
#### Feature roadmap
##### Client
- [x] Fetching single resources and resource collections
- [x] Fetching relationships and related resources and collections
- [x] Fetching single resources
- [ ] Creating resources
- [ ] Updating resource's attributes
- [ ] Updating resource's relationships
- [ ] Updating relationships
- [ ] Deleting resources
- [ ] Asynchronous processing

## General architecture
##### Server (The Server API is not stable yet!)
- [x] Fetching single resources and resource collections
- [x] Fetching relationships and related resources and collections
- [x] Fetching single resources
- [ ] Creating resources
- [ ] Updating resource's attributes
- [ ] Updating resource's relationships
- [ ] Updating relationships
- [ ] Deleting resources
- [ ] Inclusion of related resources
- [ ] Sparse fieldsets
- [ ] Sorting, pagination, filtering
- [ ] Asynchronous processing

The library consists of three major parts: Document, Server, and Client.
##### Document
- [ ] Support `meta` and `jsonapi` members
- [ ] Structure Validation
- [ ] Naming Validation
- [ ] JSON:API v1.1 features

### Document
This is the core part.
It describes JSON:API Document and its components (e.g. Resource Objects, Identifiers, Relationships, Links),
validation rules (naming conventions, full linkage), and service discovery (e.g. fetching pages and related resources).
### Usage
In the VM:
```dart
import 'package:json_api/client.dart';
### Client
This is a JSON:API client based on Dart's native HttpClient.
final client = JsonApiClient();
```

### Server
A JSON:API server. Routes requests, builds responses.
In a browser:
```dart
import 'package:json_api/client.dart';
import 'package:http/browser_client.dart';
final client = JsonApiClient(factory: () => BrowserClient());
```

For usage examples see a corresponding test in `test/functional`.
2 changes: 0 additions & 2 deletions dart_test.yaml

This file was deleted.

13 changes: 13 additions & 0 deletions example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# JSON:API examples

## [Cars Server](./cars_server)
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.
You can run it locally to play around.

- In you console run `dart example/cars_server.dart`, this will start the server at port 8080.
- Open http://localhost:8080/brands in the browser.

**Warning: Server API is not stable yet!**

## [Cars Client](./cars_client.dart)
A simple client for Cars Server API. It is also used in the tests.
35 changes: 35 additions & 0 deletions example/cars_client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'dart:async';

import 'package:json_api/client.dart';
import 'package:json_api/src/transport/collection_document.dart';
import 'package:json_api/src/transport/relationship.dart';
import 'package:json_api/src/transport/resource_object.dart';

class CarsClient {
final JsonApiClient c;
final _base = Uri.parse('http://localhost:8080');

CarsClient(this.c);

Future<CollectionDocument> fetchCollection(String type) async {
final response = await c.fetchCollection(_base.replace(path: '/$type'));
return response.document;
}

Future<ToOne> fetchToOne(String type, String id, String name) async {
final response = await c
.fetchToOne(_base.replace(path: '/$type/$id/relationships/$name'));
return response.document;
}

Future<ToMany> fetchToMany(String type, String id, String name) async {
final response = await c
.fetchToMany(_base.replace(path: '/$type/$id/relationships/$name'));
return response.document;
}

Future<ResourceObject> fetchResource(String type, String id) async {
final response = await c.fetchResource(_base.replace(path: '/$type/$id'));
return response.document.resourceObject;
}
}
23 changes: 15 additions & 8 deletions example/server/server.dart → example/cars_server.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import 'package:json_api/simple_server.dart';
import 'dart:io';

import 'controller.dart';
import 'dao.dart';
import 'model.dart';
import 'package:json_api/src/server/simple_server.dart';

import 'cars_server/controller.dart';
import 'cars_server/dao.dart';
import 'cars_server/model.dart';

void main() async {
final addr = InternetAddress.loopbackIPv4;
final port = 8080;
await createServer().start(addr, port);
print('Listening on ${addr.host}:$port');
}

SimpleServer createServer() {
final cars = CarDAO();
Expand All @@ -29,8 +38,6 @@ SimpleServer createServer() {
Brand('5', 'Toyota')
].forEach(brands.insert);

final controller =
CarsController({'brands': brands, 'cities': cities, 'cars': cars});

return SimpleServer(controller);
return SimpleServer(
CarsController({'brands': brands, 'cities': cities, 'cars': cars}));
}
36 changes: 36 additions & 0 deletions example/cars_server/controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'dart:async';

import 'package:json_api/src/identifier.dart';
import 'package:json_api/src/resource.dart';
import 'package:json_api/src/server/numbered_page.dart';
import 'package:json_api/src/server/resource_controller.dart';

import 'dao.dart';

class CarsController implements ResourceController {
final Map<String, DAO> dao;

CarsController(this.dao);

@override
bool supports(String type) => dao.containsKey(type);

Future<Collection<Resource>> fetchCollection(
String type, Map<String, String> params) async {
final page =
NumberedPage.fromQueryParameters(params, total: dao[type].length);
return Collection(
dao[type]
.fetchCollection(offset: page.number - 1)
.map(dao[type].toResource),
page: page);
}

@override
Stream<Resource> fetchResources(Iterable<Identifier> ids) async* {
for (final id in ids) {
final obj = dao[id.type].fetchById(id.id);
yield obj == null ? null : dao[id.type].toResource(obj);
}
}
}
47 changes: 47 additions & 0 deletions example/cars_server/dao.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'package:json_api/src/identifier.dart';
import 'package:json_api/src/resource.dart';

import 'model.dart';

abstract class DAO<T> {
final _collection = <String, T>{};

int get length => _collection.length;

Resource toResource(T t);

T fetchById(String id) => _collection[id];

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

Iterable<T> fetchCollection({int offset = 0, int limit = 1}) =>
_collection.values.skip(offset).take(limit);
}

class CarDAO extends DAO<Car> {
Resource toResource(Car _) =>
Resource('cars', _.id, attributes: {'name': _.name});

void insert(Car car) => _collection[car.id] = car;
}

class CityDAO extends DAO<City> {
Resource toResource(City _) =>
Resource('cities', _.id, attributes: {'name': _.name});

void insert(City city) => _collection[city.id] = city;
}

class BrandDAO extends DAO<Brand> {
Resource toResource(Brand brand) => Resource('brands', brand.id, attributes: {
'name': brand.name
}, toOne: {
'hq': brand.headquarters == null
? null
: Identifier('cities', brand.headquarters)
}, toMany: {
'models': brand.models.map((_) => Identifier('cars', _)).toList()
});

void insert(Brand brand) => _collection[brand.id] = brand;
}
16 changes: 6 additions & 10 deletions example/server/model.dart → example/cars_server/model.dart
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
abstract class HasId {
String get id;
}

class Brand implements HasId {
final String name;
class Brand {
final String id;
final String headquarters;
final List<String> models;
String name;

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

class City implements HasId {
final String name;
class City {
final String id;
String name;

City(this.id, this.name);
}

class Car implements HasId {
final String name;
class Car {
final String id;
String name;

Car(this.id, this.name);
}
10 changes: 0 additions & 10 deletions example/server.dart

This file was deleted.

49 changes: 0 additions & 49 deletions example/server/controller.dart

This file was deleted.

Loading

0 comments on commit ea3563e

Please sign in to comment.