Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
f3ath committed Jul 17, 2024
1 parent fc56c60 commit 47575fc
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 12 deletions.
3 changes: 3 additions & 0 deletions lib/http.dart
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
/// Common HTTP utilities for JSON:API clients and servers.
/// WARNING: This library is in beta stage. The API is subject to change.
library http;
export 'package:json_api/src/http/status_code.dart';
2 changes: 2 additions & 0 deletions lib/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ export 'package:json_api/src/server/cors_middleware.dart';
export 'package:json_api/src/server/error_converter.dart';
export 'package:json_api/src/server/errors/collection_not_found.dart';
export 'package:json_api/src/server/errors/method_not_allowed.dart';
export 'package:json_api/src/server/errors/not_acceptable.dart';
export 'package:json_api/src/server/errors/relationship_not_found.dart';
export 'package:json_api/src/server/errors/resource_not_found.dart';
export 'package:json_api/src/server/errors/unmatched_target.dart';
export 'package:json_api/src/server/errors/unsupported_media_type.dart';
export 'package:json_api/src/server/request_validator.dart';
export 'package:json_api/src/server/response.dart';
export 'package:json_api/src/server/router.dart';
3 changes: 2 additions & 1 deletion lib/src/http/status_code.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ extension type StatusCode(int value) {
static const badRequest = 400;
static const notFound = 404;
static const methodNotAllowed = 405;
static const unacceptable = 406;
static const notAcceptable = 406;
static const unsupportedMediaType = 415;
static const internalServerError = 500;

/// True for the requests processed asynchronously.
/// @see https://jsonapi.org/recommendations/#asynchronous-processing).
Expand Down
10 changes: 4 additions & 6 deletions lib/src/server/error_converter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import 'package:http_interop_middleware/http_interop_middleware.dart';
import 'package:json_api/document.dart';
import 'package:json_api/src/server/errors/collection_not_found.dart';
import 'package:json_api/src/server/errors/method_not_allowed.dart';
import 'package:json_api/src/server/errors/not_acceptable.dart';
import 'package:json_api/src/server/errors/relationship_not_found.dart';
import 'package:json_api/src/server/errors/resource_not_found.dart';
import 'package:json_api/src/server/errors/unacceptable.dart';
import 'package:json_api/src/server/errors/unmatched_target.dart';
import 'package:json_api/src/server/errors/unsupported_media_type.dart';
import 'package:json_api/src/server/response.dart';
Expand All @@ -21,7 +21,6 @@ Middleware errorConverter({
}) =>
middleware(
onError: (error, trace, _) async => switch (error) {
Response() => error,
MethodNotAllowed() =>
await onMethodNotAllowed?.call(error) ?? methodNotAllowed(),
UnmatchedTarget() =>
Expand Down Expand Up @@ -51,9 +50,8 @@ Middleware errorConverter({
)
])),
UnsupportedMediaType() => unsupportedMediaType(),
Unacceptable() => unacceptable(),
NotAcceptable() => notAcceptable(),
_ => await onError?.call(error, trace) ??
response(500,
document: OutboundErrorDocument(
[ErrorObject(title: 'Internal Server Error')]))
internalServerError(OutboundErrorDocument(
[ErrorObject(title: 'Internal Server Error')]))
});
1 change: 1 addition & 0 deletions lib/src/server/errors/not_acceptable.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class NotAcceptable implements Exception {}
1 change: 0 additions & 1 deletion lib/src/server/errors/unacceptable.dart

This file was deleted.

4 changes: 2 additions & 2 deletions lib/src/server/request_validator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:http_interop/http_interop.dart';
import 'package:http_interop_middleware/http_interop_middleware.dart';
import 'package:http_parser/http_parser.dart';
import 'package:json_api/src/media_type.dart';
import 'package:json_api/src/server/errors/unacceptable.dart';
import 'package:json_api/src/server/errors/not_acceptable.dart';
import 'package:json_api/src/server/errors/unsupported_media_type.dart';

