Merge branch 'sdk-2.12.x' into master

This commit is contained in:
thomashii 2021-03-13 10:13:56 +08:00
commit 71b55f9548
273 changed files with 13958 additions and 2756 deletions

4
.gitignore vendored
View file

@ -11,8 +11,10 @@
.project
.pub/
.scripts-bin/
.metals/
build/
**/packages/
#**/packages/
packages/hubbub/
# Files created by dart2js
# (Most Dart developers will use pub build to compile Dart, use/modify these

5
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"files.watcherExclude": {
"**/target": true
}
}

56
CHANGELOG.md Normal file
View file

@ -0,0 +1,56 @@
# 3.0.1 (NNBD)
* Changed Dart SDK requirements for all packages to ">=2.12.0 <3.0.0" to support NNBD.
* Updated pretty_logging to 2.0.0
* Updated angel_http_exception to 2.0.0
* Updated angel_cli to 3.0.0. (Rename not working)
# 3.0.0 (Non NNBD)
* Changed Dart SDK requirements for all packages to ">=2.10.0 <3.0.0"
* Updated pretty_logging to 2.0.0
* Updated angel_http_exception to 2.0.0
* Updated angel_cli to 3.0.0. (Rename not working)
* Updated angel_route to 4.0.0
* Updated angel_model to 2.0.0
* Updated angel_container to 2.0.0
* Updated angel_framework to 3.0.0
* Updated angel_auth to 3.0.0
* Updated angel_configuration to 3.0.0
* Updated jael to 3.0.0
* Updated jael_preprocessor to 3.0.0
* Updated validate to 3.0.0
* Added and updated json_god to 3.0.0
* Updated angel_client to 3.0.0
* Updated angel_websocket to 3.0.0 (one issue to be resolved)
* Updated test to 3.0.0
* Updated angel_jael to 3.0.0 (Issue with 2 dependencies)
* Added pub_sub and updated to 3.0.0
* Updated production to 2.0.0
* Updated hot to 3.0.0
* Updated static to 3.0.0
* Update basic-sdk-2.12.x boilerplate
* Updated angel_serialize to 3.0.0
* Updated angel_serialize_generator to 3.0.0
* Updated angel_orm to 3.0.0
* Updated angel_migration to 3.0.0
* Updated angel_orm_generator to 3.0.0 (use a fork of postgres)
* Updated angel_migration_runner to 3.0.0
* Updated angel_orm_test to 1.0.0
* Updated angel_orm_postgres to 2.0.0
* Update orm-sdk-2.12.x boilerplate
* Updated angel_auth_oauth2 to 3.0.0
* Updated angel_auth_cache to 3.0.0
* Updated angel_auth_cors to 3.0.0
* Updated angel_container_generator to 2.0.0
* Updated angel_file_service to 3.0.0
* Updated angel_eventsource to 2.0.0 (use a fork of eventsource)
* Updated angel_auth_twitter to 3.0.0 (use a fork of twitter and oauth)
# 2.2.0
* Changed Dart SDK requirements for all packages to ">=2.10.0 <2.12.0"
* Upgraded 3rd party libraries to the latest version prior to dart 2.12
* Fixed broken code due to 3rd party libraries update
* Revert packages/validate from version 3.0 to version 2.2
# 2.1.x and below
* Refer to the orginal repo before the fork

View file

