optional expose

This commit is contained in:
thosakwe 2019-07-17 15:26:34 -04:00
parent 91bc517b1f
commit 0a9b5c118f
4 changed files with 72 additions and 8 deletions

View file

@ -1,4 +1,5 @@
# 2.0.5
* Make `@Expose()` in `Controller` optional. https://github.com/angel-dart/angel/issues/107
* Add `allowHttp1` to `AngelHttp2` constructors. https://github.com/angel-dart/angel/issues/108
* Add `deserializeBody` and `decodeBody` to `RequestContext`. https://github.com/angel-dart/angel/issues/109
* Add `HostnameRouter`, which allows for routing based on hostname. https://github.com/angel-dart/angel/issues/110

View file

@ -4,7 +4,7 @@ import 'dart:async';
import 'package:angel_container/angel_container.dart';
import 'package:angel_route/angel_route.dart';
import 'package:meta/meta.dart';
import 'package:recase/recase.dart';
import '../core/core.dart';
/// Supports grouping routes with shared functionality.
@ -86,7 +86,15 @@ class Controller {
.map((m) => m.reflectee)
.firstWhere((r) => r is Expose, orElse: () => null) as Expose;
if (exposeDecl == null) return;
if (exposeDecl == null) {
// If this has a @noExpose, return null.
if (decl.function.annotations.any((m) => m.reflectee is NoExpose)) {
return;
} else {
// Otherwise, create an @Expose.
exposeDecl = Expose(null);
}
}
var reflectedMethod =
instanceMirror.getField(methodName).reflectee as Function;
@ -117,6 +125,30 @@ class Controller {
injection.optional?.addAll(exposeDecl.allowNull);
}
// If there is no path, reverse-engineer one.
var path = exposeDecl.path;
if (path == null) {
var parts = <String>[];
parts.add(ReCase(method.name).snakeCase.replaceAll(_multiScore, '_'));
// Try to infer String, int, or double.
for (var p in injection.required) {
if (p is List && p.length == 2 && p[0] is String && p[1] is Type) {
var name = p[0] as String;
var type = p[1] as Type;
if (type == String) {
parts.add(':$name');
} else if (type == int) {
parts.add('int:$name');
} else if (type == double) {
parts.add('double:$name');
}
}
}
path = parts.join('/');
}
routeMappings[name] = routable.addRoute(exposeDecl.method,
exposeDecl.path, handleContained(reflectedMethod, injection),
middleware: middleware);
@ -127,10 +159,22 @@ class Controller {
/// Used to add additional routes to the router from within a [Controller].
void configureRoutes(Routable routable) {}
static final RegExp _multiScore = RegExp(r'__+');
/// Finds the [Expose] declaration for this class.
Expose findExpose(Reflector reflector) => reflector
Expose findExpose(Reflector reflector, {bool concreteOnly = false}) {
var existing = reflector
.reflectClass(runtimeType)
.annotations
.map((m) => m.reflectee)
.firstWhere((r) => r is Expose, orElse: () => null) as Expose;
return existing ??
(concreteOnly
? null
: Expose(ReCase(runtimeType.toString())
.snakeCase
.replaceAll('_controller', '')
.replaceAll('_ctrl', '')
.replaceAll(_multiScore, '_')));
}
}

View file

@ -21,7 +21,14 @@ class Hooks {
const Hooks({this.before = const [], this.after = const []});
}
/// Exposes a [Controller] to the Internet.
/// Specifies to NOT expose a method to the Internet.
class NoExpose {
const NoExpose();
}
const NoExpose noExpose = NoExpose();
/// Exposes a [Controller] or method to the Internet.
class Expose {
final String method;
final String path;
@ -29,11 +36,22 @@ class Expose {
final String as;
final List<String> allowNull;
static const Expose get = Expose(null, method: 'GET'),
post = Expose(null, method: 'POST'),
patch = Expose(null, method: 'PATCH'),
put = Expose(null, method: 'PUT'),
delete = Expose(null, method: 'DELETE'),
head = Expose(null, method: 'HEAD');
const Expose(this.path,
{this.method = "GET",
this.middleware = const [],
this.as,
this.allowNull = const []});
const Expose.method(this.method,
{this.middleware, this.as, this.allowNull = const []})
: path = null;
}
/// Used to apply special dependency injections or functionality to a function parameter.

View file

@ -25,6 +25,7 @@ dependencies:
path: ^1.0.0
pedantic: ^1.0.0
quiver_hashcode: ^2.0.0
recase: ^2.0.0
stack_trace: ^1.0.0
tuple: ^1.0.0
uuid: ^2.0.0-rc.1