final requestValidator = middleware(onRequest: (Request request) async {
Expand All @@ -15,7 +15,7 @@ final requestValidator = middleware(onRequest: (Request request) async {
.map((it) => it.trim())
.map(MediaType.parse)
.any(_isInvalid)) {
throw Unacceptable();
throw NotAcceptable();
}
return null;
});
Expand Down
7 changes: 5 additions & 2 deletions lib/src/server/response.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,8 @@ Response badRequest([OutboundErrorDocument? document]) =>
Response unsupportedMediaType([OutboundErrorDocument? document]) =>
response(StatusCode.unsupportedMediaType, document: document);

Response unacceptable([OutboundErrorDocument? document]) =>
response(StatusCode.unacceptable, document: document);
Response notAcceptable([OutboundErrorDocument? document]) =>
response(StatusCode.notAcceptable, document: document);

Response internalServerError([OutboundErrorDocument? document]) =>
response(StatusCode.internalServerError, document: document);
84 changes: 84 additions & 0 deletions test/unit/server/error_converter_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import 'package:http_interop/http_interop.dart';
import 'package:json_api/http.dart';
import 'package:json_api/server.dart';
import 'package:test/expect.dart';
import 'package:test/scaffolding.dart';

void main() {
final get = Request('get', Uri(), Body(), Headers());

group('default handlers', () {
final converter = errorConverter();
test('can catch MethodNotAllowed', () async {
final r = await converter((_) => throw MethodNotAllowed('foo'))(get);
expect(r.statusCode, equals(StatusCode.methodNotAllowed));
});
test('can catch UnmatchedTarget', () async {
final r = await converter((_) => throw UnmatchedTarget(Uri()))(get);
expect(r.statusCode, equals(StatusCode.badRequest));
});
test('can catch CollectionNotFound', () async {
final r = await converter((_) => throw CollectionNotFound('foo'))(get);
expect(r.statusCode, equals(StatusCode.notFound));
});
test('can catch ResourceNotFound', () async {
final r =
await converter((_) => throw ResourceNotFound('foo', 'bar'))(get);
expect(r.statusCode, equals(StatusCode.notFound));
});
test('can catch RelationshipNotFound', () async {
final r = await converter(
(_) => throw RelationshipNotFound('foo', 'bar', 'baz'))(get);
expect(r.statusCode, equals(StatusCode.notFound));
});
test('can catch UnsupportedMediaType', () async {
final r = await converter((_) => throw UnsupportedMediaType())(get);
expect(r.statusCode, equals(StatusCode.unsupportedMediaType));
});
test('can catch Unacceptable', () async {
final r = await converter((_) => throw NotAcceptable())(get);
expect(r.statusCode, equals(StatusCode.notAcceptable));
});
test('can catch any other error', () async {
final r = await converter((_) => throw 'foo')(get);
expect(r.statusCode, equals(StatusCode.internalServerError));
});
});

group('custom handlers', () {
final converter = errorConverter(
onMethodNotAllowed: (_) async => Response(550, Body(), Headers()),
onUnmatchedTarget: (_) async => Response(551, Body(), Headers()),
onCollectionNotFound: (_) async => Response(552, Body(), Headers()),
onResourceNotFound: (_) async => Response(553, Body(), Headers()),
onRelationshipNotFound: (_) async => Response(554, Body(), Headers()),
onError: (_, __) async => Response(555, Body(), Headers()),
);
test('can catch MethodNotAllowed', () async {
final r = await converter((_) => throw MethodNotAllowed('foo'))(get);
expect(r.statusCode, equals(550));
});
test('can catch UnmatchedTarget', () async {
final r = await converter((_) => throw UnmatchedTarget(Uri()))(get);
expect(r.statusCode, equals(551));
});
test('can catch CollectionNotFound', () async {
final r = await converter((_) => throw CollectionNotFound('foo'))(get);
expect(r.statusCode, equals(552));
});
test('can catch ResourceNotFound', () async {
final r =
await converter((_) => throw ResourceNotFound('foo', 'bar'))(get);
expect(r.statusCode, equals(553));
});
test('can catch RelationshipNotFound', () async {
final r = await converter(
(_) => throw RelationshipNotFound('foo', 'bar', 'baz'))(get);
expect(r.statusCode, equals(554));
});
test('can catch any other error', () async {
final r = await converter((_) => throw 'foo')(get);
expect(r.statusCode, equals(555));
});
});
}

0 comments on commit 47575fc

Please sign in to comment.