Skip to content

Release v6 #135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 36 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
139d9ff
Initial Query model
f3ath Sep 9, 2022
6a1e630
Switch to extended map in Queries
f3ath Sep 9, 2022
eeca481
asQueryParameters -> toQuery()
f3ath Sep 9, 2022
dfbfe7b
Use the object instead of Map
f3ath Sep 10, 2022
a989802
Make Request unaware of particular query parameters
f3ath Sep 10, 2022
2da2c64
Change Query usage pattern
f3ath Sep 10, 2022
7d26a58
Ref -> Reference
f3ath Sep 11, 2022
49b766e
Remove Identity
f3ath Sep 11, 2022
16b37f3
Remove ToOne.findIn
f3ath Sep 11, 2022
3c54ccd
Allow repeating identifiers in ToMany
f3ath Sep 11, 2022
4306163
Remove ToMany.findIn
f3ath Sep 11, 2022
8cbc0a6
Remove ResourceCollection
f3ath Sep 11, 2022
459690d
Remove Identifier.identifies()
f3ath Sep 14, 2022
1423a68
Remove Resource.key, refactor Resource(id)
f3ath Sep 14, 2022
4b30d9f
Add Resource.lid
f3ath Sep 15, 2022
2edcf73
Add LocalIdentifier
f3ath Sep 15, 2022
7b9179d
Make Relationship generic
f3ath Sep 15, 2022
5eabf36
Revert "Make Relationship generic"
f3ath Sep 24, 2022
f928459
Remove ResourceProperties
f3ath Sep 24, 2022
928a097
Tests pass for newRel
f3ath Oct 9, 2022
1512be4
ErrorConverter extracted.
f3ath Oct 10, 2022
a436cab
Remove Filter
f3ath Oct 31, 2022
e88e2f2
Release dev-1
f3ath Oct 31, 2022
27c58d7
Do not set empty body. Fixes #129 (#130)
f3ath Dec 30, 2022
cd1c773
Use interop libraries
f3ath Apr 18, 2023
8c544eb
Use http_interop
f3ath Apr 30, 2023
313774d
Handle invalid Media Type
f3ath May 1, 2023
8c987e7
WIP
f3ath Sep 8, 2023
d547256
interop
f3ath Sep 8, 2023
2a13c2f
Merge branch 'master' into v6-dev
f3ath Sep 8, 2023
e38420c
interop
f3ath Sep 8, 2023
aceb01d
v6
f3ath Sep 8, 2023
4cc9257
v6
f3ath Sep 8, 2023
a2d8033
tests
f3ath Sep 8, 2023
991e4e1
tests
f3ath Sep 8, 2023
41c3539
coverage
f3ath Sep 8, 2023
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
4 changes: 1 addition & 3 deletions .github/workflows/dart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@ jobs:
- uses: actions/checkout@v2
- name: Install dependencies
run: dart pub get
- name: Print Dart version
run: dart --version
- name: Format
run: dart format --output none --set-exit-if-changed example lib test
- name: Analyzer
run: dart analyze --fatal-infos --fatal-warnings
- name: Tests
run: dart test --coverage=.coverage -j1
- name: Coverage
run: dart run coverage:format_coverage -l -c -i .coverage --report-on=lib | dart run check_coverage:check_coverage
run: dart run coverage:format_coverage -l -c -i .coverage --report-on=lib | dart run check_coverage:check_coverage 98
18 changes: 14 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ 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).

## [6.0.0] - 2023-09-07
### Added
- Partial support for JSON:API v1.1

### Changed
- A bunch of BC-breaking changes. Please refer to the tests.
- Min SDK version is 3.0.0
- Migrated to `http_interop`. You'll have to install `http_interop_http` or another implementation to get the HTTP client.

### Removed
- Query filter.

## [5.4.0] - 2023-04-30
### Changed
- Switch to http\_interop packages.
Expand All @@ -13,9 +25,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Client MessageConverter class to control HTTP request/response conversion.

