Skip to content

Commit 9f1f4a2

Browse files
committed
Initial version
1 parent 727aae4 commit 9f1f4a2

File tree

9 files changed

+415
-2
lines changed

9 files changed

+415
-2
lines changed

.travis.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
language: dart
2+
dart:
3+
- stable
4+
dart_task:
5+
- test: --platform vm
6+
- test: --platform chrome --exclude-tags vm-only
7+
- dartfmt: true
8+
- dartanalyzer: --fatal-infos --fatal-warnings lib test

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
# json-api-dart-client
2-
JSON:API client in Dart
1+
# [JSON:API](http://jsonapi.org) v1.0 HTTP Client
2+
3+
This is a work-in-progress. Feel free to join.

dart_test.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
tags:
2+
vm-only: {test_on: "vm"}

lib/json_api.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export 'src/exceptions.dart';
2+
export 'src/json_api_client.dart';
3+
export 'src/response.dart';

lib/src/exceptions.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class JsonApiClientException implements Exception {
2+
final String message;
3+
4+
JsonApiClientException(this.message);
5+
}
6+
7+
/// Thrown when the client receives a response with the
8+
/// Content-Type different from [Document.mediaType]
9+
class InvalidContentTypeException extends JsonApiClientException {
10+
InvalidContentTypeException(String message) : super(message);
11+
}

lib/src/json_api_client.dart

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import 'dart:convert';
2+
3+
import 'package:http/http.dart' as http;
4+
import 'package:json_api/src/response.dart';
5+
import 'package:json_api_document/json_api_document.dart';
6+
7+
/// JSON:API client
8+
///
9+
/// The client is based on top of Dart's [http.Client] class. To use a custom
10+
/// client, provide your own [clientFactory].
11+
class JsonApiClient {
12+
JsonApiClient({
13+
this.baseUrl = '',
14+
ClientFactory clientFactory,
15+
Map<String, String> defaultHeaders = const {},
16+
}) : clientFactory = clientFactory ?? (() => http.Client()),
17+
defaultHeaders = Map.unmodifiable({}
18+
..addAll(defaultHeaders)
19+
..['Accept'] = Document.mediaType);
20+
21+
final String baseUrl;
22+
final ClientFactory clientFactory;
23+
final Map<String, String> defaultHeaders;
24+
final api = Api('1.0');
25+
26+
/// Fetches a [Document] containing resource(s) from the given [url].
27+
/// Pass a [Map] of [headers] to add extra headers to the request.
28+
///
29+
/// More details: https://jsonapi.org/format/#fetching-resources
30+
Future<Response> fetchResource(String url,
31+
{Map<String, String> headers = const {}}) async =>
32+
Response(await _exec((_) => _.get(_url(url), headers: _headers(headers))),
33+
preferResource: true);
34+
35+
/// Fetches a [Document] containing identifier(s) from the given [url].
36+
/// Pass a [Map] of [headers] to add extra headers to the request.
37+
///
38+
/// More details: https://jsonapi.org/format/#fetching-relationships
39+
fetchRelationship(String url,
40+
{Map<String, String> headers = const {}}) async =>
41+
Response(
42+
await _exec((_) => _.get(_url(url), headers: _headers(headers))));
43+
44+
/// Creates a new [resource] sending a POST request to the [url].
45+
/// Pass a [Map] of [headers] to add extra headers to the request.
46+
///
47+
/// More details: https://jsonapi.org/format/#crud-creating
48+
createResource(String url, Resource resource,
49+
{Map<String, String> headers = const {}}) async =>
50+
Response(
51+
await _exec((_) => _.post(_url(url),
52+
body: _body(resource),
53+
headers: _headers(headers, withContentType: true))),
54+
preferResource: true);
55+
56+
/// Deletes the resource sending a DELETE request to the [url].
57+
/// Pass a [Map] of [headers] to add extra headers to the request.
58+
///
59+
/// More details: https://jsonapi.org/format/#crud-deleting
60+
deleteResource(String url, {Map<String, String> headers = const {}}) async =>
61+
Response(
62+
await _exec((_) => _.delete(_url(url), headers: _headers(headers))));
63+
64+
/// Updates the [resource] sending a PATCH request to the [url].
65+
/// Pass a [Map] of [headers] to add extra headers to the request.
66+
///
67+
/// More details: https://jsonapi.org/format/#crud-updating
68+
updateResource(String url, Resource resource,
69+
{Map<String, String> headers = const {}}) async =>
70+
Response(
71+
await _exec((_) => _.patch(_url(url),
72+
body: _body(resource),
73+
headers: _headers(headers, withContentType: true))),
74+
preferResource: true);
75+
76+
String _body(Resource resource) =>
77+
json.encode(DataDocument.fromResource(resource, api: api));
78+
79+
String _url(String url) => '${baseUrl}${url}';
80+
81+
Map<String, String> _headers(Map<String, String> headers,
82+
{bool withContentType = false}) {
83+
final h = <String, String>{}..addAll(defaultHeaders)..addAll(headers);
84+
if (withContentType) h['Content-Type'] = Document.mediaType;
85+
return h;
86+
}
87+
88+
Future<http.Response> _exec(
89+
Future<http.Response> fn(http.Client client)) async {
90+
final client = clientFactory();
91+
try {
92+
return await fn(client);
93+
} finally {
94+
client.close();
95+
}
96+
}
97+
}
98+
99+
typedef http.Client ClientFactory();

lib/src/response.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import 'dart:convert';
2+
3+
import 'package:http/http.dart' as http;
4+
import 'package:json_api/src/exceptions.dart';
5+
import 'package:json_api_document/json_api_document.dart';
6+
7+
/// A result of a fetch() request.
8+
class Response {
9+
final Document document;
10+
final int status;
11+
final Map<String, String> headers;
12+
13+
Response(http.Response r, {bool preferResource = false})
14+
: status = r.statusCode,
15+
headers = r.headers,
16+
document = r.contentLength > 0
17+
? Document.fromJson(json.decode(r.body),
18+
preferResource: preferResource)
19+
: null {
20+
const contentType = 'content-type';
21+
if (headers.containsKey(contentType) &&
22+
headers[contentType].startsWith(Document.mediaType)) return;
23+
24+
throw InvalidContentTypeException(headers[contentType]);
25+
}
26+
27+
String get location => headers['location'];
28+
}

pubspec.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
author: "Alexey Karapetov <karapetov@gmail.com>"
2+
description: "JSON:API v1.0 (http://jsonapi.org) HTTP Client"
3+
homepage: "https://github.com/f3ath/json-api-dart"
4+
name: "json_api"
5+
version: "0.0.1-dev1"
6+
dev_dependencies:
7+
json_api_document: "^0.3.8"
8+
pubspec_version: "^0.1.0"
9+
test: "1.3.0"
10+
environment:
11+
sdk: ">=2.0.0 <3.0.0"

0 commit comments

Comments
 (0)