Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Improved ResourceController error handling
- Resource creation
- Resource deletion

## 0.1.0 - 2019-02-27
### Added
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
# Implementation of [JSON:API v1.0](http://jsonapi.org) in Dart

#### Feature roadmap
##### Client
### Feature roadmap
#### Client
- [x] Fetching single resources and resource collections
- [x] Fetching relationships and related resources and collections
- [x] Fetching single resources
- [x] Creating resources
- [x] Deleting resources
- [ ] Updating resource's attributes
- [ ] Updating resource's relationships
- [ ] Updating relationships
- [ ] Deleting resources
- [ ] Asynchronous processing
- [ ] Optional check for `Content-Type` header in incoming responses

##### Server (The Server API is not stable yet!)
#### 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
- [x] Creating resources
- [x] Deleting resources
- [ ] Updating resource's attributes
- [ ] Updating resource's relationships
- [ ] Updating relationships
- [ ] Deleting resources
- [ ] Inclusion of related resources
- [ ] Sparse fieldsets
- [ ] Sorting, pagination, filtering
- [ ] Asynchronous processing
- [ ] Optional check for `Content-Type` header in incoming requests
- [ ] Support annotations in resource mappers (?)

##### Document
- [ ] Support `meta` and `jsonapi` members
#### Document (The Document API is not stable yet!)
- [ ] Support `meta` members
- [ ] Support `jsonapi` members
- [ ] Structure Validation including compound documents
- [ ] Support relationship objects lacking the `data` member
- [ ] Naming Validation
Expand Down
26 changes: 20 additions & 6 deletions example/cars_server/controller.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import 'dart:async';

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