### Fixed
- Content-Type being set for GET/OPTIONS requests ([issue](https://github.com/f3ath/json-api-dart/issues/129)).

## [5.2.0] - 2022-06-01
### Added
- Support for included resources in create and update methods. Author: @kszczek
Expand Down Expand Up @@ -229,6 +238,7 @@ the Document model.
### Added
- Client: fetch resources, collections, related resources and relationships

[6.0.0]: https://github.com/f3ath/json-api-dart/compare/5.4.0...6.0.0
[5.4.0]: https://github.com/f3ath/json-api-dart/compare/5.3.0...5.4.0
[5.3.0]: https://github.com/f3ath/json-api-dart/compare/5.2.0...5.3.0
[5.2.0]: https://github.com/f3ath/json-api-dart/compare/5.1.0...5.2.0
Expand Down Expand Up @@ -263,4 +273,4 @@ the Document model.
[0.4.0]: https://github.com/f3ath/json-api-dart/compare/0.3.0...0.4.0
[0.3.0]: https://github.com/f3ath/json-api-dart/compare/0.2.0...0.3.0
[0.2.0]: https://github.com/f3ath/json-api-dart/compare/0.1.0...0.2.0
[0.1.0]: https://github.com/f3ath/json-api-dart/releases/tag/0.1.0
[0.1.0]: https://github.com/f3ath/json-api-dart/releases/tag/0.1.0
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

TL;DR:
```dart
import 'package:http_interop_http/http_interop_http.dart';
import 'package:json_api/client.dart';
import 'package:json_api/routing.dart';

Expand All @@ -14,7 +15,7 @@ void main() async {

/// The [RoutingClient] is most likely the right choice.
/// It has methods covering many standard use cases.
final client = RoutingClient(uriDesign);
final client = RoutingClient(uriDesign, Client(OneOffHandler()));

try {
/// Fetch the collection.
Expand All @@ -31,14 +32,13 @@ void main() async {
});
} on RequestFailure catch (e) {
/// Catch error response
e.errors.forEach((error) => print('${error.title}'));
for (var error in e.errors) {
print(error.title);
}
}
}
```
This is a work-in-progress. You can help it by submitting a PR with a feature or documentation improvements.





[JSON:API]: https://jsonapi.org
45 changes: 35 additions & 10 deletions example/client.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:http/http.dart' as http;
import 'package:http_interop_http/http_interop_http.dart';
import 'package:json_api/client.dart';
import 'package:json_api/routing.dart';

Expand All @@ -8,27 +10,50 @@ void main() async {
/// Use the standard recommended URL structure or implement your own
final uriDesign = StandardUriDesign(Uri.parse(baseUri));

/// This is the Dart's standard HTTP client.
/// Do not forget to close it in the end.
final httpClient = http.Client();

/// This is the adapter which decouples this JSON:API implementation
/// from the HTTP client.
/// Learn more: https://pub.dev/packages/http_interop
final httpHandler = ClientWrapper(httpClient);

/// This is the basic JSON:API client. It is flexible but not very convenient
/// to use, because you would need to remember a lot of JSON:API protocol details.
/// We will use another wrapper on top of it.
final jsonApiClient = Client(httpHandler);

/// The [RoutingClient] is most likely the right choice.
/// It has methods covering many standard use cases.
final client = RoutingClient(uriDesign);
/// It is called routing because it routes the calls to the correct
/// URLs depending on the use case. Take a look at its methods, they cover
/// all the standard scenarios specified by the JSON:API standard.
final client = RoutingClient(uriDesign, jsonApiClient);

try {
/// Fetch the collection.
/// See other methods to query and manipulate resources.
final response = await client.fetchCollection('colors');

final resources = response.collection;
resources.map((resource) => resource.attributes).forEach((attr) {
final name = attr['name'];
final red = attr['red'];
final green = attr['green'];
final blue = attr['blue'];
/// The fetched collection allows us to iterate over the resources
/// and to look into their attributes
for (final resource in response.collection) {
final {
'name': name,
'red': red,
'green': green,
'blue': blue,
} = resource.attributes;
print('${resource.type}:${resource.id}');
print('$name - $red:$green:$blue');
});
}
} on RequestFailure catch (e) {
/// Catch error response
for (var error in e.errors) {
for (final error in e.errors) {
print(error.title);
}
}

/// Free up the resources before exit.
httpClient.close();
}
54 changes: 15 additions & 39 deletions example/server.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:io';

import 'package:http_interop/http_interop.dart' as interop;
import 'package:json_api/document.dart';
import 'package:json_api/http.dart';
import 'package:json_api/routing.dart';
Expand All @@ -10,19 +11,24 @@ import 'server/in_memory_repo.dart';
import 'server/json_api_server.dart';
import 'server/repository.dart';
import 'server/repository_controller.dart';
import 'server/try_catch_handler.dart';

Future<void> main() async {
final host = 'localhost';
final port = 8080;
final resources = ['colors'];
final repo = InMemoryRepo(resources);
await addColors(repo);
final repo = InMemoryRepo(['colors']);
await initRepo(repo);
final controller = RepositoryController(repo, Uuid().v4);
HttpHandler handler = Router(controller, StandardUriDesign.matchTarget);
handler = TryCatchHandler(handler, onError: convertError);
interop.Handler handler =
ControllerRouter(controller, StandardUriDesign.matchTarget);
handler = TryCatchHandler(handler,
onError: ErrorConverter(onError: (e, stack) async {
stderr.writeln(e);
return Response(500,
document: OutboundErrorDocument(
[ErrorObject(title: 'Internal Server Error')]));
}));
handler = LoggingHandler(handler,
onRequest: (r) => print('${r.method.toUpperCase()} ${r.uri}'),
onRequest: (r) => print('${r.method} ${r.uri}'),
onResponse: (r) => print('${r.statusCode}'));
final server = JsonApiServer(handler, host: host, port: port);

Expand All @@ -33,14 +39,10 @@ Future<void> main() async {

await server.start();

print('The server is listening at $host:$port.'
' Try opening the following URL(s) in your browser:');
for (var resource in resources) {
print('http://$host:$port/$resource');
}
print('The server is listening at $host:$port.');
}

Future addColors(Repository repo) async {
Future initRepo(Repository repo) async {
final models = {
{'name': 'Salmon', 'r': 250, 'g': 128, 'b': 114},
{'name': 'Pink', 'r': 255, 'g': 192, 'b': 203},
Expand All @@ -55,29 +57,3 @@ Future addColors(Repository repo) async {
await repo.persist('colors', model);
}
}

Future<HttpResponse> convertError(dynamic error) async {
if (error is MethodNotAllowed) {
return Response.methodNotAllowed();
}
if (error is UnmatchedTarget) {
return Response.badRequest();
}
if (error is CollectionNotFound) {
return Response.notFound(
OutboundErrorDocument([ErrorObject(title: 'CollectionNotFound')]));
}
if (error is ResourceNotFound) {
return Response.notFound(
OutboundErrorDocument([ErrorObject(title: 'ResourceNotFound')]));
}
if (error is RelationshipNotFound) {
return Response.notFound(
OutboundErrorDocument([ErrorObject(title: 'RelationshipNotFound')]));
}
return Response(500,
document: OutboundErrorDocument([
ErrorObject(
title: 'Error: ${error.runtimeType}', detail: error.toString())
]));
}
36 changes: 18 additions & 18 deletions example/server/cors_handler.dart
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
import 'package:http_interop/http_interop.dart';
import 'package:json_api/http.dart';

class CorsHandler implements HttpHandler {
class CorsHandler implements Handler {
CorsHandler(this._inner);

final HttpHandler _inner;
final Handler _inner;

@override
Future<HttpResponse> handle(HttpRequest request) async {
Future<Response> handle(Request request) async {
final headers = {
'Access-Control-Allow-Origin': request.headers['origin'] ?? '*',
'Access-Control-Expose-Headers': 'Location',
'Access-Control-Allow-Origin': [request.headers.last('origin') ?? '*'],
'Access-Control-Expose-Headers': ['Location'],
};

if (request.isOptions) {
if (request.method.equals('OPTIONS')) {
const methods = ['POST', 'GET', 'DELETE', 'PATCH', 'OPTIONS'];
return HttpResponse(204)
..headers.addAll({
...headers,
'Access-Control-Allow-Methods':
// TODO: Make it work for all browsers. Why is toUpperCase() needed?
request.headers['Access-Control-Request-Method']?.toUpperCase() ??
methods.join(', '),
'Access-Control-Allow-Headers':
request.headers['Access-Control-Request-Headers'] ?? '*',
});
return Response(
204,
Body.empty(),
Headers({
...headers,
'Access-Control-Allow-Methods':
request.headers['Access-Control-Request-Method'] ?? methods,
'Access-Control-Allow-Headers':
request.headers['Access-Control-Request-Headers'] ?? ['*'],
}));
}
return await _inner.handle(request)
..headers.addAll(headers);
return await _inner.handle(request..headers.addAll(headers));
}
}
53 changes: 0 additions & 53 deletions example/server/demo_handler.dart

This file was deleted.

Loading