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
19 changes: 15 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@ All notable changes to this project will be documented in this file.
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]
## [3.2.0] - 2019-12-30
### Added
- `matchBase` option to `PathBasedUrlDesign`.
- `Resource.toIdentifier()`method.

### Changed
- (Server, BC-breaking) `JsonApiController` made generic.

### Removed
- The package does not depend on `collection` anymore.

## [3.1.0] - 2019-12-19
### Added
- (Server) Routing is exposed via `server` library.

### Changed
- (Server) `Controller` renamed to `JsonApiController`.
- (Server) `Response` renamed to `JsonApiResponse`.
- (Server, BC-breaking) `Controller` renamed to `JsonApiController`.
- (Server, BC-breaking) `Response` renamed to `JsonApiResponse`.

### Fixed
- (Server) Response classes had `included` member initialized to `[]` by default. Now the default is `null`.
Expand Down Expand Up @@ -128,7 +138,8 @@ Most of the changes are **BC-BREAKING**.
### Added
- Client: fetch resources, collections, related resources and relationships

[Unreleased]: https://github.com/f3ath/json-api-dart/compare/3.1.0...HEAD
[Unreleased]: https://github.com/f3ath/json-api-dart/compare/3.2.0...HEAD
[3.2.0]: https://github.com/f3ath/json-api-dart/compare/3.1.0...3.2.0
[3.1.0]: https://github.com/f3ath/json-api-dart/compare/3.0.0...3.1.0
[3.0.0]: https://github.com/f3ath/json-api-dart/compare/2.1.0...3.0.0
[2.1.0]: https://github.com/f3ath/json-api-dart/compare/2.0.3...2.1.0
Expand Down
6 changes: 3 additions & 3 deletions example/cars_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ Future<HttpServer> createServer(InternetAddress addr, int port) async {

final httpServer = await HttpServer.bind(addr, port);
final urlDesign = PathBasedUrlDesign(Uri.parse('http://localhost:$port'));
final documentFactory =
ServerDocumentFactory(urlDesign, pagination: pagination);
final jsonApiServer = JsonApiServer(urlDesign, controller, documentFactory);
final jsonApiServer = JsonApiServer(urlDesign, controller,
documentFactory:
ServerDocumentFactory(urlDesign, pagination: pagination));

unawaited(httpServer.forEach(jsonApiServer.serve));
return httpServer;
Expand Down
36 changes: 21 additions & 15 deletions example/cars_server/controller.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import 'dart:async';

import 'package:json_api/document.dart';
import 'package:json_api/query.dart';
import 'package:json_api/server.dart';
import 'package:uuid/uuid.dart';

import 'dao.dart';
import 'job_queue.dart';

class CarsController implements JsonApiController {
class CarsController
implements JsonApiController<JsonApiRequest, JsonApiResponse> {
final Map<String, DAO> _dao;

final PaginationStrategy _pagination;

CarsController(this._dao, this._pagination);

@override
JsonApiResponse fetchCollection(String type, Uri uri) {
final page = Page.fromUri(uri);
JsonApiResponse fetchCollection(String type, JsonApiRequest request) {
final page = Page.fromUri(request.requestedUri);
final dao = _getDaoOrThrow(type);
final collection =
dao.fetchCollection(_pagination.limit(page), _pagination.offset(page));
Expand All @@ -26,9 +28,9 @@ class CarsController implements JsonApiController {

@override
JsonApiResponse fetchRelated(
String type, String id, String relationship, Uri uri) {
String type, String id, String relationship, JsonApiRequest request) {
final res = _fetchResourceOrThrow(type, id);
final page = Page.fromUri(uri);
final page = Page.fromUri(request.requestedUri);
if (res.toOne.containsKey(relationship)) {
final id = res.toOne[relationship];
final resource = _dao[id.type].fetchByIdAsResource(id.id);
Expand All @@ -47,10 +49,11 @@ class CarsController implements JsonApiController {
}

@override
JsonApiResponse fetchResource(String type, String id, Uri uri) {
JsonApiResponse fetchResource(
String type, String id, JsonApiRequest request) {
final dao = _getDaoOrThrow(type);
final obj = dao.fetchById(id);
final include = Include.fromUri(uri);
final include = Include.fromUri(request.requestedUri);

if (obj == null) {
return ErrorResponse.notFound(
Expand All @@ -74,7 +77,7 @@ class CarsController implements JsonApiController {

@override
JsonApiResponse fetchRelationship(
String type, String id, String relationship, Uri uri) {
String type, String id, String relationship, JsonApiRequest request) {
final res = _fetchResourceOrThrow(type, id);

if (res.toOne.containsKey(relationship)) {
Expand All @@ -89,7 +92,8 @@ class CarsController implements JsonApiController {
}

@override
JsonApiResponse deleteResource(String type, String id) {
JsonApiResponse deleteResource(
String type, String id, JsonApiRequest request) {
final dao = _getDaoOrThrow(type);

final res = dao.fetchByIdAsResource(id);
Expand All @@ -105,7 +109,8 @@ class CarsController implements JsonApiController {
}

@override
JsonApiResponse createResource(String type, Resource resource) {
JsonApiResponse createResource(
String type, Resource resource, JsonApiRequest request) {
final dao = _getDaoOrThrow(type);

_throwIfIncompatibleTypes(type, resource);
Expand Down Expand Up @@ -140,7 +145,8 @@ class CarsController implements JsonApiController {
}

@override
JsonApiResponse updateResource(String type, String id, Resource resource) {
JsonApiResponse updateResource(
String type, String id, Resource resource, JsonApiRequest request) {
final dao = _getDaoOrThrow(type);

_throwIfIncompatibleTypes(type, resource);
Expand All @@ -156,8 +162,8 @@ class CarsController implements JsonApiController {
}

@override
JsonApiResponse replaceToOne(
String type, String id, String relationship, Identifier identifier) {
JsonApiResponse replaceToOne(String type, String id, String relationship,
Identifier identifier, JsonApiRequest request) {
final dao = _getDaoOrThrow(type);

dao.replaceToOne(id, relationship, identifier);
Expand All @@ -166,7 +172,7 @@ class CarsController implements JsonApiController {

@override
JsonApiResponse replaceToMany(String type, String id, String relationship,
List<Identifier> identifiers) {
List<Identifier> identifiers, JsonApiRequest request) {
final dao = _getDaoOrThrow(type);

dao.replaceToMany(id, relationship, identifiers);
Expand All @@ -175,7 +181,7 @@ class CarsController implements JsonApiController {

@override
JsonApiResponse addToMany(String type, String id, String relationship,
List<Identifier> identifiers) {
List<Identifier> identifiers, JsonApiRequest request) {
final dao = _getDaoOrThrow(type);

return ToManyResponse(
Expand Down
4 changes: 1 addition & 3 deletions lib/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
/// **The API is not stable!**
library server;

export 'package:json_api/query.dart';
export 'package:json_api/src/server/http_method.dart';
export 'package:json_api/src/server/json_api_controller.dart';
export 'package:json_api/src/server/json_api_request.dart';
export 'package:json_api/src/server/json_api_server.dart';
export 'package:json_api/src/server/pagination/fixed_size_page.dart';
export 'package:json_api/src/server/pagination/pagination_strategy.dart';
Expand All @@ -30,4 +29,3 @@ export 'package:json_api/src/server/response/to_one_response.dart';
export 'package:json_api/src/server/routing/route.dart';
export 'package:json_api/src/server/routing/route_factory.dart';
export 'package:json_api/src/server/server_document_factory.dart';
export 'package:json_api/url_design.dart';
2 changes: 1 addition & 1 deletion lib/src/client/json_api_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class JsonApiClient {
/// You have to create and pass an instance of the [httpClient] yourself.
/// Do not forget to call [httpClient.close()] when you're done using
/// the JSON:API client.
/// The [onHttpCall] hook, if passed, gets called when an http response is
/// The [onHttpCall] hook, if passed, gets called when an http response is
/// received from the HTTP Client.
JsonApiClient(this.httpClient,
{ClientDocumentFactory builder, OnHttpCall onHttpCall})
Expand Down
8 changes: 7 additions & 1 deletion lib/src/document/relationship.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ class ToOne extends Relationship {
/// Converts to [Identifier].
/// For empty relationships returns null.
Identifier unwrap() => linkage?.unwrap();

/// Same as [unwrap()]
Identifier get identifier => unwrap();
}

/// Relationship to-many
Expand Down Expand Up @@ -134,5 +137,8 @@ class ToMany extends Relationship {

/// Converts to List<Identifier>.
/// For empty relationships returns an empty List.
List<Identifier> get identifiers => linkage.map((_) => _.unwrap()).toList();
List<Identifier> unwrap() => linkage.map((_) => _.unwrap()).toList();

/// Same as [unwrap()]
List<Identifier> get identifiers => unwrap();
}
7 changes: 7 additions & 0 deletions lib/src/document/resource.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ class Resource {
ArgumentError.checkNotNull(type, 'type');
}

Identifier toIdentifier() {
if (id == null) {
throw StateError('Can not create an Identifier with id==null');
}
return Identifier(type, id);
}

@override
String toString() => 'Resource(${type}:${id})';
}
5 changes: 5 additions & 0 deletions lib/src/document/resource_collection_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,13 @@ class ResourceCollectionData extends PrimaryData {
/// The link to the prev page. May be null.
Link get prev => (links ?? {})['prev'];

/// Returns a list of resources contained in the collection
List<Resource> unwrap() => collection.map((_) => _.unwrap()).toList();

/// Returns a map of resources indexed by ids
Map<String, Resource> unwrapToMap() =>
Map.fromIterable(unwrap(), key: (r) => r.id);

@override
Map<String, Object> toJson() => {
...super.toJson(),
Expand Down
13 changes: 0 additions & 13 deletions lib/src/server/http_method.dart

This file was deleted.

38 changes: 18 additions & 20 deletions lib/src/server/json_api_controller.dart
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
import 'dart:async';

import 'package:json_api/document.dart';
import 'package:json_api/src/document/identifier.dart';
import 'package:json_api/src/document/resource.dart';
import 'package:json_api/src/server/response/json_api_response.dart';
import 'package:json_api/src/server/json_api_request.dart';

abstract class JsonApiController {
FutureOr<JsonApiResponse> fetchCollection(String type, Uri uri);
abstract class JsonApiController<Request extends JsonApiRequest, Response> {
Response fetchCollection(String type, Request request);

FutureOr<JsonApiResponse> fetchResource(String type, String id, Uri uri);
Response fetchResource(String type, String id, Request request);

FutureOr<JsonApiResponse> fetchRelated(
String type, String id, String relationship, Uri uri);
Response fetchRelated(
String type, String id, String relationship, Request request);

FutureOr<JsonApiResponse> fetchRelationship(
String type, String id, String relationship, Uri uri);
Response fetchRelationship(
String type, String id, String relationship, Request request);

FutureOr<JsonApiResponse> deleteResource(String type, String id);
Response deleteResource(String type, String id, Request request);

FutureOr<JsonApiResponse> createResource(String type, Resource resource);
Response createResource(String type, Resource resource, Request request);

FutureOr<JsonApiResponse> updateResource(
String type, String id, Resource resource);
Response updateResource(
String type, String id, Resource resource, Request request);

FutureOr<JsonApiResponse> replaceToOne(
String type, String id, String relationship, Identifier identifier);
Response replaceToOne(String type, String id, String relationship,
Identifier identifier, Request request);

FutureOr<JsonApiResponse> replaceToMany(String type, String id,
String relationship, List<Identifier> identifiers);
Response replaceToMany(String type, String id, String relationship,
List<Identifier> identifiers, Request request);

FutureOr<JsonApiResponse> addToMany(String type, String id,
String relationship, List<Identifier> identifiers);
Response addToMany(String type, String id, String relationship,
List<Identifier> identifiers, Request request);
}
13 changes: 13 additions & 0 deletions lib/src/server/json_api_request.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// JSON:API HTTP request
class JsonApiRequest {
JsonApiRequest(this.method, this.requestedUri, this.body);

/// Requested URI
final Uri requestedUri;

/// JSON-decoded body, may be null
final Object body;

/// HTTP method
final String method;
}
14 changes: 8 additions & 6 deletions lib/src/server/json_api_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:json_api/src/server/http_method.dart';
import 'package:json_api/src/server/json_api_controller.dart';
import 'package:json_api/src/server/json_api_request.dart';
import 'package:json_api/src/server/response/error_response.dart';
import 'package:json_api/src/server/response/json_api_response.dart';
import 'package:json_api/src/server/routing/route_factory.dart';
Expand All @@ -17,9 +17,10 @@ class JsonApiServer {
final String allowOrigin;
final RouteFactory routeMapper;

JsonApiServer(this.urlDesign, this.controller, this.documentFactory,
{this.allowOrigin = '*'})
: routeMapper = RouteFactory();
JsonApiServer(this.urlDesign, this.controller,
{this.allowOrigin = '*', ServerDocumentFactory documentFactory})
: routeMapper = RouteFactory(),
documentFactory = documentFactory ?? ServerDocumentFactory(urlDesign);

Future serve(HttpRequest request) async {
final response = await _call(controller, request);
Expand All @@ -33,12 +34,13 @@ class JsonApiServer {

Future<JsonApiResponse> _call(
JsonApiController controller, HttpRequest request) async {
final method = HttpMethod(request.method);
final body = await _getBody(request);
final jsonApiRequest =
JsonApiRequest(request.method, request.requestedUri, body);
try {
return await urlDesign
.match(request.requestedUri, routeMapper)
.call(controller, request.requestedUri, method, body);
.call(controller, jsonApiRequest);
} on ErrorResponse catch (error) {
return error;
}
Expand Down
Loading