Expand All @@ -17,9 +18,9 @@ class CarsController implements ResourceController {
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);
String type, JsonApiHttpRequest request) async {
final page = NumberedPage.fromQueryParameters(request.uri.queryParameters,
total: dao[type].length);
return Collection(
dao[type]
.fetchCollection(offset: page.number - 1)
Expand All @@ -40,7 +41,7 @@ class CarsController implements ResourceController {

@override
Future<Resource> createResource(
String type, Resource resource, Map<String, String> params) async {
String type, Resource resource, JsonApiHttpRequest request) async {
if (type != resource.type) {
throw ResourceControllerException(409, detail: 'Incompatible type');
}
Expand All @@ -57,4 +58,17 @@ class CarsController implements ResourceController {
dao[type].insert(obj);
return dao[type].toResource(obj);
}

@override
Future<Map<String, Object>> deleteResource(
String type, String id, JsonApiHttpRequest request) async {
if (dao[type].fetchById(id) == null) {
throw ResourceControllerException(404, detail: 'Resource not found');
}
final deps = dao[type].deleteById(id);
if (deps > 0) {
return {'deps': deps};
}
return null;
}
}
19 changes: 17 additions & 2 deletions example/cars_server/dao.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:json_api/src/identifier.dart';
import 'package:json_api/src/resource.dart';
import 'package:json_api/src/document/identifier.dart';
import 'package:json_api/src/document/resource.dart';

import 'model.dart';

Expand All @@ -18,6 +18,12 @@ abstract class DAO<T> {

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

/// Returns the number of depending objects the entity had
int deleteById(String id) {
_collection.remove(id);
return 0;
}
}

class ModelDAO extends DAO<Model> {
Expand Down Expand Up @@ -59,4 +65,13 @@ class CompanyDAO extends DAO<Company> {
Company create(Resource r) {
return Company(r.id, r.attributes['name']);
}

@override
int deleteById(String id) {
final company = fetchById(id);
int deps = company.headquarters == null ? 0 : 1;
deps += company.models.length;
_collection.remove(id);
return deps;
}
}
3 changes: 1 addition & 2 deletions lib/client.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export 'package:json_api/src/client/client.dart';
export 'package:json_api/src/identifier.dart';
export 'package:json_api/src/resource.dart';
export 'package:json_api/src/document.dart';
17 changes: 16 additions & 1 deletion lib/src/client/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import 'package:json_api/src/client/status_code.dart';
import 'package:json_api/src/document/collection_document.dart';
import 'package:json_api/src/document/document.dart';
import 'package:json_api/src/document/error_document.dart';
import 'package:json_api/src/document/meta_document.dart';
import 'package:json_api/src/document/relationship.dart';
import 'package:json_api/src/document/resource.dart';
import 'package:json_api/src/document/resource_document.dart';
import 'package:json_api/src/document/resource_object.dart';
import 'package:json_api/src/nullable.dart';
import 'package:json_api/src/resource.dart';

typedef D ResponseParser<D extends Document>(Object j);

Expand Down Expand Up @@ -65,6 +66,11 @@ class JsonApiClient {
_post(ResourceDocument.fromJson, uri,
ResourceDocument(ResourceObject.fromResource(resource)), headers);

/// Deletes the resource.
Future<Response<MetaDocument>> deleteResource(Uri uri,
{Map<String, String> headers}) =>
_delete(MetaDocument.fromJson, uri, headers);

// /// Adds the [identifiers] to a to-many relationship identified by [uri]
// Future<Response<ToMany>> addToMany(Uri uri, Iterable<Identifier> identifiers,
// {Map<String, String> headers}) =>
Expand All @@ -89,6 +95,15 @@ class JsonApiClient {
..addAll(headers ?? {})
..addAll({'Accept': contentType})));

Future<Response<D>> _delete<D extends Document>(
ResponseParser<D> parse, uri, Map<String, String> headers) =>
_call(
parse,
(_) => _.delete(uri,
headers: {}
..addAll(headers ?? {})
..addAll({'Accept': contentType})));

Future<Response<D>> _post<D extends Document>(ResponseParser<D> parse, uri,
Document document, Map<String, String> headers) =>
_call(
Expand Down
12 changes: 12 additions & 0 deletions lib/src/document.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export 'package:json_api/src/document/collection_document.dart';
export 'package:json_api/src/document/document.dart';
export 'package:json_api/src/document/error_document.dart';
export 'package:json_api/src/document/error_object.dart';
export 'package:json_api/src/document/identifier.dart';
export 'package:json_api/src/document/identifier_object.dart';
export 'package:json_api/src/document/link.dart';
export 'package:json_api/src/document/meta_document.dart';
export 'package:json_api/src/document/relationship.dart';
export 'package:json_api/src/document/resource.dart';
export 'package:json_api/src/document/resource_document.dart';
export 'package:json_api/src/document/resource_object.dart';
2 changes: 1 addition & 1 deletion lib/src/document/collection_document.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:json_api/src/document/document.dart';
import 'package:json_api/src/document/link.dart';
import 'package:json_api/src/document/resource_object.dart';

class CollectionDocument implements Document {
class CollectionDocument extends Document {
final List<ResourceObject> collection;
final List<ResourceObject> included;

Expand Down
8 changes: 7 additions & 1 deletion lib/src/document/document.dart
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
abstract class Document {}
class Document {
final meta = <String, Object>{};

Document({Map<String, Object> meta}) {
this.meta.addAll(meta ?? {});
}
}
2 changes: 1 addition & 1 deletion lib/src/document/error_document.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:json_api/src/document/document.dart';
import 'package:json_api/src/document/error_object.dart';

class ErrorDocument implements Document {
class ErrorDocument extends Document {
final errors = <ErrorObject>[];

ErrorDocument(Iterable<ErrorObject> errors) {
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion lib/src/document/identifier_object.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:json_api/src/identifier.dart';
import 'package:json_api/src/document/identifier.dart';

class IdentifierObject {
final String type;
Expand Down
16 changes: 16 additions & 0 deletions lib/src/document/meta_document.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:json_api/src/document/document.dart';

class MetaDocument extends Document {
MetaDocument(Map<String, Object> meta) : super(meta: meta) {
ArgumentError.checkNotNull(meta);
}

static MetaDocument fromJson(Object json) {
if (json is Map) {
return MetaDocument(json['meta']);
}
throw 'Can not parse MetaDocument from $json';
}

toJson() => {'meta': meta};
}
4 changes: 2 additions & 2 deletions lib/src/document/relationship.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import 'dart:async';

import 'package:json_api/src/client/client.dart';
import 'package:json_api/src/document/document.dart';
import 'package:json_api/src/document/identifier.dart';
import 'package:json_api/src/document/identifier_object.dart';
import 'package:json_api/src/document/link.dart';
import 'package:json_api/src/document/resource_object.dart';
import 'package:json_api/src/identifier.dart';
import 'package:json_api/src/nullable.dart';

/// A relationship. Can be to-one or to-many.
///
/// https://jsonapi.org/format/#document-resource-object-linkage
abstract class Relationship implements Document {
abstract class Relationship extends Document {
final Link self;
final Link related;

Expand Down
2 changes: 1 addition & 1 deletion lib/src/resource.dart → lib/src/document/resource.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:json_api/src/identifier.dart';
import 'package:json_api/src/document/identifier.dart';

/// The core of the Resource object
/// https://jsonapi.org/format/#document-resource-objects
Expand Down
2 changes: 1 addition & 1 deletion lib/src/document/resource_document.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:json_api/src/document/document.dart';
import 'package:json_api/src/document/link.dart';
import 'package:json_api/src/document/resource_object.dart';

class ResourceDocument implements Document {
class ResourceDocument extends Document {
final ResourceObject resourceObject;
final List<ResourceObject> included;
final Link self;
Expand Down
4 changes: 2 additions & 2 deletions lib/src/document/resource_object.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'package:json_api/src/document/identifier.dart';
import 'package:json_api/src/document/identifier_object.dart';
import 'package:json_api/src/document/link.dart';
import 'package:json_api/src/document/relationship.dart';
import 'package:json_api/src/identifier.dart';
import 'package:json_api/src/document/resource.dart';
import 'package:json_api/src/nullable.dart';
import 'package:json_api/src/resource.dart';

/// Resource object
class ResourceObject {
Expand Down
18 changes: 13 additions & 5 deletions lib/src/server/json_api_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ import 'package:json_api/src/server/response.dart';

/// JSON:API Controller
abstract class JsonApiController {
Future<ServerResponse> fetchCollection(CollectionRequest rq);
Future<ServerResponse> fetchCollection(
String type, JsonApiHttpRequest request);

Future<ServerResponse> fetchResource(ResourceRequest rq);
Future<ServerResponse> fetchResource(
String type, String id, JsonApiHttpRequest request);

Future<ServerResponse> fetchRelationship(RelationshipRequest rq);
Future<ServerResponse> fetchRelationship(
String type, String id, String relationship, JsonApiHttpRequest request);

Future<ServerResponse> fetchRelated(RelatedRequest rq);
Future<ServerResponse> fetchRelated(
String type, String id, String relationship, JsonApiHttpRequest request);

Future<ServerResponse> createResource(CollectionRequest rq);
Future<ServerResponse> createResource(
String type, JsonApiHttpRequest request);

Future<ServerResponse> deleteResource(
String type, String id, JsonApiHttpRequest request);
}
Loading