@ -73,6 +73,12 @@ dart --observe bin/dev.dart
Next, check out the [detailed documentation](https://docs.angel-dart.dev/v/2.x) to learn to flesh out your project.
## Development
* Install development version of Angel CLI
`dart pub global activate --source path ./packages/cli`
`dart pub global activate --source git https://github.com/dukefirehawk/angel/packages/cli`
## Examples and Documentation
Visit the [documentation](https://docs.angel-dart.dev/v/2.x)
for dozens of guides and resources, including video tutorials,

9
TODO.md Normal file
View file

@ -0,0 +1,9 @@
# Todo
### Container/angel_container_generator
* test/reflector_test.reflectab.dart - Changed ImplicitGetterMirrorImpl() from 5 to 3 parameters (revisit later)
* A user forum
* Updated User Guide

View file

@ -1,21 +1,26 @@
name: angel_auth
description: A complete authentication plugin for Angel. Includes support for stateless JWT tokens, Basic Auth, and more.
version: 2.1.5+1
version: 3.0.0
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_auth
publish_to: none
environment:
sdk: ">=2.0.0-dev <3.0.0"
sdk: ">=2.10.0 <3.0.0"
dependencies:
angel_framework: ^2.0.0-rc.6
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/framework
charcode: ^1.0.0
collection: ^1.0.0
crypto: ^2.0.0
http_parser: ^3.0.0
crypto: ^3.0.0
http_parser: ^4.0.0
meta: ^1.0.0
quiver_hashcode: ^2.0.0
dev_dependencies:
http: ^0.12.0
io: ^0.3.2
logging: ^0.11.0
http: ^0.13.0
io: ^1.0.0
logging: ^1.0.0
pedantic: ^1.0.0
test: ^1.0.0
test: ^1.15.7

View file

@ -122,7 +122,7 @@ main() {
});
test('login', () async {
final response = await client.post('$url/login',
final response = await client.post(Uri.parse('$url/login'),
body: {'username': 'jdoe1', 'password': 'password'});
print('Response: ${response.body}');
expect(response.body, equals('Hello!'));
@ -132,7 +132,7 @@ main() {
: null);
test('preserve existing user', () async {
final response = await client.post('$url/existing/foo',
final response = await client.post(Uri.parse('$url/existing/foo'),
body: {'username': 'jdoe1', 'password': 'password'},
headers: {'accept': 'application/json'});
print('Response: ${response.body}');

View file

@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:angel_auth/angel_auth.dart';
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/http.dart';
@ -17,9 +16,10 @@ Map<String, String> sampleUser = {'hello': 'world'};
Future<Map<String, String>> verifier(String username, String password) async {
if (username == 'username' && password == 'password') {
return sampleUser;
} else
} else {
return null;
}
}
Future wireAuth(Angel app) async {
auth.serializer = (user) async => 1337;
@ -29,7 +29,7 @@ Future wireAuth(Angel app) async {
await app.configure(auth.configureServer);
}
main() async {
void main() async {
Angel app;
AngelHttp angelHttp;
http.Client client;
@ -45,10 +45,10 @@ main() async {
middleware: [auth.authenticate('local')]);
app.post('/login', (req, res) => 'This should not be shown',
middleware: [auth.authenticate('local', localOpts)]);
app.get('/success', (req, res) => "yep", middleware: [
app.get('/success', (req, res) => 'yep', middleware: [
requireAuthentication<Map<String, String>>(),
]);
app.get('/failure', (req, res) => "nope");
app.get('/failure', (req, res) => 'nope');
app.logger = Logger('angel_auth')
..onRecord.listen((rec) {
@ -58,10 +58,10 @@ main() async {
}
});
HttpServer server = await angelHttp.startServer('127.0.0.1', 0);
url = "http://${server.address.host}:${server.port}";
var server = await angelHttp.startServer('127.0.0.1', 0);
url = 'http://${server.address.host}:${server.port}';
basicAuthUrl =
"http://username:password@${server.address.host}:${server.port}";
'http://username:password@${server.address.host}:${server.port}';
});
tearDown(() async {
@ -72,15 +72,15 @@ main() async {
});
test('can use "auth" as middleware', () async {
var response = await client
.get("$url/success", headers: {'Accept': 'application/json'});
var response = await client.get(Uri.parse('$url/success'),
headers: {'Accept': 'application/json'});
print(response.body);
expect(response.statusCode, equals(403));
});
test('successRedirect', () async {
Map postData = {'username': 'username', 'password': 'password'};
var response = await client.post("$url/login",
var postData = {'username': 'username', 'password': 'password'};
var response = await client.post(Uri.parse('$url/login'),
body: json.encode(postData),
headers: {'content-type': 'application/json'});
expect(response.statusCode, equals(302));
@ -88,24 +88,24 @@ main() async {
});
test('failureRedirect', () async {
Map postData = {'username': 'password', 'password': 'username'};
var response = await client.post("$url/login",
var postData = {'username': 'password', 'password': 'username'};
var response = await client.post(Uri.parse('$url/login'),
body: json.encode(postData),
headers: {'content-type': 'application/json'});
print("Login response: ${response.body}");
print('Login response: ${response.body}');
expect(response.headers['location'], equals('/failure'));
expect(response.statusCode, equals(401));
});
test('allow basic', () async {
String authString = base64.encode("username:password".runes.toList());
var response = await client
.get("$url/hello", headers: {'authorization': 'Basic $authString'});
var authString = base64.encode('username:password'.runes.toList());
var response = await client.get(Uri.parse('$url/hello'),
headers: {'authorization': 'Basic $authString'});
expect(response.body, equals('"Woo auth"'));
});
test('allow basic via URL encoding', () async {
var response = await client.get("$basicAuthUrl/hello");
var response = await client.get(Uri.parse('$basicAuthUrl/hello'));
expect(response.body, equals('"Woo auth"'));
});
@ -113,7 +113,7 @@ main() async {
auth.strategies.clear();
auth.strategies['local'] =
LocalAuthStrategy(verifier, forceBasic: true, realm: 'test');
var response = await client.get("$url/hello", headers: {
var response = await client.get(Uri.parse('$url/hello'), headers: {
'accept': 'application/json',
'content-type': 'application/json'
});

View file

@ -20,17 +20,18 @@ var options = ExternalAuthOptions(
/// Github doesn't properly follow the OAuth2 spec, so here's logic to parse their response.
Map<String, dynamic> parseParamsFromGithub(MediaType contentType, String body) {
if (contentType.type == 'application') {
if (contentType.subtype == 'x-www-form-urlencoded')
if (contentType.subtype == 'x-www-form-urlencoded') {
return Uri.splitQueryString(body);
else if (contentType.subtype == 'json')
} else if (contentType.subtype == 'json') {
return (json.decode(body) as Map).cast<String, String>();
}
}
throw FormatException(
'Invalid content-type $contentType; expected application/x-www-form-urlencoded or application/json.');
}
main() async {
void main() async {
// Create the server instance.
var app = Angel();
var http = AngelHttp(app);
@ -60,7 +61,7 @@ main() async {
// This function is called when the user ACCEPTS the request to sign in with Github.
(client, req, res) async {
var response = await client.get('https://api.github.com/user');
var response = await client.get(Uri.parse('https://api.github.com/user'));
var ghUser = json.decode(response.body);
var id = ghUser['id'] as int;

View file

@ -1,15 +1,24 @@
name: angel_auth_oauth2
description: angel_auth strategy for OAuth2 login, i.e. Facebook, Github, etc.
version: 2.1.0
author: Tobe O <thosakwe@gmail.com>
version: 3.0.0
#author: Tobe O <thosakwe@gmail.com>
publish_to: none
environment:
sdk: ">=2.0.0-dev <3.0.0"
sdk: ">=2.10.0 <3.0.0"
homepage: https://github.com/angel-dart/auth_oauth2.git
dependencies:
angel_auth: ^2.0.0
angel_framework: ^2.0.0-alpha
http_parser: ^3.0.0
oauth2: ^1.0.0
angel_auth:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/auth
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/framework
http_parser: ^4.0.0
oauth2: ^2.0.0
dev_dependencies:
logging: ^0.11.0
logging: ^1.0.0
pedantic: ^1.0.0

View file

@ -1,20 +1,28 @@
author: "Tobe O <thosakwe@gmail.com>"
name: "angel_auth_twitter"
#author: "Tobe O <thosakwe@gmail.com>"
description: "package:angel_auth strategy for Twitter login. Auto-signs requests."
environment:
sdk: ">=2.0.0 <3.0.0"
sdk: ">=2.10.0 <3.0.0"
homepage: "https://github.com/angel-dart/auth_twitter.git"
name: "angel_auth_twitter"
version: 2.0.0
version: 3.0.0
publish_to: none
dependencies:
angel_auth: ^2.0.0
angel_framework: ^2.0.0-alpha
http: ">=0.11.0 <0.13.0"
angel_auth:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/auth
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/framework
http: ^0.13.0
path: ^1.0.0
# oauth:
# git:
# url: git://github.com/sh4869/oauth.dart.git
# ref: develop
twitter: ^1.0.0
twitter:
git:
url: https://github.com/dukefirehawk/twitter.dart.git
ref: sdk-2.12.x
dev_dependencies:
logging: ^0.11.0
pedantic: ^1.0.0
logging: ^1.0.0
pedantic: ^1.11.0

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'package:http_parser/http_parser.dart';
import 'package:body_parser/body_parser.dart';
main() async {
@ -23,12 +24,20 @@ main() async {
void start(List args) {
var address = new InternetAddress(args[0] as String);
int port = args[1], id = args[2];
int port = 8080;
if (args[1] is int) {
args[1];
}
int id = 0;
if (args[2] is int) {
args[2];
}
HttpServer.bind(address, port, shared: true).then((server) {
server.listen((request) async {
// ignore: deprecated_member_use
var body = await parseBody(request);
var body = await defaultParseBody(request);
request.response
..headers.contentType = new ContentType('application', 'json')
..write(json.encode(body.body))
@ -39,3 +48,14 @@ void start(List args) {
'Server #$id listening at http://${server.address.address}:${server.port}');
});
}
Future<BodyParseResult> defaultParseBody(HttpRequest request,
{bool storeOriginalBuffer: false}) {
return parseBodyFromStream(
request,
request.headers.contentType != null
? new MediaType.parse(request.headers.contentType.toString())
: null,
request.uri,
storeOriginalBuffer: storeOriginalBuffer);
}

View file

@ -4,7 +4,7 @@ version: 1.1.1
description: Parse request bodies and query strings in Dart. Supports JSON, URL-encoded, and multi-part bodies.
homepage: https://github.com/angel-dart/body_parser
environment:
sdk: ">=1.8.0 <3.0.0"
sdk: ">=2.10.0 <2.12.0"
dependencies:
dart2_constant: ^1.0.0
http_parser: ">=3.1.1 <4.0.0"
@ -12,4 +12,4 @@ dependencies:
mime: ">=0.9.3 <1.0.0"
dev_dependencies:
http: ">=0.11.3 <0.12.0"
test: ">=0.12.15"
test: ^1.15.7

View file

@ -1,11 +1,12 @@
import 'dart:io';
import 'dart:convert';
import 'package:body_parser/body_parser.dart';
import 'package:dart2_constant/convert.dart';
import 'package:http/http.dart' as http;
import 'package:test/test.dart';
import 'server_test.dart';
main() {
void main() {
HttpServer server;
String url;
http.Client client;
@ -20,7 +21,7 @@ main() {
});
url = 'http://localhost:${server.port}';
print('Test server listening on $url');
client = new http.Client();
client = http.Client();
});
tearDown(() async {
@ -49,7 +50,7 @@ world
'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}');
var response = await client.post(url, headers: headers, body: postData);
print('Response: ${response.body}');
Map jsons = json.decode(response.body);
var jsons = json.decode(response.body);
var files = jsons['files'].map((map) {
return map == null
? null
@ -63,7 +64,7 @@ world
test('Single upload', () async {
String boundary = 'myBoundary';
Map<String, String> headers = {
'content-type': new ContentType("multipart", "form-data",
'content-type': ContentType("multipart", "form-data",
parameters: {"boundary": boundary}).toString()
};
String postData = '''
@ -84,7 +85,7 @@ Hello world
'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}');
var response = await client.post(url, headers: headers, body: postData);
print('Response: ${response.body}');
Map jsons = json.decode(response.body);
var jsons = json.decode(response.body);
var files = jsons['files'];
expect(files.length, equals(1));
expect(files[0]['name'], equals('file'));
@ -128,7 +129,7 @@ function main() {
'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}');
var response = await client.post(url, headers: headers, body: postData);
print('Response: ${response.body}');
Map jsons = json.decode(response.body);
var jsons = json.decode(response.body);
var files = jsons['files'];
expect(files.length, equals(2));
expect(files[0]['name'], equals('file'));

View file

@ -26,7 +26,7 @@ String jsonEncodeBody(BodyParseResult result) {
});
}
main() {
void main() {
HttpServer server;
String url;
http.Client client;
@ -42,7 +42,7 @@ main() {
});
url = 'http://localhost:${server.port}';
print('Test server listening on $url');
client = new http.Client();
client = http.Client();
});
tearDown(() async {
await server.close(force: true);

View file

@ -1,14 +1,15 @@
import 'package:angel_cache/angel_cache.dart';
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/http.dart';
main() async {
var app = new Angel();
var app = Angel();
app.use(
'/api/todos',
new CacheService(
cache: new MapService(),
database: new AnonymousService(index: ([params]) {
CacheService(
cache: MapService(),
database: AnonymousService(index: ([params]) {
print(
'Fetched directly from the underlying service at ${new DateTime.now()}!');
return ['foo', 'bar', 'baz'];
@ -18,7 +19,7 @@ main() async {
),
);
var http = new AngelHttp(app);
var http = AngelHttp(app);
var server = await http.startServer('127.0.0.1', 3000);
print('Listening at http://${server.address.address}:${server.port}');
}

View file

@ -1,26 +1,27 @@
import 'package:angel_cache/angel_cache.dart';
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/http.dart';
import 'package:glob/glob.dart';
main() async {
var app = new Angel();
var app = Angel();
// Cache a glob
var cache = new ResponseCache()
var cache = ResponseCache()
..patterns.addAll([
new Glob('/*.txt'),
Glob('/*.txt'),
]);
// Handle `if-modified-since` header, and also send cached content
app.fallback(cache.handleRequest);
// A simple handler that returns a different result every time.
app.get('/date.txt',
(req, res) => res.write(new DateTime.now().toIso8601String()));
app.get(
'/date.txt', (req, res) => res.write(DateTime.now().toIso8601String()));
// Support purging the cache.
app.addRoute('PURGE', '*', (req, res) {
if (req.ip != '127.0.0.1') throw new AngelHttpException.forbidden();
if (req.ip != '127.0.0.1') throw AngelHttpException.forbidden();
cache.purge(req.uri.path);
print('Purged ${req.uri.path}');
@ -29,7 +30,7 @@ main() async {
// The response finalizer that actually saves the content
app.responseFinalizers.add(cache.responseFinalizer);
var http = new AngelHttp(app);
var http = AngelHttp(app);
var server = await http.startServer('127.0.0.1', 3000);
print('Listening at http://${server.address.address}:${server.port}');
}

View file

@ -12,11 +12,13 @@ RequestHandler cacheSerializationResults(
shouldCache}) {
return (RequestContext req, ResponseContext res) async {
var oldSerializer = res.serializer;
var cache = <dynamic, String>{};
// TODO: Commented out as it is not doing anything useful
//var cache = <dynamic, String>{};
res.serializer = (value) {
if (shouldCache == null) {
return cache.putIfAbsent(value, () => oldSerializer(value));
}
//if (shouldCache == null) {
// return cache.putIfAbsent(value, () => oldSerializer(value));
//}
return oldSerializer(value);
};

View file

@ -1,17 +1,26 @@
name: angel_cache
version: 2.0.1
version: 3.0.0
homepage: https://github.com/angel-dart/cache
description: Support for server-side caching in Angel.
author: Tobe O <thosakwe@gmail.com>
publish_to: none
environment:
sdk: ">=2.0.0-dev <3.0.0"
sdk: ">=2.10.0 <3.0.0"
dependencies:
angel_framework: ^2.0.0-alpha
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/framework
collection: ^1.0.0
meta: ^1.0.0
pool: ^1.0.0
dev_dependencies:
angel_test: ^2.0.0-alpha
glob: ^1.0.0
http: any
test: ^1.0.0
angel_test:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/test
glob: ^2.0.0
http: ^0.13.0
test: ^1.16.5

View file

@ -42,8 +42,8 @@ main() async {
};
client = await connectTo(app);
response1 = await client.get('/date.txt');
response2 = await client.get('/date.txt');
response1 = await client.get(Uri.parse('/date.txt'));
response2 = await client.get(Uri.parse('/date.txt'));
print(response2.headers);
lastModified = HttpDate.parse(response2.headers['last-modified']);
print('Response 1 status: ${response1.statusCode}');
@ -76,7 +76,7 @@ main() async {
test('invalidate', () async {
await client.sendUnstreamed('PURGE', '/date.txt', {});
var response = await client.get('/date.txt');
var response = await client.get(Uri.parse('/date.txt'));
print('Response after invalidation: ${response.body}');
expect(response.body, isNot(response1.body));
});
@ -86,14 +86,14 @@ main() async {
'if-modified-since':
HttpDate.format(lastModified.add(const Duration(days: 1)))
};
var response = await client.get('/date.txt', headers: headers);
var response = await client.get(Uri.parse('/date.txt'), headers: headers);
print('Sending headers: $headers');
print('Response (${response.statusCode}): ${response.headers}');
expect(response.statusCode, 304);
});
test('last-modified in the past', () async {
var response = await client.get('/date.txt', headers: {
var response = await client.get(Uri.parse('/date.txt'), headers: {
'if-modified-since':
HttpDate.format(lastModified.subtract(const Duration(days: 10)))
});

View file

@ -9,12 +9,16 @@ Includes functionality such as:
* Renaming projects
* Much more...
To install:
* To install:
```bash
$ pub global activate angel_cli
```
* Install development version
`dart pub global activate --source path ./packages/cli`
`dart pub global activate --source git https://github.com/dukefirehawk/angel/packages/cli`
And then, for information on each command:
```bash

View file

@ -11,7 +11,7 @@ class DeployCommand extends Command {
'Generates scaffolding + helper functionality for deploying servers. Run this in your project root.';
DeployCommand() {
addSubcommand(new NginxCommand());
addSubcommand(new SystemdCommand());
addSubcommand(NginxCommand());
addSubcommand(SystemdCommand());
}
}

View file

@ -26,7 +26,7 @@ class DoctorCommand extends Command {
print(green.wrap(
"$checkmark Git executable found: v${version.replaceAll('git version', '').trim()}"));
} else
throw new Exception("Git executable exit code not 0");
throw Exception("Git executable exit code not 0");
} catch (exc) {
print(red.wrap("$ballot Git executable not found"));
}

View file

@ -12,7 +12,7 @@ import 'pub.dart';
import 'rename.dart';
class InitCommand extends Command {
final KeyCommand _key = new KeyCommand();
final KeyCommand _key = KeyCommand();
@override
String get name => "init";
@ -35,21 +35,19 @@ class InitCommand extends Command {
@override
run() async {
Directory projectDir =
new Directory(argResults.rest.isEmpty ? "." : argResults.rest[0]);
Directory(argResults.rest.isEmpty ? "." : argResults.rest[0]);
print("Creating new Angel project in ${projectDir.absolute.path}...");
await _cloneRepo(projectDir);
// await preBuild(projectDir);
var secret = rs.randomAlphaNumeric(32);
print('Generated new development JWT secret: $secret');
await _key.changeSecret(
new File.fromUri(projectDir.uri.resolve('config/default.yaml')),
secret);
File.fromUri(projectDir.uri.resolve('config/default.yaml')), secret);
secret = rs.randomAlphaNumeric(32);
print('Generated new production JWT secret: $secret');
await _key.changeSecret(
new File.fromUri(projectDir.uri.resolve('config/production.yaml')),
secret);
File.fromUri(projectDir.uri.resolve('config/production.yaml')), secret);
var name = argResults.wasParsed('project-name')
? argResults['project-name'] as String
@ -110,9 +108,9 @@ class InitCommand extends Command {
switch (stat.type) {
case FileSystemEntityType.directory:
return await _deleteRecursive(new Directory(path));
return await _deleteRecursive(Directory(path));
case FileSystemEntityType.file:
return await _deleteRecursive(new File(path));
return await _deleteRecursive(File(path));
default:
break;
}
@ -198,7 +196,7 @@ class InitCommand extends Command {
}
if (await git.exitCode != 0) {
throw new Exception("Could not clone repo.");
throw Exception("Could not clone repo.");
}
}
@ -224,7 +222,7 @@ class InitCommand extends Command {
await preBuild(projectDir).catchError((_) => null);
}
var gitDir = new Directory.fromUri(projectDir.uri.resolve(".git"));
var gitDir = Directory.fromUri(projectDir.uri.resolve(".git"));
if (await gitDir.exists()) await gitDir.delete(recursive: true);
} catch (e) {
await boilerplateDir.delete(recursive: true).catchError((_) => null);
@ -260,44 +258,48 @@ Future preBuild(Directory projectDir) async {
var buildCode = await build.exitCode;
if (buildCode != 0) throw new Exception('Failed to pre-build resources.');
if (buildCode != 0) throw Exception('Failed to pre-build resources.');
}
const RepoArchiveLocation = "https://github.com/angel-dart";
const RepoLocation = "https://github.com/dukefirehawk";
const BoilerplateInfo graphQLBoilerplate = const BoilerplateInfo(
'GraphQL',
"A starting point for GraphQL API servers.",
'https://github.com/angel-dart/angel.git',
ref: 'graphql',
'${RepoLocation}/boilerplates.git',
ref: 'graphql-sdk-2.12.x',
);
const BoilerplateInfo ormBoilerplate = const BoilerplateInfo(
'ORM',
"A starting point for applications that use Angel's ORM.",
'https://github.com/angel-dart/angel.git',
ref: 'orm',
'${RepoLocation}/boilerplates.git',
ref: 'orm-sdk-2.12.x',
);
const BoilerplateInfo basicBoilerplate = const BoilerplateInfo(
'Basic',
'Minimal starting point for Angel 2.x - A simple server with only a few additional packages.',
'https://github.com/angel-dart/angel.git');
'${RepoLocation}/boilerplates.git',
ref: 'basic-sdk-2.12.x');
const BoilerplateInfo legacyBoilerplate = const BoilerplateInfo(
'Legacy',
'Minimal starting point for applications running Angel 1.1.x.',
'https://github.com/angel-dart/angel.git',
'${RepoArchiveLocation}/angel.git',
ref: '1.1.x',
);
const BoilerplateInfo sharedBoilerplate = const BoilerplateInfo(
'Shared',
'Holds common models and files shared across multiple Dart projects.',
'https://github.com/angel-dart/boilerplate_shared.git');
'${RepoLocation}/boilerplate_shared.git');
const BoilerplateInfo sharedOrmBoilerplate = const BoilerplateInfo(
'Shared (ORM)',
'Holds common models and files shared across multiple Dart projects.',
'https://github.com/angel-dart/boilerplate_shared.git',
'${RepoLocation}/boilerplate_shared.git',
ref: 'orm',
);
@ -306,8 +308,8 @@ const List<BoilerplateInfo> boilerplates = const [
//legacyBoilerplate,
ormBoilerplate,
graphQLBoilerplate,
sharedBoilerplate,
sharedOrmBoilerplate,
//sharedBoilerplate,
//sharedOrmBoilerplate,
];
class BoilerplateInfo {

View file

@ -3,7 +3,7 @@ import 'dart:io';
import 'package:args/command_runner.dart';
import 'package:glob/glob.dart';
import 'package:io/ansi.dart';
import 'package:mustache4dart/mustache4dart.dart' as mustache;
import 'package:mustache4dart2/mustache4dart2.dart' as mustache;
import 'package:path/path.dart' as p;
import 'package:prompts/prompts.dart' as prompts;
import 'package:pubspec_parse/pubspec_parse.dart';
@ -14,7 +14,7 @@ import 'make/maker.dart';
class InstallCommand extends Command {
static const String repo = 'https://github.com/angel-dart/install.git';
static final Directory installRepo =
new Directory.fromUri(homeDir.uri.resolve('./.angel/addons'));
Directory.fromUri(homeDir.uri.resolve('./.angel/addons'));
@override
String get name => 'install';
@ -72,7 +72,7 @@ class InstallCommand extends Command {
for (var packageName in argResults.rest) {
var packageDir =
new Directory.fromUri(installRepo.uri.resolve(packageName));
Directory.fromUri(installRepo.uri.resolve(packageName));
if (!await packageDir.exists())
throw 'No add-on named "$packageName" is installed. You might need to run `angel install --update`.';
@ -90,7 +90,7 @@ class InstallCommand extends Command {
.map((k) {
var dep = projectPubspec.dependencies[k];
if (dep is HostedDependency)
return new MakerDependency(k, dep.version.toString());
return MakerDependency(k, dep.version.toString());
return null;
})
.where((d) => d != null)
@ -99,14 +99,13 @@ class InstallCommand extends Command {
deps.addAll(projectPubspec.devDependencies.keys.map((k) {
var dep = projectPubspec.devDependencies[k];
if (dep is HostedDependency)
return new MakerDependency(k, dep.version.toString(), dev: true);
return MakerDependency(k, dep.version.toString(), dev: true);
return null;
}).where((d) => d != null));
await depend(deps);
var promptFile =
new File.fromUri(packageDir.uri.resolve('angel_cli.yaml'));
var promptFile = File.fromUri(packageDir.uri.resolve('angel_cli.yaml'));
if (await promptFile.exists()) {
var contents = await promptFile.readAsString();
@ -116,7 +115,7 @@ class InstallCommand extends Command {
// Loads globs
if (cfg['templates'] is List) {
globs.addAll(
(cfg['templates'] as List).map((p) => new Glob(p.toString())));
(cfg['templates'] as List).map((p) => Glob(p.toString())));
}
if (cfg['values'] is Map) {
@ -144,14 +143,14 @@ class InstallCommand extends Command {
await for (var entity in src.list()) {
if (entity is Directory) {
var name = p.basename(entity.path);
var newDir = new Directory.fromUri(dst.uri.resolve(name));
var newDir = Directory.fromUri(dst.uri.resolve(name));
await merge(
entity, newDir, prefix.isEmpty ? name : '$prefix/$name');
} else if (entity is File &&
!entity.path.endsWith('angel_cli.yaml')) {
var name = p.basename(entity.path);
var target = dst.uri.resolve(name);
var targetFile = new File.fromUri(target);
var targetFile = File.fromUri(target);
bool allClear = !await targetFile.exists();
if (!allClear) {
@ -187,7 +186,7 @@ class InstallCommand extends Command {
}
}
await merge(new Directory.fromUri(packageDir.uri.resolve('files')),
await merge(Directory.fromUri(packageDir.uri.resolve('files')),
Directory.current, '');
print('Successfully installed $packageName@${projectPubspec.version}.');
}

View file

@ -13,17 +13,17 @@ class KeyCommand extends Command {
run() async {
var secret = rs.randomAlphaNumeric(32);
print('Generated new development JWT secret: $secret');
await changeSecret(new File('config/default.yaml'), secret);
await changeSecret(File('config/default.yaml'), secret);
secret = rs.randomAlphaNumeric(32);
print('Generated new production JWT secret: $secret');
await changeSecret(new File('config/production.yaml'), secret);
await changeSecret(File('config/production.yaml'), secret);
}
changeSecret(File file, String secret) async {
if (await file.exists()) {
var contents = await file.readAsString();
contents = contents.replaceAll(new RegExp(r'jwt_secret:[^\n]+\n?'), '');
contents = contents.replaceAll(RegExp(r'jwt_secret:[^\n]+\n?'), '');
await file.writeAsString(contents.trim() + '\njwt_secret: "$secret"');
}
}

View file

@ -15,11 +15,11 @@ class MakeCommand extends Command {
'Generates common code for your project, such as projects and controllers.';
MakeCommand() {
addSubcommand(new ControllerCommand());
addSubcommand(new MigrationCommand());
addSubcommand(new ModelCommand());
addSubcommand(new PluginCommand());
addSubcommand(new TestCommand());
addSubcommand(new ServiceCommand());
addSubcommand(ControllerCommand());
addSubcommand(MigrationCommand());
addSubcommand(ModelCommand());
addSubcommand(PluginCommand());
addSubcommand(TestCommand());
addSubcommand(ServiceCommand());
}
}

View file

@ -1,9 +1,9 @@
import 'dart:io';
final RegExp _leadingSlashes = new RegExp(r'^/+');
final RegExp _leadingSlashes = RegExp(r'^/+');
String resolvePub() {
var exec = new File(Platform.resolvedExecutable);
var exec = File(Platform.resolvedExecutable);
var pubPath = exec.parent.uri.resolve('pub').path;
if (Platform.isWindows)
pubPath = pubPath.replaceAll(_leadingSlashes, '') + '.bat';

View file

@ -1,8 +1,10 @@
import 'dart:io';
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:args/command_runner.dart';
import 'package:dart_style/dart_style.dart';
import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart';
import 'package:io/ansi.dart';
import 'package:prompts/prompts.dart' as prompts;
import 'package:recase/recase.dart';
@ -14,7 +16,7 @@ class RenameCommand extends Command {
String get name => 'rename';
@override
String get description => 'Renames the current project.';
String get description => 'Renames the current project (To be available).';
@override
String get invocation => '$name <new name>';
@ -29,17 +31,20 @@ class RenameCommand extends Command {
newName = prompts.get('Rename project to');
}
newName = new ReCase(newName).snakeCase;
newName = ReCase(newName).snakeCase;
var choice = prompts.getBool('Rename the project to `$newName`?');
// TODO: To be available once the issue is fixed
if (choice) {
print('Rename the project is currently not available');
/*
print('Renaming project to `$newName`...');
var pubspecFile =
new File.fromUri(Directory.current.uri.resolve('pubspec.yaml'));
File.fromUri(Directory.current.uri.resolve('pubspec.yaml'));
if (!await pubspecFile.exists()) {
throw new Exception('No pubspec.yaml found in current directory.');
throw Exception('No pubspec.yaml found in current directory.');
} else {
var pubspec = await loadPubspec();
var oldName = pubspec.name;
@ -53,6 +58,7 @@ class RenameCommand extends Command {
stderr.addStream(pub.stderr);
await pub.exitCode;
}
*/
}
}
}
@ -61,12 +67,12 @@ renamePubspec(Directory dir, String oldName, String newName) async {
// var pubspec = await loadPubspec(dir);
print(cyan.wrap('Renaming your project to `$newName.`'));
var pubspecFile = new File.fromUri(dir.uri.resolve('pubspec.yaml'));
var pubspecFile = File.fromUri(dir.uri.resolve('pubspec.yaml'));
if (await pubspecFile.exists()) {
var contents = await pubspecFile.readAsString(), oldContents = contents;
contents =
contents.replaceAll(new RegExp('name:\\s*$oldName'), 'name: $newName');
contents.replaceAll(RegExp('name:\\s*$oldName'), 'name: $newName');
if (contents != oldContents) {
await pubspecFile.writeAsString(contents);
@ -76,7 +82,7 @@ renamePubspec(Directory dir, String oldName, String newName) async {
// print(cyan
// .wrap('Note that this does not actually modify your `pubspec.yaml`.'));
// TODO: https://github.com/dart-lang/pubspec_parse/issues/17
// var newPubspec = new Pubspec.fromJson(pubspec.toJson()..['name'] = newName);
// var newPubspec = Pubspec.fromJson(pubspec.toJson()..['name'] = newName);
// await newPubspec.save(dir);
}
@ -84,34 +90,39 @@ renameDartFiles(Directory dir, String oldName, String newName) async {
if (!await dir.exists()) return;
// Try to replace MongoDB URL
var configGlob = new Glob('config/**/*.yaml');
var configGlob = Glob('config/**/*.yaml');
try {
await for (var yamlFile in configGlob.list(root: dir.absolute.path)) {
if (yamlFile is File) {
print(
'Replacing occurrences of "$oldName" with "$newName" in file "${yamlFile.absolute.path}"...');
var contents = await yamlFile.readAsString();
if (yamlFile is File) {
var contents = (yamlFile as File).readAsStringSync();
contents = contents.replaceAll(oldName, newName);
await yamlFile.writeAsString(contents);
(yamlFile as File).writeAsStringSync(contents);
}
}
}
} catch (_) {}
var entry = new File.fromUri(dir.uri.resolve('lib/$oldName.dart'));
var entry = File.fromUri(dir.uri.resolve('lib/$oldName.dart'));
if (await entry.exists()) {
await entry.rename(dir.uri.resolve('lib/$newName.dart').toFilePath());
print('Renaming library file `${entry.absolute.path}`...');
}
var fmt = new DartFormatter();
var fmt = DartFormatter();
await for (FileSystemEntity file in dir.list(recursive: true)) {
if (file is File && file.path.endsWith('.dart')) {
var contents = await file.readAsString();
var ast = parseCompilationUnit(contents);
var visitor = new RenamingVisitor(oldName, newName)
..visitCompilationUnit(ast);
// TODO: Issue to be fixed: parseCompilationUnit uses Hubbub library which uses discontinued Google front_end library
// front_end package. Temporarily commeted out
//var ast = parseCompilationUnit(contents);
var visitor = RenamingVisitor(oldName, newName);
// ..visitCompilationUnit(ast);
if (visitor.replace.isNotEmpty) {
visitor.replace.forEach((range, replacement) {

View file

@ -12,7 +12,7 @@ class CustomServiceGenerator extends ServiceGenerator {
@override
void applyToLibrary(LibraryBuilder library, String name, String lower) {
library.body.add(new Class((clazz) {
library.body.add(Class((clazz) {
clazz
..name = '${name}Service'
..extend = refer('Service');

View file

@ -34,7 +34,7 @@ class RethinkServiceGenerator extends ServiceGenerator {
void applyToLibrary(LibraryBuilder library, String name, String lower) {
library.directives.addAll([
'package:angel_rethink/angel_rethink.dart',
'package:rethinkdb_driver/rethinkdb_driver.dart'
'package:rethinkdb_dart/rethinkdb_dart.dart'
].map((str) => new Directive.import(str)));
}

View file

@ -2,10 +2,10 @@ import 'dart:math';
const String _valid =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
final Random _rnd = new Random.secure();
final Random _rnd = Random.secure();
String randomAlphaNumeric(int length) {
var b = new StringBuffer();
var b = StringBuffer();
for (int i = 0; i < length; i++) {
b.writeCharCode(_valid.codeUnitAt(_rnd.nextInt(_valid.length)));

View file

@ -1,28 +1,27 @@
author: Tobe O <thosakwe@gmail.com>
#author: Tobe O <thosakwe@gmail.com>
description: Command-line tools for the Angel framework, including scaffolding.
homepage: https://github.com/angel-dart/angel_cli
homepage: https://github.com/dukefirehawk/angel/packages/angel_cli
name: angel_cli
version: 2.1.7+1
version: 3.0.0
environment:
sdk: ">=2.10.0 <3.0.0"
dependencies:
analyzer: ">=0.32.0 <2.0.0"
args: ^1.0.0
analyzer: ^1.1.0
args: ^2.0.0
code_builder: ^3.0.0
dart_style: ^1.0.0
glob: ^1.1.0
http: ^0.12.0
io: ^0.3.2
glob: ^2.0.0
http: ^0.13.0
io: ^0.3.5
inflection2: ^0.4.2
mustache4dart: ^3.0.0-dev.1.0
mustache4dart2: ^0.1.0
path: ^1.0.0
prompts: ^1.0.0
pubspec_parse: ^0.1.2
quiver: ^2.0.0
recase: ^2.0.0
prompts: ^1.3.1
pubspec_parse: ^1.0.0
quiver: ^3.0.0
recase: ^3.0.1
shutdown: ^0.4.0
watcher: ^0.9.7
yaml: ^2.0.0
#yamlicious: ^0.0.5
environment:
sdk: ">=2.0.0-dev <3.0.0"
watcher: ^1.0.0
yaml: ^3.0.0
executables:
angel: angel

View file

@ -64,6 +64,7 @@ abstract class Angel extends http.BaseClient {
Stream<String> authenticateViaPopup(String url, {String eventName = 'token'});
/// Disposes of any outstanding resources.
@override
Future<void> close();
/// Applies an [AngelConfigurer] to this instance.
@ -86,8 +87,8 @@ abstract class Angel extends http.BaseClient {
Service<Id, Data> service<Id, Data>(String path,
{@deprecated Type type, AngelDeserializer<Data> deserializer});
@override
Future<http.Response> delete(url, {Map<String, String> headers});
//@override
//Future<http.Response> delete(url, {Map<String, String> headers});
@override
Future<http.Response> get(url, {Map<String, String> headers});
@ -123,7 +124,7 @@ class AngelAuthResult {
/// Attempts to deserialize a response from a [Map].
factory AngelAuthResult.fromMap(Map data) {
final result = new AngelAuthResult();
final result = AngelAuthResult();
if (data is Map && data.containsKey('token') && data['token'] is String)
result._token = data['token'].toString();
@ -132,10 +133,10 @@ class AngelAuthResult {
result.data.addAll((data['data'] as Map<String, dynamic>) ?? {});
if (result.token == null) {
throw new FormatException(
throw FormatException(
'The required "token" field was not present in the given data.');
} else if (data['data'] is! Map) {
throw new FormatException(
throw FormatException(
'The required "data" field in the given data was not a map; instead, it was ${data['data']}.');
}
@ -144,7 +145,7 @@ class AngelAuthResult {
/// Attempts to deserialize a response from a [String].
factory AngelAuthResult.fromJson(String s) =>
new AngelAuthResult.fromMap(json.decode(s) as Map);
AngelAuthResult.fromMap(json.decode(s) as Map);
/// Converts this instance into a JSON-friendly representation.
Map<String, dynamic> toJson() {
@ -199,7 +200,7 @@ abstract class Service<Id, Data> {
///
/// Handy utility for handling data in a type-safe manner.
Service<Id, U> map<U>(U Function(Data) encoder, Data Function(U) decoder) {
return new _MappedService(this, encoder, decoder);
return _MappedService(this, encoder, decoder);
}
}
@ -214,7 +215,7 @@ class _MappedService<Id, Data, U> extends Service<Id, U> {
Angel get app => inner.app;
@override
Future close() => new Future.value();
Future close() => Future.value();
@override
Future<U> create(U data, [Map<String, dynamic> params]) {
@ -280,19 +281,18 @@ class ServiceList<Id, Data> extends DelegatingList<Data> {
final Service<Id, Data> service;
final StreamController<ServiceList<Id, Data>> _onChange =
new StreamController();
final StreamController<ServiceList<Id, Data>> _onChange = StreamController();
final List<StreamSubscription> _subs = [];
ServiceList(this.service, {this.idField = 'id', Equality<Data> equality})
: super([]) {
_equality = equality;
_equality ??= new EqualityBy<Data, Id>((map) {
_equality ??= EqualityBy<Data, Id>((map) {
if (map is Map)
return map[idField ?? 'id'] as Id;
else
throw new UnsupportedError(
throw UnsupportedError(
'ServiceList only knows how to find the id from a Map object. Provide a custom `Equality` in your call to the constructor.');
});
// Index

View file

@ -10,14 +10,14 @@ import 'package:http/src/streamed_response.dart' as http;
import 'package:path/path.dart' as p;
import 'angel_client.dart';
const Map<String, String> _readHeaders = const {'Accept': 'application/json'};
const Map<String, String> _writeHeaders = const {
const Map<String, String> _readHeaders = {'Accept': 'application/json'};
const Map<String, String> _writeHeaders = {
'Accept': 'application/json',
'Content-Type': 'application/json'
};
Map<String, String> _buildQuery(Map<String, dynamic> params) {
return params?.map((k, v) => new MapEntry(k, v.toString()));
return params?.map((k, v) => MapEntry(k, v.toString()));
}
bool _invalid(http.Response response) =>
@ -31,16 +31,16 @@ AngelHttpException failure(http.Response response,
var v = json.decode(response.body);
if (v is Map && (v['is_error'] == true) || v['isError'] == true) {
return new AngelHttpException.fromMap(v as Map);
return AngelHttpException.fromMap(v as Map);
} else {
return new AngelHttpException(error,
return AngelHttpException(error,
message: message ??
'Unhandled exception while connecting to Angel backend.',
statusCode: response.statusCode,
stackTrace: stack);
}
} catch (e, st) {
return new AngelHttpException(error ?? e,
return AngelHttpException(error ?? e,
message: message ??
'Angel backend did not return JSON - an error likely occurred.',
statusCode: response.statusCode,
@ -50,7 +50,7 @@ AngelHttpException failure(http.Response response,
abstract class BaseAngelClient extends Angel {
final StreamController<AngelAuthResult> _onAuthenticated =
new StreamController<AngelAuthResult>();
StreamController<AngelAuthResult>();
final List<Service> _services = [];
final http.BaseClient client;
@ -70,7 +70,11 @@ abstract class BaseAngelClient extends Angel {
var segments = baseUrl.pathSegments
.followedBy(p.split(authEndpoint))
.followedBy([type]);
var url = baseUrl.replace(path: p.joinAll(segments));
// TODO: convert windows path to proper url
var p1 = p.joinAll(segments).replaceAll('\\', '/');
var url = baseUrl.replace(path: p1);
http.Response response;
if (credentials != null) {
@ -85,16 +89,17 @@ abstract class BaseAngelClient extends Angel {
}
try {
var v = json.decode(response.body);
//var v = json.decode(response.body);
var v = jsonDecode(response.body);
if (v is! Map ||
!(v as Map).containsKey('data') ||
!(v as Map).containsKey('token')) {
throw new AngelHttpException.notAuthenticated(
throw AngelHttpException.notAuthenticated(
message: "Auth endpoint '$url' did not return a proper response.");
}
var r = new AngelAuthResult.fromMap(v as Map);
var r = AngelAuthResult.fromMap(v as Map);
_onAuthenticated.add(r);
return r;
} on AngelHttpException {
@ -104,6 +109,7 @@ abstract class BaseAngelClient extends Angel {
}
}
@override
Future<void> close() async {
client.close();
await _onAuthenticated.close();
@ -112,14 +118,16 @@ abstract class BaseAngelClient extends Angel {
});
}
@override
Future<void> logout() async {
authToken = null;
}
@override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
if (authToken?.isNotEmpty == true)
if (authToken?.isNotEmpty == true) {
request.headers['authorization'] ??= 'Bearer $authToken';
}
return client.send(request);
}
@ -128,7 +136,7 @@ abstract class BaseAngelClient extends Angel {
String method, url, Map<String, String> headers,
[body, Encoding encoding]) async {
var request =
new http.Request(method, url is Uri ? url : Uri.parse(url.toString()));
http.Request(method, url is Uri ? url : Uri.parse(url.toString()));
if (headers != null) request.headers.addAll(headers);
@ -137,12 +145,12 @@ abstract class BaseAngelClient extends Angel {
if (body is String) {
request.body = body;
} else if (body is List<int>) {
request.bodyBytes = new List<int>.from(body);
request.bodyBytes = List<int>.from(body);
} else if (body is Map<String, dynamic>) {
request.bodyFields =
body.map((k, v) => MapEntry(k, v is String ? v : v.toString()));
} else {
throw new ArgumentError.value(body, 'body',
throw ArgumentError.value(body, 'body',
'must be a String, List<int>, or Map<String, String>.');
}
}
@ -154,7 +162,7 @@ abstract class BaseAngelClient extends Angel {
Service<Id, Data> service<Id, Data>(String path,
{Type type, AngelDeserializer<Data> deserializer}) {
var url = baseUrl.replace(path: p.join(baseUrl.path, path));
var s = new BaseAngelService<Id, Data>(client, this, url,
var s = BaseAngelService<Id, Data>(client, this, url,
deserializer: deserializer);
_services.add(s);
return s;
@ -166,10 +174,10 @@ abstract class BaseAngelClient extends Angel {
return u.replace(path: p.join(baseUrl.path, u.path));
}
@override
Future<http.Response> delete(url, {Map<String, String> headers}) async {
return sendUnstreamed('DELETE', _join(url), headers);
}
//@override
//Future<http.Response> delete(url, {Map<String, String> headers}) async {
// return sendUnstreamed('DELETE', _join(url), headers);
//}
@override
Future<http.Response> get(url, {Map<String, String> headers}) async {
@ -207,12 +215,12 @@ class BaseAngelService<Id, Data> extends Service<Id, Data> {
final http.BaseClient client;
final AngelDeserializer<Data> deserializer;
final StreamController<List<Data>> _onIndexed = new StreamController();
final StreamController<Data> _onRead = new StreamController(),
_onCreated = new StreamController(),
_onModified = new StreamController(),
_onUpdated = new StreamController(),
_onRemoved = new StreamController();
final StreamController<List<Data>> _onIndexed = StreamController();
final StreamController<Data> _onRead = StreamController(),
_onCreated = StreamController(),
_onModified = StreamController(),
_onUpdated = StreamController(),
_onRemoved = StreamController();
@override
Stream<List<Data>> get onIndexed => _onIndexed.stream;
@ -253,8 +261,9 @@ class BaseAngelService<Id, Data> extends Service<Id, Data> {
return deserializer != null ? deserializer(x) : x as Data;
}
makeBody(x) {
return json.encode(x);
String makeBody(x) {
//return json.encode(x);
return jsonEncode(x);
}
Future<http.StreamedResponse> send(http.BaseRequest request) {
@ -272,22 +281,24 @@ class BaseAngelService<Id, Data> extends Service<Id, Data> {
try {
if (_invalid(response)) {
if (_onIndexed.hasListener)
if (_onIndexed.hasListener) {
_onIndexed.addError(failure(response));
else
} else {
throw failure(response);
}
}
var v = json.decode(response.body) as List;
var r = v.map(deserialize).toList();
_onIndexed.add(r);
return r;
} catch (e, st) {
if (_onIndexed.hasListener)
if (_onIndexed.hasListener) {
_onIndexed.addError(e, st);
else
} else {
throw failure(response, error: e, stack: st);
}
}
return null;
}

View file

@ -13,13 +13,13 @@ export 'angel_client.dart';
class Rest extends BaseAngelClient {
final List<Service> _services = [];
Rest(String path) : super(new http.Client() as http.BaseClient, path);
Rest(String path) : super(http.Client() as http.BaseClient, path);
@override
Service<Id, Data> service<Id, Data>(String path,
{Type type, AngelDeserializer deserializer}) {
var url = baseUrl.replace(path: p.join(baseUrl.path, path));
var s = new RestService<Id, Data>(client, this, url, type);
var s = RestService<Id, Data>(client, this, url, type);
_services.add(s);
return s;
}
@ -27,10 +27,11 @@ class Rest extends BaseAngelClient {
@override
Stream<String> authenticateViaPopup(String url,
{String eventName = 'token'}) {
throw new UnimplementedError(
throw UnimplementedError(
'Opening popup windows is not supported in the `dart:io` client.');
}
@override
Future close() async {
await super.close();
await Future.wait(_services.map((s) => s.close())).then((_) {
@ -48,6 +49,7 @@ class RestService<Id, Data> extends BaseAngelService<Id, Data> {
@override
Data deserialize(x) {
print(x);
if (type != null) {
return x.runtimeType == type
? x as Data
@ -58,7 +60,8 @@ class RestService<Id, Data> extends BaseAngelService<Id, Data> {
}
@override
makeBody(x) {
String makeBody(x) {
print(x);
if (type != null) {
return super.makeBody(god.serializeObject(x));
}

View file

@ -1,23 +1,41 @@
name: angel_client
version: 2.0.2
version: 3.0.0
description: Support for querying Angel servers in the browser, Flutter, and command-line.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_client
publish_to: none
environment:
sdk: ">=2.0.0-dev <3.0.0"
sdk: ">=2.10.0 <3.0.0"
dependencies:
angel_http_exception: ^1.0.0
angel_http_exception:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/http_exception
collection: ^1.0.0
http: ^0.12.0
json_god: ">=2.0.0-beta <3.0.0"
http: ^0.13.0
json_god:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/json_god
#dart_json_mapper: ^1.7.0
meta: ^1.0.0
path: ^1.0.0
dev_dependencies:
angel_framework: ^2.0.0-alpha
angel_model: ^1.0.0
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/framework
angel_model:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/model
async: ^2.0.0
build_runner: ^1.0.0
build_web_compilers: ^1.0.0
build_web_compilers: ^2.12.2
mock_request: ^1.0.0
pedantic: ^1.0.0
test: ^1.0.0
pedantic: ^1.11.0
test: ^1.16.5

View file

@ -1,14 +1,14 @@
import 'package:angel_client/angel_client.dart';
import 'dart:convert';
import 'package:test/test.dart';
import 'common.dart';
main() {
var app = new MockAngel();
Service todoService = app.service('api/todos');
void main() {
var app = MockAngel();
var todoService = app.service('api/todos');
test('sets method,body,headers,path', () async {
await app.post('/post', headers: {'method': 'post'}, body: 'post');
await app.post(Uri.parse('/post'),
headers: {'method': 'post'}, body: 'post');
expect(app.client.spec.method, 'POST');
expect(app.client.spec.path, '/post');
expect(app.client.spec.headers['method'], 'post');

View file

@ -1,19 +1,24 @@
name: angel_configuration
description: Automatic YAML application configuration loader for Angel, with .env support.
version: 2.2.0
version: 3.0.0
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_configuration
publish_to: none
environment:
sdk: ">=2.0.0 <3.0.0"
sdk: ">=2.10.0 <3.0.0"
dependencies:
angel_framework: ^2.0.0
dotenv: ^1.0.0
file: ^5.0.0
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/framework
dotenv: ^3.0.0-nullsafety.0
# file: ^5.0.0
merge_map: ^1.0.0
yaml: ^2.0.0
yaml: ^3.1.0
dev_dependencies:
io: ^0.3.2
logging: ^0.11.0
io: ^1.0.0
# logging: ^0.11.0
pedantic: ^1.0.0
pretty_logging: ^1.0.0
test: ^1.0.0
# pretty_logging: ^1.0.0
test: ^1.15.7

View file

@ -5,11 +5,10 @@ import 'package:angel_configuration/angel_configuration.dart';
import 'package:file/local.dart';
import 'package:io/ansi.dart';
import 'package:logging/logging.dart';
import 'package:pretty_logging/pretty_logging.dart';
import 'package:test/test.dart';
Future<void> main() async {
Logger.root.onRecord.listen(prettyLog);
//Logger.root.onRecord.listen(prettyLog);
// Note: Set ANGEL_ENV to 'development'
var app = Angel(logger: Logger('angel_configuration'));

View file

@ -1,4 +1,4 @@
import 'package:angel_container/angel_container.dart';
import '../../angel_container.dart';
final Map<Symbol, String> _symbolNames = <Symbol, String>{};

View file

@ -1,7 +1,8 @@
import 'dart:async';
import 'dart:mirrors' as dart;
import 'package:angel_container/angel_container.dart';
import 'package:angel_container/src/reflector.dart';
import '../exception.dart';
import '../reflector.dart';
/// A [Reflector] implementation that forwards to `dart:mirrors`.
///

View file

@ -1,4 +1,4 @@
import 'package:angel_container/angel_container.dart';
import '../reflector.dart';
/// A [Reflector] implementation that performs simple [Map] lookups.
///

View file

@ -1,4 +1,5 @@
import 'package:angel_container/angel_container.dart';
import 'empty/empty.dart';
import 'reflector.dart';
/// A [Reflector] implementation that throws exceptions on all attempts
/// to perform reflection.

View file

@ -1,13 +1,13 @@
name: angel_container
version: 1.1.0
version: 2.0.0
author: Tobe O <thosakwe@gmail.com>
description: A hierarchical DI container, and pluggable backends for reflection.
homepage: https://github.com/angel-dart/container.git
environment:
sdk: ">=1.8.0 <3.0.0"
sdk: ">=2.10.0 <3.0.0"
dependencies:
collection: ^1.0.0
quiver: ^2.0.0
collection: ^1.15.0
quiver: ^2.1.5
dev_dependencies:
pedantic: ^1.0.0
test: ^1.0.0
pedantic: ^1.11.0
test: ^1.16.5

View file

@ -1,14 +1,19 @@
name: angel_container_generator
version: 1.0.1
version: 2.0.0
author: Tobe O <thosakwe@gmail.com>
description: Codegen support for using pkg:reflectable with pkg:angel_container.
homepage: https://github.com/angel-dart/container.git
publish_to: none
environment:
sdk: ">=2.0.0-dev <3.0.0"
sdk: ">=2.10.0 <3.0.0"
dependencies:
angel_container: ^1.0.0-alpha
reflectable: ^2.0.0
angel_container:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/container/angel_container
reflectable: ^2.2.9
dev_dependencies:
build_runner: ^1.0.0
build_test:
test: ^1.0.0
build_runner: ^1.11.1
build_test: ^1.3.6
test: ^1.16.5

View file

@ -1,14 +1,23 @@
author: Tobe O <thosakwe@gmail.com>
description: Angel CORS middleware. Port of expressjs/cors to the Angel framework.
environment:
sdk: ">=2.0.0 <3.0.0"
sdk: ">=2.10.0 <3.0.0"
homepage: https://github.com/angel-dart/cors.git
name: angel_cors
version: 2.0.0
version: 3.0.0
publish_to: none
dependencies:
angel_framework: ^2.0.0-alpha
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/framework
dev_dependencies:
angel_test: ^2.0.0
http: ^0.12.0
pedantic: ^1.0.0
test: ^1.0.0
angel_test:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/test
http: ^0.13.0
pedantic: ^1.11.0
test: ^1.16.5

View file

@ -1,16 +1,27 @@
name: angel_eventsource
version: 1.0.0
version: 2.0.0
description: Server-sent Events (SSE) plugin for Angel.
homepage: https://github.com/angel-dart/eventsource
author: Tobe O <thosakwe@gmail.com>
publish_to: none
environment:
sdk: ">=2.0.0-dev <3.0.0"
sdk: ">=2.10.0 <3.0.0"
dependencies:
angel_framework: ^2.0.0-alpha
angel_websocket: ^2.0.0-alpha
eventsource: ^0.2.0
stream_channel: ^1.0.0
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/framework
angel_websocket:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/websocket
eventsource:
git:
url: https://github.com/dukefirehawk/dart-eventsource.git
stream_channel: ^2.0.0
dev_dependencies:
console: ^3.0.0
logging:
test: ^1.0.0
console: ^4.0.0
logging: ^1.0.0
test: ^1.16.5

View file

@ -1,13 +1,18 @@
name: angel_file_service
version: 2.0.1
version: 3.0.0
description: Angel service that persists data to a file on disk.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/file_service
publish_to: none
environment:
sdk: ">=2.0.0-dev <3.0.0"
sdk: ">=2.10.0 <3.0.0"
dependencies:
angel_framework: ^2.0.0-alpha
file: ^5.0.0
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/framework
file: ^6.1.0
pool: ^1.0.0
dev_dependencies:
test: ^1.0.0
test: ^1.16.5

View file

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/http.dart';
import 'package:logging/logging.dart';
import 'package:pretty_logging/pretty_logging.dart';
Future<void> apiConfigurer(Angel app) async {
app.get('/', (req, res) => 'Hello, API!');
@ -18,7 +17,7 @@ Future<void> frontendConfigurer(Angel app) async {
main() async {
// Logging set up/boilerplate
hierarchicalLoggingEnabled = true;
Logger.root.onRecord.listen(prettyLog);
//Logger.root.onRecord.listen(prettyLog);
var app = Angel(logger: Logger('angel'));
var http = AngelHttp(app);

View file

@ -2,11 +2,10 @@ import 'package:angel_container/mirrors.dart';
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/http.dart';
import 'package:logging/logging.dart';
import 'package:pretty_logging/pretty_logging.dart';
main() async {
// Logging set up/boilerplate
Logger.root.onRecord.listen(prettyLog);
//Logger.root.onRecord.listen(prettyLog);
// Create our server.
var app = Angel(

View file

@ -1,37 +1,55 @@
name: angel_framework
version: 2.1.1
version: 3.0.0
description: A high-powered HTTP server with dependency injection, routing and much more.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_framework
publish_to: none
environment:
sdk: ">=2.0.0-dev <3.0.0"
sdk: ">=2.10.0 <3.0.0"
dependencies:
angel_container: ^1.0.4
angel_http_exception: ^1.0.0
angel_model: ^1.0.0
angel_route: ^3.0.0
angel_container:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/container/angel_container
angel_http_exception:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/http_exception
angel_model:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/model
angel_route:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/route
charcode: ^1.0.0
combinator: ^1.0.0
file: ^5.0.0
http_parser: ^3.0.0
file: ^6.1.0
http_parser: ^4.0.0
http_server: ^0.9.0
http2: "^1.0.0"
logging: ">=0.11.3 <1.0.0"
http2: ^2.0.0
logging: ^1.0.0
matcher: ^0.12.0
merge_map: ^1.0.0
meta: ^1.0.0
mime: ^0.9.3
mime: ^0.9.0
mock_request: ^1.0.0
path: ^1.0.0
# pedantic: ^1.0.0
quiver_hashcode: ^2.0.0
recase: ^2.0.0
recase: ^3.0.1
stack_trace: ^1.0.0
string_scanner: ^1.0.0
tuple: ^1.0.0
uuid: ^2.0.0-rc.1
tuple: ^1.0.3
uuid: ^3.0.1
dev_dependencies:
http: ^0.12.0
io: ^0.3.0
pretty_logging: ^1.0.0
test: ^1.0.0
http: ^0.13.0
io: ^1.0.0
#pretty_logging: ^1.0.0
test: ^1.15.7

View file

@ -139,7 +139,7 @@ main() {
test("middleware", () async {
var rgx = RegExp("^Hello, world!");
var response = await client.get("$url/todos/0");
var response = await client.get(Uri.parse("$url/todos/0"));
print('Response: ${response.body}');
expect(rgx.firstMatch(response.body)?.start, equals(0));
@ -152,7 +152,7 @@ main() {
test("controller in group", () async {
var rgx = RegExp("^Hello, world!");
var response = await client.get("$url/ctrl_group/todos/0");
var response = await client.get(Uri.parse("$url/ctrl_group/todos/0"));
print('Response: ${response.body}');
expect(rgx.firstMatch(response.body)?.start, equals(0));
@ -164,7 +164,7 @@ main() {
});
test("named actions", () async {
var response = await client.get("$url/redirect");
var response = await client.get(Uri.parse("$url/redirect"));
print('Response: ${response.body}');
expect(response.body, equals("Hello, \"world!\""));
});

View file

@ -71,32 +71,33 @@ main() {
});
test("singleton in route", () async {
validateTodoSingleton(await client.get("$url/errands"));
validateTodoSingleton(await client.get(Uri.parse("$url/errands")));
});
test("singleton in controller", () async {
validateTodoSingleton(await client.get("$url/errands2"));
validateTodoSingleton(await client.get(Uri.parse("$url/errands2")));
});
test("make in route", () async {
var response = await client.get("$url/errands3");
var response = await client.get(Uri.parse("$url/errands3"));
var text = await json.decode(response.body) as String;
expect(text, equals(TEXT));
});
test("make in controller", () async {
var response = await client.get("$url/errands4");
var response = await client.get(Uri.parse("$url/errands4"));
var text = await json.decode(response.body) as String;
expect(text, equals(TEXT));
});
test('resolve from future in controller', () async {
var response = await client.post('$url/errands4/async', body: 'hey');
var response =
await client.post(Uri.parse('$url/errands4/async'), body: 'hey');
expect(response.body, json.encode({'bar': 'hey'}));
});
test('resolve from future in route', () async {
var response = await client.post('$url/async', body: 'yes');
var response = await client.post(Uri.parse('$url/async'), body: 'yes');
expect(response.body, json.encode({'baz': 'yes'}));
});
}

View file

@ -31,8 +31,8 @@ main() {
});
test("allow override of method", () async {
var response = await client
.get('$url/foo', headers: {'X-HTTP-Method-Override': 'POST'});
var response = await client.get(Uri.parse('$url/foo'),
headers: {'X-HTTP-Method-Override': 'POST'});
print('Response: ${response.body}');
expect(json.decode(response.body), equals({'hello': 'world'}));
});

View file

@ -59,7 +59,7 @@ main() {
count++;
});
var response = await client.get("$url/todos");
var response = await client.get(Uri.parse("$url/todos"));
print(response.body);
expect(count, equals(2));
});
@ -73,7 +73,7 @@ main() {
event.cancel({"this_hook": "should never run"});
});
var response = await client.post("$url/todos",
var response = await client.post(Uri.parse("$url/todos"),
body: json.encode({"arbitrary": "data"}),
headers: headers as Map<String, String>);
print(response.body);
@ -93,7 +93,7 @@ main() {
event.cancel({"this_hook": "should never run either"});
});
var response = await client.get("$url/todos");
var response = await client.get(Uri.parse("$url/todos"));
print(response.body);
var result = json.decode(response.body) as List;
expect(result[0]["angel"], equals("framework"));
@ -121,7 +121,7 @@ main() {
print('Indexing books at path: ${e.request.path}');
});
var response = await client.get('$url/books');
var response = await client.get(Uri.parse('$url/books'));
print(response.body);
var result = json.decode(response.body);

View file

@ -127,12 +127,12 @@ main() {
});
test('Can match basic url', () async {
var response = await client.get("$url/hello");
var response = await client.get(Uri.parse("$url/hello"));
expect(response.body, equals('"world"'));
});
test('Can match url with multiple parameters', () async {
var response = await client.get('$url/name/HELLO/last/WORLD');
var response = await client.get(Uri.parse('$url/name/HELLO/last/WORLD'));
print('Response: ${response.body}');
var json_ = json.decode(response.body);
expect(json_, const IsInstanceOf<Map>());
@ -141,18 +141,18 @@ main() {
});
test('Chained routes', () async {
var response = await client.get("$url/chained");
var response = await client.get(Uri.parse("$url/chained"));
expect(response.body, equals('abc'));
});
test('Can nest another Angel instance', () async {
var response = await client.post('$url/nes/ted/foo');
var response = await client.post(Uri.parse('$url/nes/ted/foo'));
var json_ = json.decode(response.body);
expect(json_['route'], equals('foo'));
});
test('Can parse parameters from a nested Angel instance', () async {
var response = await client.get('$url/todos/1337/action/test');
var response = await client.get(Uri.parse('$url/todos/1337/action/test'));
var json_ = json.decode(response.body);
print('JSON: $json_');
expect(json_['id'], equals('1337'));
@ -160,27 +160,27 @@ main() {
});
test('Can add and use named middleware', () async {
var response = await client.get('$url/intercepted');
var response = await client.get(Uri.parse('$url/intercepted'));
expect(response.body, equals('Middleware'));
});
test('Middleware via metadata', () async {
// Metadata
var response = await client.get('$url/meta');
var response = await client.get(Uri.parse('$url/meta'));
expect(response.body, equals('Middleware'));
});
test('Can serialize function result as JSON', () async {
Map headers = <String, String>{'Content-Type': 'application/json'};
String postData = json.encode({'it': 'works'});
var response = await client.post("$url/lambda",
var response = await client.post(Uri.parse("$url/lambda"),
headers: headers as Map<String, String>, body: postData);
print('Response: ${response.body}');
expect(json.decode(response.body)['it'], equals('works'));
});
test('Fallback routes', () async {
var response = await client.get('$url/my_favorite_artist');
var response = await client.get(Uri.parse('$url/my_favorite_artist'));
expect(response.body, equals('"MJ"'));
});
@ -193,32 +193,32 @@ main() {
});
test('Redirect to named routes', () async {
var response = await client.get('$url/named');
var response = await client.get(Uri.parse('$url/named'));
print(response.body);
expect(json.decode(response.body), equals('Hello tests'));
});
test('Match routes, even with query params', () async {
var response =
await client.get("$url/log?foo=bar&bar=baz&baz.foo=bar&baz.bar=foo");
var response = await client
.get(Uri.parse("$url/log?foo=bar&bar=baz&baz.foo=bar&baz.bar=foo"));
print(response.body);
expect(json.decode(response.body), equals('Logged'));
response = await client.get("$url/query/foo?bar=baz");
response = await client.get(Uri.parse("$url/query/foo?bar=baz"));
print(response.body);
expect(response.body, equals("Service with Middleware"));
});
test('only match route with matching method', () async {
var response = await client.get("$url/method");
var response = await client.get(Uri.parse("$url/method"));
print(response.body);
expect(response.body, '"Only GET"');
response = await client.post("$url/method");
response = await client.post(Uri.parse("$url/method"));
print(response.body);
expect(response.body, '"Only POST"');
response = await client.patch("$url/method");
response = await client.patch(Uri.parse("$url/method"));
print(response.body);
expect(response.body, '"MJ"');
});

View file

@ -35,11 +35,11 @@ main() {
});
test("correct content-type", () async {
var response = await client.get('$url/foo');
var response = await client.get(Uri.parse('$url/foo'));
print('Response: ${response.body}');
expect(response.headers['content-type'], contains('application/json'));
response = await client.get('$url/bar');
response = await client.get(Uri.parse('$url/bar'));
print('Response: ${response.body}');
expect(response.headers['content-type'], contains('text/html'));
});

View file

@ -44,7 +44,7 @@ main() {
group('memory', () {
test('can index an empty service', () async {
var response = await client.get("$url/todos/");
var response = await client.get(Uri.parse("$url/todos/"));
print(response.body);
expect(response.body, equals('[]'));
print(response.body);
@ -53,7 +53,7 @@ main() {
test('can create data', () async {
String postData = json.encode({'text': 'Hello, world!'});
var response = await client.post("$url/todos",
var response = await client.post(Uri.parse("$url/todos"),
headers: headers as Map<String, String>, body: postData);
expect(response.statusCode, 201);
var jsons = json.decode(response.body);
@ -63,9 +63,9 @@ main() {
test('can fetch data', () async {
String postData = json.encode({'text': 'Hello, world!'});
await client.post("$url/todos",
await client.post(Uri.parse("$url/todos"),
headers: headers as Map<String, String>, body: postData);
var response = await client.get("$url/todos/0");
var response = await client.get(Uri.parse("$url/todos/0"));
expect(response.statusCode, 200);
var jsons = json.decode(response.body);
print(jsons);
@ -74,11 +74,11 @@ main() {
test('can modify data', () async {
String postData = json.encode({'text': 'Hello, world!'});
await client.post("$url/todos",
await client.post(Uri.parse("$url/todos"),
headers: headers as Map<String, String>, body: postData);
postData = json.encode({'text': 'modified'});
var response = await client.patch("$url/todos/0",
var response = await client.patch(Uri.parse("$url/todos/0"),
headers: headers as Map<String, String>, body: postData);
expect(response.statusCode, 200);
var jsons = json.decode(response.body);
@ -88,11 +88,11 @@ main() {
test('can overwrite data', () async {
String postData = json.encode({'text': 'Hello, world!'});
await client.post("$url/todos",
await client.post(Uri.parse("$url/todos"),
headers: headers as Map<String, String>, body: postData);
postData = json.encode({'over': 'write'});
var response = await client.post("$url/todos/0",
var response = await client.post(Uri.parse("$url/todos/0"),
headers: headers as Map<String, String>, body: postData);
expect(response.statusCode, 200);
var jsons = json.decode(response.body);
@ -115,10 +115,11 @@ main() {
test('can delete data', () async {
String postData = json.encode({'text': 'Hello, world!'});
var created = await client
.post("$url/todos",
.post(Uri.parse("$url/todos"),
headers: headers as Map<String, String>, body: postData)
.then((r) => json.decode(r.body));
var response = await client.delete("$url/todos/${created['id']}");
var response =
await client.delete(Uri.parse("$url/todos/${created['id']}"));
expect(response.statusCode, 200);
var json_ = json.decode(response.body);
print(json_);
@ -126,7 +127,7 @@ main() {
});
test('cannot remove all unless explicitly set', () async {
var response = await client.delete('$url/todos/null');
var response = await client.delete(Uri.parse('$url/todos/null'));
expect(response.statusCode, 403);
});
});

View file

@ -4,19 +4,29 @@ description: The fastest + easiest way to get a GraphQL backend in Dart, using A
homepage: https://github.com/angel-dart/graphql
author: Tobe O <thosakwe@gmail.com>
environment:
sdk: ">=2.0.0-dev <3.0.0"
sdk: '>=2.10.0 <2.12.0'
dependencies:
angel_file_service: ^2.0.0
angel_framework: ^2.0.0
angel_websocket: ^2.0.0
angel_validate: ^2.0.0
graphql_parser: ^1.0.0
graphql_schema: ^1.0.0
graphql_server: ^1.0.0
angel_file_service: #^2.0.0
path: ../../file_service
angel_framework: #^2.0.0
path: ../../framework
angel_websocket: #^2.0.0
path: ../../websocket
angel_validate: #^2.0.0
path: ../../validate
graphql_parser: #^1.0.0
path: ../graphql_parser
graphql_schema: #^1.0.0
path: ../graphql_schema
graphql_server: #^1.0.0
path: ../graphql_server
http_parser: ^3.0.0
web_socket_channel: ^1.0.0
dev_dependencies:
angel_serialize: ^2.0.0
angel_serialize: #^2.0.0
path: ../../serialize/angel_serialize
file: ^5.0.0
logging: ^0.11.0
pedantic: ^1.0.0

View file

@ -4,8 +4,9 @@ author: Tobe O <thosakwe@gmail.com>
description: Batch and cache database lookups. Works well with GraphQL. Ported from JS.
homepage: https://github.com/angel-dart/graphql
environment:
sdk: ">=2.0.0-dev <3.0.0"
sdk: '>=2.10.0 <2.12.0'
dev_dependencies:
graphql_schema: ^1.0.0
graphql_schema: #^1.0.0
path: ../graphql_schema
pedantic: ^1.0.0
test: ">=0.12.0 <2.0.0"
test: ^1.15.7

View file

@ -1,14 +1,19 @@
name: star_wars
publish_to: none
environment:
sdk: ">=2.10.0 <2.12.0"
dependencies:
#angel_file_service: ^1.0.0
angel_graphql:
path: ../angel_graphql
angel_hot: ^2.0.0-alpha
angel_serialize: ^2.0.0
angel_hot: #^2.0.0-alpha
path: ../../hot
angel_serialize: #^2.0.0
path: ../../serialize/angel_serialize
io: ^0.3.2
dev_dependencies:
angel_serialize_generator: ^2.0.0
angel_serialize_generator: #^2.0.0
path: ../../serialize/angel_serialize_generator
build_runner: ^1.0.0
graphql_generator:
path: ../graphql_generator
@ -19,3 +24,4 @@ dependency_overrides:
path: ../graphql_schema
graphql_server:
path: ../graphql_server

View file

@ -4,15 +4,18 @@ description: Generates GraphQL schemas from Dart classes, for use with pkg:graph
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/graphql
environment:
sdk: ">=2.0.0 <3.0.0"
sdk: ">=2.10.0 <2.12.0"
dependencies:
analyzer: ">=0.27.1 <2.0.0"
angel_model: ^1.0.0
angel_serialize_generator: ^2.0.0
angel_model: #^1.0.0
path: ../../model
angel_serialize_generator: #^2.0.0
path: ../../serialize/angel_serialize_generator
build: ^1.0.0
build_config: ^0.3.0
code_builder: ^3.0.0
graphql_schema: ^1.0.2
graphql_schema: #^1.0.2
path: ../graphql_schema
recase: ^2.0.0
source_gen: ^0.9.4
dev_dependencies:

View file

@ -4,7 +4,7 @@ description: Parses GraphQL queries and schemas. Also includes classes for the G
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/graphql
environment:
sdk: ">=1.8.0 <3.0.0"
sdk: '>=2.10.0 <2.12.0'
dependencies:
charcode: ^1.0.0
source_span: ^1.0.0
@ -12,4 +12,4 @@ dependencies:
dev_dependencies:
matcher: any
pedantic: ^1.0.0
test: ">=0.12.0 <2.0.0"
test: ^1.15.7

View file

@ -4,10 +4,10 @@ description: An implementation of GraphQL's type system in Dart. Basis of graphq
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/graphql
environment:
sdk: ">=1.8.0 <3.0.0"
sdk: '>=2.10.0 <2.12.0'
dependencies:
collection: ^1.0.0
meta: ^1.0.0
source_span: ^1.0.0
dev_dependencies:
test: ">=0.12.0 <2.0.0"
test: ^1.15.7

View file

@ -4,19 +4,22 @@ author: Tobe O <thosakwe@gmail.com>
description: Base package for implementing GraphQL servers. You might prefer `package:angel_graphql`, the fastest way to implement GraphQL backends in Dart.
homepage: https://github.com/angel-dart/graphql
environment:
sdk: ">=2.0.0 <3.0.0"
sdk: ">=2.10.0 <2.12.0"
dependencies:
angel_serialize: ^2.0.0
angel_serialize: #^2.0.0
path: ../../serialize/angel_serialize
collection: ^1.0.0
graphql_schema: ^1.0.0
graphql_parser: ^1.0.0
graphql_schema: #^1.0.0
path: ../graphql_schema
graphql_parser: #^1.0.0
path: ../graphql_parser
meta: ^1.0.0
recase: ^2.0.0
stream_channel: ^2.0.0
tuple: ^1.0.0
dev_dependencies:
pedantic: ^1.0.0
test: ">=0.12.0 <2.0.0"
dependency_overrides:
graphql_parser:
path: ../graphql_parser
test: ^1.15.7
#dependency_overrides:
# graphql_parser:
# path: ../graphql_parser

View file

@ -9,6 +9,7 @@ import 'package:angel_framework/http.dart';
import 'package:angel_websocket/server.dart';
import 'package:charcode/ascii.dart';
import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart';
import 'package:html_builder/elements.dart';
import 'package:html_builder/html_builder.dart';
import 'package:io/ansi.dart';
@ -103,8 +104,9 @@ class HotReloader {
response
..headers.set(HttpHeaders.contentEncodingHeader, 'gzip')
..add(gzip.encode(utf8.encode(_renderer.render(doc))));
} else
} else {
response.write(_renderer.render(doc));
}
response.close();
}
@ -113,11 +115,11 @@ class HotReloader {
}
Future handleRequest(HttpRequest request) async {
if (_server != null)
if (_server != null) {
return await _handle(request);
else if (timeout == null)
} else if (timeout == null) {
_requestQueue.add(request);
else {
} else {
_requestQueue.add(request);
Timer(timeout, () {
if (_requestQueue.remove(request)) {
@ -157,9 +159,10 @@ class HotReloader {
var isHot = true;
_server = await _generateServer();
if (_paths?.isNotEmpty != true)
if (_paths?.isNotEmpty != true) {
_logWarning(
'You have instantiated a HotReloader without providing any filesystem paths to watch.');
}
bool _sw(String s) {
return Platform.executableArguments.any((ss) => ss.startsWith(s));
@ -173,10 +176,11 @@ class HotReloader {
var info = await dev.Service.getInfo();
var uri = info.serverUri;
uri = uri.replace(path: p.join(uri.path, 'ws'));
if (uri.scheme == 'https')
if (uri.scheme == 'https') {
uri = uri.replace(scheme: 'wss');
else
} else {
uri = uri.replace(scheme: 'ws');
}
_client = await vm.vmServiceConnectUri(uri.toString());
_vmachine ??= await _client.getVM();
_mainIsolate ??= _vmachine.isolates.first;
@ -192,7 +196,9 @@ class HotReloader {
//.transform(new _Debounce(new Duration(seconds: 1)))
.listen(_handleWatchEvent);
while (_requestQueue.isNotEmpty) await _handle(_requestQueue.removeFirst());
while (_requestQueue.isNotEmpty) {
await _handle(_requestQueue.removeFirst());
}
var server = _io = await HttpServer.bind(address ?? '127.0.0.1', port ?? 0);
server.listen(handleRequest);
@ -278,12 +284,14 @@ class HotReloader {
} else if (path is Uri) {
if (path.scheme == 'package') {
var uri = await Isolate.resolvePackageUri(path);
if (uri != null)
if (uri != null) {
await _listenToStat(uri.toFilePath());
else
} else {
await _listenToStat(path.toFilePath());
} else
}
} else {
await _listenToStat(path.toFilePath());
}
} else {
throw ArgumentError(
'Hot reload paths must be a FileSystemEntity, a Uri, a String or a Glob. You provided: $path');
@ -305,8 +313,9 @@ class HotReloader {
} else if (stat.type == FileSystemEntityType.directory) {
var dir = Directory(path);
if (!await dir.exists()) return null;
} else
} else {
return null;
}
var watcher = Watcher(path);
@ -357,7 +366,7 @@ class HotReloader {
}
}
_handleWatchEvent(WatchEvent e, [bool hot = true]) async {
void _handleWatchEvent(WatchEvent e, [bool hot = true]) async {
_logInfo('${e.path} changed. Reloading server...\n');
await _killServer();
_server = null;
@ -373,7 +382,9 @@ class HotReloader {
var s = await _generateServer();
_server = s;
while (_requestQueue.isNotEmpty) await _handle(_requestQueue.removeFirst());
while (_requestQueue.isNotEmpty) {
await _handle(_requestQueue.removeFirst());
}
}
}

View file

@ -1,21 +1,30 @@
name: angel_hot
description: Supports hot reloading/hot code push of Angel servers on file changes.
version: 2.0.6
version: 3.0.0
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/hot
publish_to: none
environment:
sdk: ">=2.0.0 <3.0.0"
sdk: ">=2.10.0 <3.0.0"
dependencies:
angel_framework: ^2.0.0-alpha
angel_websocket: ^2.0.0-alpha
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/framework
angel_websocket: #^2.0.0-alpha
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/websocket
charcode: ^1.0.0
glob: ^1.0.0
glob: ^2.0.0
html_builder: ^1.0.0
io: ^0.3.2
io: ^0.3.5
path: ^1.0.0
vm_service_lib: ^0.3.5
watcher: ^0.9.0
vm_service_lib: ^3.22.2+1
watcher: ^1.0.0
dev_dependencies:
http: ^0.11.3
logging: ^0.11.0
http: ^0.13.0
logging: ^1.0.0
pedantic: ^1.0.0

View file

@ -4,13 +4,15 @@ description: Support for rendering html_builder AST's as responses in Angel.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/html_builder
environment:
sdk: ">=2.0.0-dev <3.0.0"
sdk: ">=2.10.0 <2.12.0"
dependencies:
angel_framework: ^2.0.0-alpha
angel_framework: #^2.0.0-alpha
path: ../framework
html_builder: ^1.0.0
dev_dependencies:
angel_test: ^2.0.0-alpha
angel_test: #^2.0.0-alpha
path: ../test
html: ^0.13.2
logging: ^0.11.0
test: ^1.0.0
test: ^1.15.7
pedantic: ^1.0.0

View file

@ -1,6 +1,7 @@
library angel_http_exception;
import 'package:dart2_constant/convert.dart';
//import 'package:dart2_constant/convert.dart';
import 'dart:convert';
/// Exception class that can be serialized to JSON and serialized to clients.
/// Carries HTTP-specific metadata, like [statusCode].
@ -45,11 +46,11 @@ class AngelHttpException implements Exception {
@override
String toString() {
return "$statusCode: $message";
return '$statusCode: $message';
}
factory AngelHttpException.fromMap(Map data) {
return new AngelHttpException(
return AngelHttpException(
null,
statusCode: (data['status_code'] ?? data['statusCode']) as int,
message: data['message']?.toString(),
@ -60,64 +61,64 @@ class AngelHttpException implements Exception {
}
factory AngelHttpException.fromJson(String str) =>
new AngelHttpException.fromMap(json.decode(str) as Map);
AngelHttpException.fromMap(json.decode(str) as Map);
/// Throws a 400 Bad Request error, including an optional arrray of (validation?)
/// errors you specify.
factory AngelHttpException.badRequest(
{String message = '400 Bad Request',
List<String> errors = const []}) =>
new AngelHttpException(null,
AngelHttpException(null,
message: message, errors: errors, statusCode: 400);
/// Throws a 401 Not Authenticated error.
factory AngelHttpException.notAuthenticated(
{String message = '401 Not Authenticated'}) =>
new AngelHttpException(null, message: message, statusCode: 401);
AngelHttpException(null, message: message, statusCode: 401);
/// Throws a 402 Payment Required error.
factory AngelHttpException.paymentRequired(
{String message = '402 Payment Required'}) =>
new AngelHttpException(null, message: message, statusCode: 402);
AngelHttpException(null, message: message, statusCode: 402);
/// Throws a 403 Forbidden error.
factory AngelHttpException.forbidden({String message = '403 Forbidden'}) =>
new AngelHttpException(null, message: message, statusCode: 403);
AngelHttpException(null, message: message, statusCode: 403);
/// Throws a 404 Not Found error.
factory AngelHttpException.notFound({String message = '404 Not Found'}) =>
new AngelHttpException(null, message: message, statusCode: 404);
AngelHttpException(null, message: message, statusCode: 404);
/// Throws a 405 Method Not Allowed error.
factory AngelHttpException.methodNotAllowed(
{String message = '405 Method Not Allowed'}) =>
new AngelHttpException(null, message: message, statusCode: 405);
AngelHttpException(null, message: message, statusCode: 405);
/// Throws a 406 Not Acceptable error.
factory AngelHttpException.notAcceptable(
{String message = '406 Not Acceptable'}) =>
new AngelHttpException(null, message: message, statusCode: 406);
AngelHttpException(null, message: message, statusCode: 406);
/// Throws a 408 Timeout error.
factory AngelHttpException.methodTimeout({String message = '408 Timeout'}) =>
new AngelHttpException(null, message: message, statusCode: 408);
AngelHttpException(null, message: message, statusCode: 408);
/// Throws a 409 Conflict error.
factory AngelHttpException.conflict({String message = '409 Conflict'}) =>
new AngelHttpException(null, message: message, statusCode: 409);
AngelHttpException(null, message: message, statusCode: 409);
/// Throws a 422 Not Processable error.
factory AngelHttpException.notProcessable(
{String message = '422 Not Processable'}) =>
new AngelHttpException(null, message: message, statusCode: 422);
AngelHttpException(null, message: message, statusCode: 422);
/// Throws a 501 Not Implemented error.
factory AngelHttpException.notImplemented(
{String message = '501 Not Implemented'}) =>
new AngelHttpException(null, message: message, statusCode: 501);
AngelHttpException(null, message: message, statusCode: 501);
/// Throws a 503 Unavailable error.
factory AngelHttpException.unavailable(
{String message = '503 Unavailable'}) =>
new AngelHttpException(null, message: message, statusCode: 503);
AngelHttpException(null, message: message, statusCode: 503);
}

View file

@ -1,11 +1,9 @@
name: angel_http_exception
version: 1.1.0
version: 2.0.0
description: Exception class that can be serialized to JSON and serialized to clients.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/http_exception
#author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/dukefirehawk/angel/packages/http_exception
environment:
sdk: ">=1.19.0 <3.0.0"
dependencies:
dart2_constant: ^1.0.0
sdk: ">=2.10.0 <3.0.0"
dev_dependencies:
pedantic: ^1.0.0
pedantic: ^1.11.0

26
packages/jael/LSP_LICENSE Normal file
View file

@ -0,0 +1,26 @@
Copyright 2017 dart_language_server authors
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,19 +1,38 @@
name: angel_jael
version: 2.0.0
version: 3.0.0
description: Angel support for the Jael templating engine, similar to Blade or Liquid.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/jael/tree/master/jael
publish_to: none
environment:
sdk: ">=2.0.0-dev <=3.0.0"
sdk: '>=2.10.0 <3.0.0'
dependencies:
angel_framework: ^2.0.0-alpha
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/framework
code_buffer: ^1.0.0
file: ^5.0.0
jael: ^2.0.0
jael_preprocessor: #^2.0.0
path: ../jael_preprocessor
file: ^6.0.0
jael:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/jael/jael
jael_preprocessor:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/jael/jael_preprocessor
symbol_table: ^2.0.0
dev_dependencies:
angel_test: ^2.0.0-alpha
html:
test: ^1.0.0
angel_test:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/test
html: ^0.15.0
test: ^1.15.7
dependency_overrides:
web_socket_channel: ^2.0.0
http: ^0.13.0

View file

@ -62,7 +62,7 @@ main() {
});
test('can render', () async {
var response = await client.get('/github/thosakwe');
var response = await client.get(Uri.parse('/github/thosakwe'));
print('Body:\n${response.body}');
expect(
html.parse(response.body).outerHtml,

View file

@ -1,12 +1,12 @@
name: jael
version: 2.0.2
version: 3.0.0
description: A simple server-side HTML templating engine for Dart. Comparable to Blade or Liquid.
author: Tobe O <thosakwe@gmail.com>
homepage: https://docs.angel-dart.dev/packages/front-end/jael
environment:
sdk: ">=2.0.0 <3.0.0"
sdk: '>=2.10.0 <3.0.0'
dependencies:
args: ^1.0.0
args: ^2.0.0
charcode: ^1.0.0
code_buffer: ^1.0.0
source_span: ^1.0.0

View file

@ -3,8 +3,9 @@ import 'dart:io';
import 'package:args/args.dart';
import 'package:io/ansi.dart';
import 'package:io/io.dart';
import 'package:dart_language_server/dart_language_server.dart';
//import 'package:dart_language_server/dart_language_server.dart';
import 'package:jael_language_server/jael_language_server.dart';
import 'package:jael_language_server/src/protocol/language_server/server.dart';
main(List<String> args) async {
var argParser = new ArgParser()

View file

@ -0,0 +1,64 @@
import 'dart:async';
import 'package:json_rpc_2/json_rpc_2.dart';
import 'messages.dart';
abstract class LanguageServer {
final _onDone = Completer<void>();
Future<void> get onDone => _onDone.future;
Future<void> shutdown() async {}
void exit() {
_onDone.complete();
}
Future<ServerCapabilities> initialize(int clientPid, String rootUri,
ClientCapabilities clientCapabilities, String trace) async =>
ServerCapabilities((b) => b);
void initialized() {}
void textDocumentDidOpen(TextDocumentItem document) {}
void textDocumentDidChange(VersionedTextDocumentIdentifier documentId,
List<TextDocumentContentChangeEvent> changes) {}
void textDocumentDidClose(TextDocumentIdentifier documentId) {}
Future<CompletionList> textDocumentCompletion(
TextDocumentIdentifier documentId, Position position) async =>
CompletionList((b) => b);
Future<Location> textDocumentDefinition(
TextDocumentIdentifier documentId, Position position) async =>
null;
Future<List<Location>> textDocumentReferences(
TextDocumentIdentifier documentId,
Position position,
ReferenceContext context) async =>
[];
Future<List<Location>> textDocumentImplementation(
TextDocumentIdentifier documentId, Position position) async =>
[];
Future<List<DocumentHighlight>> textDocumentHighlight(
TextDocumentIdentifier documentId, Position position) async =>
[];
Future<List<SymbolInformation>> textDocumentSymbols(
TextDocumentIdentifier documentId) async =>
[];
Future<List<SymbolInformation>> workspaceSymbol(String query) async => [];
Future<dynamic> textDocumentHover(
TextDocumentIdentifier documentId, Position position) async =>
null;
Future<List<dynamic /*Command|CodeAction*/ >> textDocumentCodeAction(
TextDocumentIdentifier documentId,
Range range,
CodeActionContext context) async =>
[];
Future<void> workspaceExecuteCommand(
String command, List<dynamic> arguments) async {}
Future<WorkspaceEdit> textDocumentRename(TextDocumentIdentifier documentId,
Position position, String newName) async =>
null;
Stream<Diagnostics> get diagnostics => Stream.empty();
Stream<ApplyWorkspaceEditParams> get workspaceEdits => Stream.empty();
Stream<ShowMessageParams> get showMessages => Stream.empty();
Stream<ShowMessageParams> get logMessages => Stream.empty();
void setupExtraMethods(Peer peer) {}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,321 @@
TextDocumentItem:
uri: String
text: String
languageId: String
version: int
TextDocumentIdentifier:
uri: String
VersionedTextDocumentIdentifier:
uri: String
version: int
TextDocumentContentChangeEvent:
range: Range
rangeLength: int
text: String
Range:
start: Position
end: Position
Position:
line: int
character: int
Diagnostics:
uri: String
diagnostics:
listType: Diagnostic
Diagnostic:
range: Range
severity: int
code: dynamic
source: String
message: String
CompletionList:
isIncomplete: bool
items:
listType: CompletionItem
CompletionItem:
label: String
kind: CompletionItemKind
detail: String
documentation: String
sortText: String
filterText: String
insertText: String
insertTextFormat: InsertTextFormat
textEdit: TextEdit
additionalTextEdits:
listType: TextEdit
command: Command
data: dynamic
CompletionItemKind:
enumValues:
text: 1
method: 2
function: 3
constructor: 4
field: 5
variable: 6
classKind: 7
interface: 8
module: 9
property: 10
unit: 11
value: 12
enumKind: 13
keyword: 14
snippet: 15
color: 16
file: 17
reference: 18
wireType: int
InsertTextFormat:
enumValues:
plainText: 1
snippet: 2
wireType: int
TextEdit:
range: Range
newText: String
Command:
title: String
command: String
arguments:
listType: dynamic
Location:
uri: String
range: Range
DynamicRegistrationCapability:
dynamicRegistration: bool
WorkspaceClientCapabilities:
applyEdit: bool
didChangeConfiguration: DynamicRegistrationCapability
didChangeWatchedFiles: DynamicRegistrationCapability
symbol: DynamicRegistrationCapability
executeCommand: DynamicRegistrationCapability
SynchronizationCapabilities:
dynamicRegistration: bool
willSave: bool
willSaveWaitUntil: bool
didSave: bool
CompletionItemCapabilities:
snippetSupport: bool
CompletionCapabilities:
dynamicRegistration: bool
completionItem: CompletionItemCapabilities
HoverCapabilities:
dynamicRegistration: bool
contentFormat:
listType: String
CodeActionCapabilities:
dynamicRegistration: bool
codeActionLiteralSupport: CodeActionLiteralSupport
CodeActionLiteralSupport:
codeActionKind: CodeActionKinds
CodeActionKinds:
valueSet:
listType: String # open ended enum
TextDocumentClientCapabilities:
codeAction: CodeActionCapabilities
completion: CompletionCapabilities
hover: HoverCapabilities
synchronization: SynchronizationCapabilities
codeLens: DynamicRegistrationCapability
definition: DynamicRegistrationCapability
documentHighlight: DynamicRegistrationCapability
documentLink: DynamicRegistrationCapability
documentSymbol: DynamicRegistrationCapability
formatting: DynamicRegistrationCapability
onTypeFormatting: DynamicRegistrationCapability
references: DynamicRegistrationCapability
rename: DynamicRegistrationCapability
ClientCapabilities:
workspace: WorkspaceClientCapabilities
textDocument: TextDocumentClientCapabilities
TextDocumentSyncKind:
enumValues:
none: 0
full: 1
incremental: 2
wireType: int
CompletionOptions:
resolveProvider: bool
triggerCharacters:
listType: String
SignatureHelpOptions:
triggerCharacters:
listType: String
CodeLensOptions:
resolveProvider: bool
DocumentOnTypeFormattingOptions:
firstTriggerCharacter: String
moreTriggerCharacter:
listType: String
DocumentLinkOptions:
resolveProvider: bool
ExecuteCommandOptions:
commands:
listType: String
SaveOptions:
includeText: bool
TextDocumentSyncOptions:
openClose: bool
change: TextDocumentSyncKind
willSave: bool
willSaveWaitUntil: bool
save: SaveOptions
ServerCapabilities:
codeActionProvider: bool
codeLensProvider: CodeLensOptions
completionProvider: CompletionOptions
definitionProvider: bool
documentFormattingProvider: bool
documentHighlightProvider: bool
documentLinkProvider: DocumentLinkOptions
documentOnTypeFormattingProvider: DocumentOnTypeFormattingOptions
documentRangeFormattingProvider: bool
documentSymbolProvider: bool
executeCommandProvider: ExecuteCommandOptions
hoverProvider: bool
implementationProvider: bool
referencesProvider: bool
renameProvider: bool
signatureHelpProvider: SignatureHelpOptions
textDocumentSync: TextDocumentSyncOptions
workspaceSymbolProvider: bool
ReferenceContext:
includeDeclaration: bool
Hover:
contents: String
range: Range
HoverMarkup:
contents: MarkupContent
range: Range
CodeActionContext:
diagnostics:
listType: Diagnostic
CodeAction:
title: String
kind: String
diagnostics:
listType: Diagnostic
edit: WorkspaceEdit
command: Command
ApplyWorkspaceEditParams:
label: String
edit: WorkspaceEdit
WorkspaceEdit:
# Not using `documentChanges` since there is no reasonable way to support text
# document version
changes:
mapType:
listType: TextEdit
DocumentHighlight:
range: Range
kind: DocumentHighlightKind
DocumentHighlightKind:
enumValues:
text: 1
read: 2
write: 3
wireType: int
SymbolInformation:
name: String
kind: SymbolKind
location: Location
containerName: String
SymbolKind:
enumValues:
file: 1
module: 2
namespace: 3
package: 4
classSymbol: 5
method: 6
property: 7
field: 8
constructor: 9
enumSymbol: 10
interface: 11
function: 12
variable: 13
constant: 14
string: 15
number: 16
boolean: 17
array: 18
object: 19
key: 20
nullSymbol: 21
enumMember: 22
struct: 23
event: 24
operator: 25
typeParameter: 26
wireType: int
MarkupContentKind:
enumValues:
plaintext: 'plaintext'
markdown: 'markdown'
wireType: String
MarkupContent:
kind: MarkupContentKind
value: String
MessageType:
enumValues:
error: 1
warning: 2
info: 3
log: 4
wireType: int
ShowMessageParams:
type: MessageType
message: String

View file

@ -0,0 +1,201 @@
import 'dart:async';
import 'dart:io';
import 'package:json_rpc_2/json_rpc_2.dart';
import 'interface.dart';
import 'messages.dart';
import 'wireformat.dart';
/// A Language Server communicating over stdin and stdout.
class StdIOLanguageServer {
final LanguageServer _server;
Future<void> onDone;
/// Wrap [_server] and register RPC methods using the LSP wire protocol.
///
/// Methods are guarded against being called before the server is initialized.
StdIOLanguageServer.start(this._server) {
final peer = Peer(lspChannel(stdin, stdout));
_lifecycleMethods(peer);
_fileHandlingMethods(peer);
_notifications(peer);
_completionMethods(peer);
_referenceMethods(peer);
_codeActionMethods(peer);
_server.setupExtraMethods(peer);
peer.listen();
onDone = _server.onDone.then((_) => peer.close()).then((_) => null);
}
bool _isInitialized = false;
void _lifecycleMethods(Peer peer) {
peer
..registerMethod('initialize', (params) async {
final serverCapabilities = await _server.initialize(
params['processId'].valueOr(0) as int,
params['rootUri'].valueOr('') as String,
ClientCapabilities.fromJson(params['capabilities'].value as Map),
params['trace'].valueOr('off') as String);
_isInitialized = true;
return {'capabilities': serverCapabilities.toJson()};
})
..registerMethod('initialized', (params) => _server.initialized())
..registerMethod('shutdown', _server.shutdown)
..registerMethod('exit', _server.exit);
}
/// Register a request that will throw if throw if used before initialization.
void _registerRequest(Peer peer, String methodName, Function callback) {
peer.registerMethod(methodName, (params) {
if (!_isInitialized) {
throw RpcException(-32003, 'The server has not been initialized');
}
return callback(params);
});
}
/// Notifications are ignored until after initialization.
void _registerNotification(Peer peer, String methodName, Function callback) {
peer.registerMethod(methodName, (params) {
if (_isInitialized) return callback(params);
});
}
void _fileHandlingMethods(Peer peer) {
_registerNotification(peer, 'textDocument/didOpen', (params) {
_server.textDocumentDidOpen(_documentItem(params));
});
_registerNotification(peer, 'textDocument/didChange', (params) {
_server.textDocumentDidChange(
_versionedDocument(params), _contentChanges(params));
});
_registerNotification(peer, 'textDocument/didClose', (params) {
_server.textDocumentDidClose(_document(params));
});
}
void _notifications(Peer peer) {
_server
..diagnostics.map((d) => d.toJson()).forEach((diagnostics) =>
peer.sendNotification('textDocument/publishDiagnostics', diagnostics))
..workspaceEdits.map((e) => e.toJson()).forEach((edit) {
// Ignore response?
peer.sendRequest('workspace/applyEdit', edit);
})
..logMessages.map((e) => e.toJson()).forEach(
(message) => peer.sendNotification('window/logMessage', message))
..showMessages.map((e) => e.toJson()).forEach(
(message) => peer.sendNotification('window/showMessage', message));
}
void _completionMethods(Peer peer) {
_registerRequest(
peer,
'textDocument/completion',
(params) => _server
.textDocumentCompletion(_document(params), _position(params))
.then((r) => r.toJson()));
}
void _referenceMethods(Peer peer) {
_registerRequest(
peer,
'textDocument/definition',
(params) => _server
.textDocumentDefinition(_document(params), _position(params))
.then((r) => r?.toJson()));
_registerRequest(
peer,
'textDocument/hover',
(params) => _server
.textDocumentHover(_document(params), _position(params))
.then((r) => r?.toJson()));
_registerRequest(
peer,
'textDocument/references',
(params) => _server
.textDocumentReferences(
_document(params), _position(params), _referenceContext(params))
.then((r) => r?.map((e) => e.toJson())?.toList()));
_registerRequest(
peer,
'textDocument/implementation',
(params) => _server
.textDocumentImplementation(_document(params), _position(params))
.then((r) => r?.map((e) => e.toJson())?.toList()));
_registerRequest(
peer,
'textDocument/documentHighlight',
(params) => _server
.textDocumentHighlight(_document(params), _position(params))
.then((r) => r?.map((e) => e.toJson())?.toList()));
_registerRequest(
peer,
'textDocument/documentSymbol',
(params) => _server
.textDocumentSymbols(_document(params))
.then((r) => r?.map((e) => e.toJson())?.toList()));
_registerRequest(
peer,
'workspace/symbol',
(params) => _server
.workspaceSymbol(_query(params))
.then((r) => r?.map((e) => e.toJson())?.toList()));
}
void _codeActionMethods(Peer peer) {
_registerRequest(
peer,
'textDocument/codeAction',
(params) => _server
.textDocumentCodeAction(
_document(params), _range(params), _codeActionContext(params))
.then((r) => r?.map((e) => e.toJson())?.toList()));
_registerRequest(
peer,
'workspace/executeCommand',
(params) => _server.workspaceExecuteCommand(
params['command'].value as String,
params['arguments']?.value as List));
_registerRequest(
peer,
'textDocument/rename',
(params) async => (await _server.textDocumentRename(_document(params),
_position(params), params['newName'].value as String))
.toJson());
}
}
TextDocumentItem _documentItem(params) =>
TextDocumentItem.fromJson(params['textDocument'].value as Map);
VersionedTextDocumentIdentifier _versionedDocument(params) =>
VersionedTextDocumentIdentifier.fromJson(
params['textDocument'].value as Map);
TextDocumentIdentifier _document(params) =>
TextDocumentIdentifier.fromJson(params['textDocument'].value as Map);
Range _range(params) => Range.fromJson(params['range'].value as Map);
Position _position(params) =>
Position.fromJson(params['position'].value as Map);
CodeActionContext _codeActionContext(params) =>
CodeActionContext.fromJson(params['context'].value as Map);
ReferenceContext _referenceContext(params) =>
ReferenceContext.fromJson(params['context'].value as Map);
List<TextDocumentContentChangeEvent> _contentChanges(params) =>
(params['contentChanges'].value as Iterable)
.map((change) => TextDocumentContentChangeEvent.fromJson(change as Map))
.toList();
String _query(params) => params['query'].value as String;

View file

@ -0,0 +1,98 @@
import 'dart:async';
import 'dart:convert';
import 'package:stream_channel/stream_channel.dart';
import 'package:async/async.dart';
StreamChannel<String> lspChannel(
Stream<List<int>> stream, StreamSink<List<int>> sink) {
final parser = _Parser(stream);
final outSink = StreamSinkTransformer.fromHandlers(
handleData: _serialize,
handleDone: (sink) {
sink.close();
parser.close();
}).bind(sink);
return StreamChannel.withGuarantees(parser.stream, outSink);
}
void _serialize(String data, EventSink<List<int>> sink) {
final message = utf8.encode(data);
final header = 'Content-Length: ${message.length}\r\n\r\n';
sink.add(ascii.encode(header));
for (var chunk in _chunks(message, 1024)) {
sink.add(chunk);
}
}
class _Parser {
final _streamCtl = StreamController<String>();
Stream<String> get stream => _streamCtl.stream;
final _buffer = <int>[];
bool _headerMode = true;
int _contentLength = -1;
StreamSubscription _subscription;
_Parser(Stream<List<int>> stream) {
_subscription =
stream.expand((bytes) => bytes).listen(_handleByte, onDone: () {
_streamCtl.close();
});
}
Future<void> close() => _subscription.cancel();
void _handleByte(int byte) {
_buffer.add(byte);
if (_headerMode && _headerComplete) {
_contentLength = _parseContentLength();
_buffer.clear();
_headerMode = false;
} else if (!_headerMode && _messageComplete) {
_streamCtl.add(utf8.decode(_buffer));
_buffer.clear();
_headerMode = true;
}
}
/// Whether the entire message is in [_buffer].
bool get _messageComplete => _buffer.length >= _contentLength;
/// Decodes [_buffer] into a String and looks for the 'Content-Length' header.
int _parseContentLength() {
final asString = ascii.decode(_buffer);
final headers = asString.split('\r\n');
final lengthHeader =
headers.firstWhere((h) => h.startsWith('Content-Length'));
final length = lengthHeader.split(':').last.trim();
return int.parse(length);
}
/// Whether [_buffer] ends in '\r\n\r\n'.
bool get _headerComplete {
final l = _buffer.length;
return l > 4 &&
_buffer[l - 1] == 10 &&
_buffer[l - 2] == 13 &&
_buffer[l - 3] == 10 &&
_buffer[l - 4] == 13;
}
}
Iterable<List<T>> _chunks<T>(List<T> data, int chunkSize) sync* {
if (data.length <= chunkSize) {
yield data;
return;
}
var low = 0;
while (low < data.length) {
if (data.length > low + chunkSize) {
yield data.sublist(low, low + chunkSize);
} else {
yield data.sublist(low);
}
low += chunkSize;
}
}

View file

@ -1,6 +1,6 @@
import 'dart:async';
import 'package:dart_language_server/src/protocol/language_server/interface.dart';
import 'package:dart_language_server/src/protocol/language_server/messages.dart';
//import 'package:dart_language_server/src/protocol/language_server/interface.dart';
//import 'package:dart_language_server/src/protocol/language_server/messages.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:file/memory.dart';
@ -13,6 +13,8 @@ import 'package:string_scanner/string_scanner.dart';
import 'package:symbol_table/symbol_table.dart';
import 'analyzer.dart';
import 'object.dart';
import 'protocol/language_server/interface.dart';
import 'protocol/language_server/messages.dart';
class JaelLanguageServer extends LanguageServer {
var _diagnostics = new StreamController<Diagnostics>();

View file

@ -4,14 +4,16 @@ description: Language Server Protocol implementation for the Jael templating eng
author: Tobe Osakwe <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/vscode
environment:
sdk: ">=2.0.0-dev <3.0.0"
sdk: '>=2.10.0 <2.12.0'
dependencies:
args: ^1.0.0
dart_language_server: ^0.1.3
# dart_language_server: ^0.1.16
file: ^5.0.0
io: ^0.3.2
jael: ^2.0.0
jael_preprocessor: ^2.0.0
jael: #^2.0.0
path: ../jael
jael_preprocessor: #^2.0.0
path: ../jael_preprocessor
json_rpc_2: ^2.0.0
logging: ^0.11.3
path: ^1.0.0
@ -20,3 +22,4 @@ dependencies:
symbol_table: ^2.0.0
executables:
jael_language_server: jael_language_server

View file

@ -1,14 +1,19 @@
name: jael_preprocessor
version: 2.0.1
version: 3.0.0
description: A pre-processor for resolving blocks and includes within Jael templates.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/jael/tree/master/jael_preprocessor
publish_to: none
environment:
sdk: ">=2.0.0-dev <3.0.0"
sdk: '>=2.10.0 <3.0.0'
dependencies:
file: ^5.0.0
jael: ^2.0.0
file: ^6.1.0
jael:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x
path: packages/jael/jael
symbol_table: ^2.0.0
dev_dependencies:
code_buffer:
test: ^1.0.0
test: ^1.15.7

View file

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
@ -206,6 +207,25 @@ class BuildSystemFile extends File {
Directory get parent => BuildSystemDirectory(
fileSystem, reader, package, fileSystem.path.dirname(path));
@override
Future<Uint8List> readAsBytes() {
// TODO: implement readAsBytes
throw UnimplementedError();
}
@override
Uint8List readAsBytesSync() {
// TODO: implement readAsBytesSync
throw UnimplementedError();
}
@override
Future<List<String>> readAsLines({Encoding encoding = utf8}) {
// TODO: implement readAsLines
throw UnimplementedError();
}
/*
@override
Future<List<int>> readAsBytes() {
var assetId = AssetId(package, path);
@ -217,7 +237,7 @@ class BuildSystemFile extends File {
@override
Future<List<String>> readAsLines({Encoding encoding = utf8}) =>
throw _unsupported();
*/
@override
List<String> readAsLinesSync({Encoding encoding = utf8}) =>
throw _unsupported();

View file

@ -2,13 +2,15 @@ name: jael_web
version: 0.0.0
description: Experimental virtual DOM/SPA engine built on Jael. Supports SSR.
environment:
sdk: ">=2.0.0 <3.0.0"
sdk: '>=2.10.0 <2.12.0'
dependencies:
build: ^1.0.0
build_config: ^0.3.0
code_builder: ^3.0.0
jael: ^2.0.0
jael_preprocessor: ^2.0.0
jael: #^2.0.0
path: ../jael
jael_preprocessor: #^2.0.0
path: ../jael_preprocessor
source_gen: ^0.9.0
dev_dependencies:
build_runner: ^1.0.0

View file

@ -40,7 +40,7 @@ AngelConfigurer jinja({
varClose: varClose,
commentOpen: commentOpen,
commentClose: commentClose,
defaultValue: defaultValue,
//defaultValue: defaultValue,
autoReload: autoReload,
filters: filters,
tests: tests,

View file

@ -4,12 +4,14 @@ version: 1.0.0-rc.0
homepage: https://github.com/angel-dart/jinja
author: Tobe O <thosakwe@gmail.com>
environment:
sdk: '>=2.0.0-dev <3.0.0'
sdk: '>=2.10.0 <2.12.0'
dependencies:
angel_framework: ^2.0.0-alpha
angel_framework: #^2.0.0-alpha
path: ../framework
jinja: ^0.0.4
dev_dependencies:
angel_test: ^2.0.0
angel_test: #^2.0.0
path: ../test
path: ^1.0.0
pedantic: ^1.0.0
test: ^1.0.0
test: ^1.15.7

84
packages/json_god/.gitignore vendored Normal file
View file

@ -0,0 +1,84 @@
# See https://www.dartlang.org/tools/private-files.html
.idea
**/bower_components/
# Files and directories created by pub
.buildlog
.packages
.project
.pub/
build/
**/packages/
# Files created by dart2js
# (Most Dart developers will use pub build to compile Dart, use/modify these
# rules if you intend to use dart2js directly
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
# differentiate from explicit Javascript files)
# *.dart.js
*.part.js
*.js.deps
# *.js.map
*.info.json
# Directory created by dartdoc
doc/
# Don't commit pubspec lock file
# (Library packages only! Remove pattern if developing an application package)
pubspec.lock
### Dart template
# See https://www.dartlang.org/tools/private-files.html
# Files and directories created by pub
# Files created by dart2js
# (Most Dart developers will use pub build to compile Dart, use/modify these
# rules if you intend to use dart2js directly
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
# differentiate from explicit Javascript files)
*.dart.js
# Directory created by dartdoc
# Don't commit pubspec lock file
# (Library packages only! Remove pattern if developing an application package)
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/tasks.xml
.idea/dictionaries
.idea/jsLibraryMappings.xml
# Sensitive or high-churn files:
.idea/dataSources.ids
.idea/dataSources.xml
.idea/dataSources.local.xml
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
# Gradle:
.idea/gradle.xml
# Mongo Explorer plugin:
.idea/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

View file

@ -0,0 +1,4 @@
language: dart
dart:
- dev
- stable

View file

@ -0,0 +1,8 @@
# 2.0.0-beta+3
* Long-needed updates, ensured Dart 2 compatibility, fixed DDC breakages.
* Patches for reflection bugs with typing.
# 2.0.0-beta+2
* This version breaks in certain Dart versions (likely anything *after* `2.0.0-dev.59.0`)
until https://github.com/dart-lang/sdk/issues/33594 is resolved.
* Removes the reference to `Schema` class.

21
packages/json_god/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Tobe O
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

113
packages/json_god/README.md Normal file
View file

@ -0,0 +1,113 @@
# JSON God v2
[![Pub](https://img.shields.io/pub/v/json_god.svg)](https://pub.dartlang.org/packages/json_god)
[![build status](https://travis-ci.org/thosakwe/json_god.svg)](https://travis-ci.org/thosakwe/json_god)
The ***new and improved*** definitive solution for JSON in Dart.
# Installation
dependencies:
json_god: ^2.0.0-beta
# Usage
It is recommended to import the library under an alias, i.e., `god`.
```dart
import 'package:json_god/json_god.dart' as god;
```
## Serializing JSON
Simply call `god.serialize(x)` to synchronously transform an object into a JSON
string.
```dart
Map map = {"foo": "bar", "numbers": [1, 2, {"three": 4}]};
// Output: {"foo":"bar","numbers":[1,2,{"three":4]"}
String json = god.serialize(map);
print(json);
```
You can easily serialize classes, too. JSON God also supports classes as members.
```dart
class A {
String foo;
A(this.foo);
}
class B {
String hello;
A nested;
B(String hello, String foo) {
this.hello = hello;
this.nested = new A(foo);
}
}
main() {
God god = new God();
print(god.serialize(new B("world", "bar")));
}
// Output: {"hello":"world","nested":{"foo":"bar"}}
```
If a class has a `toJson` method, it will be called instead.
## Deserializing JSON
Deserialization is equally easy, and is provided through `god.deserialize`.
```dart
Map map = god.deserialize('{"hello":"world"}');
int three = god.deserialize("3");
```
### Deserializing to Classes
JSON God lets you deserialize JSON into an instance of any type. Simply pass the
type as the second argument to `god.deserialize`.
If the class has a `fromJson` constructor, it will be called instead.
```dart
class Child {
String foo;
}
class Parent {
String hello;
Child child = new Child();
}
main() {
God god = new God();
Parent parent = god.deserialize('{"hello":"world","child":{"foo":"bar"}}', Parent);
print(parent);
}
```
**Any JSON-deserializable classes must initializable without parameters.
If `new Foo()` would throw an error, then you can't use Foo with JSON.**
This allows for validation of a sort, as only fields you have declared will be
accepted.
```dart
class HasAnInt { int theInt; }
HasAnInt invalid = god.deserialize('["some invalid input"]', HasAnInt);
// Throws an error
```
An exception will be thrown if validation fails.
# Thank you for using JSON God
Thank you for using this library. I hope you like it.
Feel free to follow me on Twitter:
[@thosakwe](http://twitter.com/thosakwe)
Or, check out [my blog](https://thosakwe.com)

View file

@ -0,0 +1,3 @@
analyzer:
strong-mode:
implicit-casts: false

View file

@ -0,0 +1,17 @@
/// A robust library for JSON serialization and deserialization.
library json_god;
import 'package:dart2_constant/convert.dart';
import 'package:logging/logging.dart';
import 'src/reflection.dart' as reflection;
part 'src/serialize.dart';
part 'src/deserialize.dart';
part 'src/validation.dart';
part 'src/util.dart';
/// Instead, listen to [logger].
@deprecated
bool debug = false;
final Logger logger = new Logger('json_god');

View file

@ -0,0 +1,43 @@
part of json_god;
/// Deserializes a JSON string into a Dart datum.
///
/// You can also provide an output Type to attempt to serialize the JSON into.
deserialize(String json, {Type outputType}) {
var deserialized = deserializeJson(json, outputType: outputType);
logger.info("Deserialization result: $deserialized");
return deserialized;
}
/// Deserializes JSON into data, without validating it.
deserializeJson(String s, {Type outputType}) {
logger.info("Deserializing the following JSON: $s");
if (outputType == null) {
logger.info("No output type was specified, so we are just using json.decode");
return json.decode(s);
} else {
logger.info("Now deserializing to type: $outputType");
return deserializeDatum(json.decode(s), outputType: outputType);
}
}
/// Deserializes some JSON-serializable value into a usable Dart value.
deserializeDatum(value, {Type outputType}) {
if (outputType != null) {
return reflection.deserialize(value, outputType, deserializeDatum);
} else if (value is List) {
logger.info("Deserializing this List: $value");
return value.map(deserializeDatum).toList();
} else if (value is Map) {
logger.info("Deserializing this Map: $value");
Map result = {};
value.forEach((k, v) {
result[k] = deserializeDatum(v);
});
return result;
} else if (_isPrimitive(value)) {
logger.info("Value $value is a primitive");
return value;
}
}

View file

@ -0,0 +1,191 @@
library json_god.reflection;
import 'dart:mirrors';
import 'package:json_god/json_god.dart';
const Symbol hashCodeSymbol = #hashCode;
const Symbol runtimeTypeSymbol = #runtimeType;
typedef Serializer(value);
typedef Deserializer(value, {Type outputType});
List<Symbol> _findGetters(ClassMirror classMirror) {
List<Symbol> result = [];
classMirror.instanceMembers
.forEach((Symbol symbol, MethodMirror methodMirror) {
if (methodMirror.isGetter &&
symbol != hashCodeSymbol &&
symbol != runtimeTypeSymbol) {
logger.info("Found getter on instance: $symbol");
result.add(symbol);
}
});
return result;
}
serialize(value, Serializer serializer, [@deprecated bool debug = false]) {
logger.info("Serializing this value via reflection: $value");
Map result = {};
InstanceMirror instanceMirror = reflect(value);
ClassMirror classMirror = instanceMirror.type;
// Check for toJson
for (Symbol symbol in classMirror.instanceMembers.keys) {
if (symbol == #toJson) {
logger.info("Running toJson...");
var result = instanceMirror.invoke(symbol, []).reflectee;
logger.info("Result of serialization via reflection: $result");
return result;
}
}
for (Symbol symbol in _findGetters(classMirror)) {
String name = MirrorSystem.getName(symbol);
var valueForSymbol = instanceMirror.getField(symbol).reflectee;
try {
result[name] = serializer(valueForSymbol);
logger.info("Set $name to $valueForSymbol");
} catch (e, st) {
logger.severe("Could not set $name to $valueForSymbol", e, st);
}
}
logger.info("Result of serialization via reflection: $result");
return result;
}
deserialize(value, Type outputType, Deserializer deserializer,
[@deprecated bool debug = false]) {
logger.info("About to deserialize $value to a $outputType");
try {
if (value is List) {
List<TypeMirror> typeArguments = reflectType(outputType).typeArguments;
Iterable it;
if (typeArguments.isEmpty) {
it = value.map(deserializer);
} else {
it = value.map((item) =>
deserializer(item, outputType: typeArguments[0].reflectedType));
}
if (typeArguments.isEmpty) return it.toList();
logger.info('Casting list elements to ${typeArguments[0]
.reflectedType} via List.from');
var mirror = reflectType(List, [typeArguments[0].reflectedType]);
if (mirror is ClassMirror) {
var output = mirror.newInstance(#from, [it]).reflectee;
logger.info('Casted list type: ${output.runtimeType}');
return output;
} else {
throw new ArgumentError(
'${typeArguments[0].reflectedType} is not a class.');
}
} else if (value is Map)
return _deserializeFromJsonByReflection(value, deserializer, outputType);
else
return deserializer(value);
} catch (e, st) {
logger.severe('Deserialization failed.', e, st);
rethrow;
}
}
/// Uses mirrors to deserialize an object.
_deserializeFromJsonByReflection(
data, Deserializer deserializer, Type outputType,
[@deprecated bool debug = false]) {
// Check for fromJson
var typeMirror = reflectType(outputType);
if (typeMirror is! ClassMirror) {
throw new ArgumentError('$outputType is not a class.');
}
var type = typeMirror as ClassMirror;
var fromJson =
new Symbol('${MirrorSystem.getName(type.simpleName)}.fromJson');
for (Symbol symbol in type.declarations.keys) {
if (symbol == fromJson) {
var decl = type.declarations[symbol];
if (decl is MethodMirror && decl.isConstructor) {
logger.info("Running fromJson...");
var result = type.newInstance(#fromJson, [data]).reflectee;
logger.info("Result of deserialization via reflection: $result");
return result;
}
}
}
ClassMirror classMirror = type;
InstanceMirror instanceMirror = classMirror.newInstance(new Symbol(""), []);
if (classMirror.isSubclassOf(reflectClass(Map))) {
var typeArguments = classMirror.typeArguments;
if (typeArguments.isEmpty ||
classMirror.typeArguments
.every((t) => t == currentMirrorSystem().dynamicType)) {
return data;
} else {
var mapType =
reflectType(Map, typeArguments.map((t) => t.reflectedType).toList())
as ClassMirror;
logger.info('Casting this map $data to Map of [$typeArguments]');
var output = mapType.newInstance(new Symbol(''), []).reflectee;
for (var key in data.keys) {
output[key] = data[key];
}
logger.info('Output: $output of type ${output.runtimeType}');
return output;
}
} else {
data.keys.forEach((key) {
try {
logger.info("Now deserializing value for $key");
logger.info("data[\"$key\"] = ${data[key]}");
var deserializedValue = deserializer(data[key]);
logger.info("I want to set $key to the following ${deserializedValue
.runtimeType}: $deserializedValue");
// Get target type of getter
Symbol searchSymbol = new Symbol(key.toString());
Symbol symbolForGetter = classMirror.instanceMembers.keys
.firstWhere((x) => x == searchSymbol);
Type requiredType = classMirror
.instanceMembers[symbolForGetter].returnType.reflectedType;
if (data[key].runtimeType != requiredType) {
logger.info("Currently, $key is a ${data[key].runtimeType}.");
logger.info("However, $key must be a $requiredType.");
deserializedValue =
deserializer(deserializedValue, outputType: requiredType);
}
logger.info(
"Final deserialized value for $key: $deserializedValue <${deserializedValue
.runtimeType}>");
instanceMirror.setField(new Symbol(key.toString()), deserializedValue);
logger.info("Success! $key has been set to $deserializedValue");
} catch (e, st) {
logger.severe('Could not set value for field $key.', e, st);
}
});
}
return instanceMirror.reflectee;
}

View file

@ -0,0 +1,35 @@
part of json_god;
/// Serializes any arbitrary Dart datum to JSON. Supports schema validation.
String serialize(value) {
var serialized = serializeObject(value);
logger.info('Serialization result: $serialized');
return json.encode(serialized);
}
/// Transforms any Dart datum into a value acceptable to json.encode.
serializeObject(value) {
if (_isPrimitive(value)) {
logger.info("Serializing primitive value: $value");
return value;
} else if (value is DateTime) {
logger.info("Serializing this DateTime: $value");
return value.toIso8601String();
} else if (value is Iterable) {
logger.info("Serializing this Iterable: $value");
return value.map(serializeObject).toList();
} else if (value is Map) {
logger.info("Serializing this Map: $value");
return serializeMap(value);
} else
return serializeObject(reflection.serialize(value, serializeObject));
}
/// Recursively transforms a Map and its children into JSON-serializable data.
Map serializeMap(Map value) {
Map outputMap = {};
value.forEach((key, value) {
outputMap[key] = serializeObject(value);
});
return outputMap;
}

View file

@ -0,0 +1,5 @@
part of json_god;
bool _isPrimitive(value) {
return value is num || value is bool || value is String || value == null;
}

Some files were not shown because too many files have changed in this diff Show more