From 17f34da654cf3552b6da0c43c77167b16fff4a6d Mon Sep 17 00:00:00 2001 From: Ravi Teja Gudapati Date: Tue, 8 Nov 2016 00:05:28 +0100 Subject: [PATCH 1/6] Added some comments --- README.md | 4 ++-- lib/src/annotations/inputs.dart | 13 +++++++++++-- pubspec.yaml | 7 ++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1ecbf4b3..e94b06e7 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ Jaguar, a server framework built for **speed, simplicity and extensiblity**. 1. Keeps your route handlers concise and clean 2. Bare metal speed achieved through code generation 3. Extensible interceptor infrastructure -4. Generates API console client to try your server without writing single line of -client code +4. Generates API console client to try your server without writing single +line of client code 5. Mock HTTP requests and use dependency injection to test your API 6. Optional Firebase/Parse like no code or little code servers diff --git a/lib/src/annotations/inputs.dart b/lib/src/annotations/inputs.dart index 7d0e754e..a7ba4a2c 100644 --- a/lib/src/annotations/inputs.dart +++ b/lib/src/annotations/inputs.dart @@ -1,35 +1,44 @@ part of jaguar.src.annotations; -/// Defines inputs to an interceptor +/// Annotation to request inputs from another interceptor in request chain class Input { + /// Defines an interceptor, whose response must be used as input final Type resultFrom; + /// Identifier to identify an interceptor from interceptors of same type final String id; const Input(this.resultFrom, {this.id}); } +/// Annotation to request input of Http header class InputHeader { + /// Key of the header final String key; const InputHeader(this.key); } +/// Annotation to request input of Http headers class InputHeaders { const InputHeaders(); } + +/// Annotation to request input of cookie class InputCookie { + /// Key of the cookie final String key; const InputCookie(this.key); } +/// Annotation to request input of cookies class InputCookies { const InputCookies(); } -/// Dummy annotation used to request injection of Route's result +/// Dummy interceptor used to request injection of Route's result /// /// Must be only used in post interceptors class RouteResponse { diff --git a/pubspec.yaml b/pubspec.yaml index 9bfd6e56..ffa1ba77 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,9 +1,10 @@ name: jaguar version: 0.0.3 description: jaguar is a performant server side framework - -author: Jaguar Authors -homepage: https://github.com/Jaguar-dart/jaguar +authors: +- Ravi Teja Gudapati +- Kevin Segaud +homepage: http://jaguar-dart.github.io executables: jaguar: From c99ac0d30aa47ac07004b3aeba00ce6b520dd807 Mon Sep 17 00:00:00 2001 From: Ravi Teja Gudapati Date: Tue, 8 Nov 2016 00:10:27 +0100 Subject: [PATCH 2/6] Added writesResponse to decode interceptors --- lib/src/annotations/interceptors.dart | 11 ++++++++--- lib/src/interceptors/import.dart | 6 +++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/src/annotations/interceptors.dart b/lib/src/annotations/interceptors.dart index 0aaecb0a..e8d842a1 100644 --- a/lib/src/annotations/interceptors.dart +++ b/lib/src/annotations/interceptors.dart @@ -4,25 +4,30 @@ class DefineInterceptorFunc { const DefineInterceptorFunc(); } -///An annotation to add a function as interceptor to a route +/// Annotation to define a function as interceptor to a route class InterceptorFunction { ///Function that contains the implementation of the interceptor final Function function; + /// Defines if the interceptor should be executed pre or post route execution final bool isPost; const InterceptorFunction(this.function, {this.isPost: false}); } -/// Defines a dual interceptor +/// Annotation to defines an interceptor class class InterceptorClass { + /// Defines if the interceptor writes response + /// + /// Only one interceptor can write response in an interceptor chain final bool writesResponse; const InterceptorClass({this.writesResponse: false}); } -/// Base class for dual interceptors +/// Base class for interceptor class Interceptor { + /// Id of the interceptor final String id; const Interceptor({this.id}); diff --git a/lib/src/interceptors/import.dart b/lib/src/interceptors/import.dart index 211dc96b..4947b773 100644 --- a/lib/src/interceptors/import.dart +++ b/lib/src/interceptors/import.dart @@ -46,7 +46,7 @@ class FormField { } } -@InterceptorClass() +@InterceptorClass(writesResponse: true) class DecodeFormData extends Interceptor { const DecodeFormData(); @@ -91,7 +91,7 @@ class DecodeFormData extends Interceptor { } } -@InterceptorClass() +@InterceptorClass(writesResponse: true) class DecodeUrlEncodedForm extends Interceptor { final Encoding encoding; @@ -107,7 +107,7 @@ class DecodeUrlEncodedForm extends Interceptor { } } -@InterceptorClass() +@InterceptorClass(writesResponse: true) class DecodeJson extends Interceptor { final Encoding encoding; From 82342dac7f55cb10a7b4dd0ca1863119940c4939 Mon Sep 17 00:00:00 2001 From: Ravi Teja Gudapati Date: Tue, 8 Nov 2016 00:30:27 +0100 Subject: [PATCH 3/6] Websocket route reponse fix --- example/silly/main.g.dart | 3 +-- lib/generator/writer/import.dart | 13 +++++++---- .../interceptor_class_instantiator.dart | 22 +++++++++++++++++++ lib/generator/writer/route_call.dart | 12 ++++++---- lib/src/interceptors/import.dart | 6 ++--- test/jaguar/websocket/websocket.dart | 4 +++- test/jaguar/websocket/websocket.g.dart | 3 +-- 7 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 lib/generator/writer/interceptor_class_instantiator.dart diff --git a/example/silly/main.g.dart b/example/silly/main.g.dart index be327f3c..ff21337a 100644 --- a/example/silly/main.g.dart +++ b/example/silly/main.g.dart @@ -84,9 +84,8 @@ abstract class _$JaguarExampleApi implements ApiInterface { match = _routes[4].match(request.uri.path, request.method, '/api', pathParams); if (match) { - dynamic rRouteResponse; WebSocket ws = await WebSocketTransformer.upgrade(request); - rRouteResponse = await websocket( + await websocket( ws, ); return true; diff --git a/lib/generator/writer/import.dart b/lib/generator/writer/import.dart index 722ef788..53c0a003 100644 --- a/lib/generator/writer/import.dart +++ b/lib/generator/writer/import.dart @@ -10,6 +10,7 @@ part 'post_dual_inter.dart'; part 'route_call.dart'; part 'route_exceptions.dart'; part 'default_response.dart'; +part 'interceptor_class_instantiator.dart'; class Writer { final String className; @@ -102,8 +103,10 @@ class Writer { } void _writeRouteCall(RouteInfo route) { - if (!route.returnsVoid) { - sb.write(route.returnTypeIntended.displayName + " rRouteResponse;"); + if(!route.isWebSocket) { + if (!route.returnsVoid) { + sb.write(route.returnTypeIntended.displayName + " rRouteResponse;"); + } } if (route.exceptions.length != 0) { @@ -144,9 +147,11 @@ class Writer { } void _writePreInterceptorClass(RouteInfo route, InterceptorClassInfo info) { - InterceptorFuncDef pre = info.dual.pre; + InterceptorClassDecl declWriter = new InterceptorClassDecl(route, info); - sb.write(info.instantiationString); + sb.write(declWriter.code); + + InterceptorFuncDef pre = info.dual.pre; if (pre is! InterceptorFuncDef) { return; diff --git a/lib/generator/writer/interceptor_class_instantiator.dart b/lib/generator/writer/interceptor_class_instantiator.dart new file mode 100644 index 00000000..a31008e3 --- /dev/null +++ b/lib/generator/writer/interceptor_class_instantiator.dart @@ -0,0 +1,22 @@ +part of jaguar.generator.writer; + +class InterceptorClassDecl { + /// Route of the interceptor + final RouteInfo _r; + + /// Interceptor for which instantiation code is being generated + final InterceptorClassInfo _i; + + /// String buffer to write the result to + StringBuffer _w = new StringBuffer(); + + InterceptorClassDecl(this._r, this._i) { + _generate(); + } + + void _generate() { + _w.write(_i.instantiationString); + } + + String get code => _w.toString(); +} \ No newline at end of file diff --git a/lib/generator/writer/route_call.dart b/lib/generator/writer/route_call.dart index fab83da2..6dffb30f 100644 --- a/lib/generator/writer/route_call.dart +++ b/lib/generator/writer/route_call.dart @@ -60,11 +60,15 @@ class RouteCallWriter { sb.write(_generateQueryParamInjector()); - if (!route.returnsVoid) { - if (!route.returnsFuture) { + if (!route.isWebSocket) { + if (!route.returnsVoid) { sb.write("rRouteResponse = "); - } else { - sb.write("rRouteResponse = await "); + } + } + + if (!route.returnsVoid) { + if (route.returnsFuture) { + sb.write("await "); } } diff --git a/lib/src/interceptors/import.dart b/lib/src/interceptors/import.dart index 4947b773..211dc96b 100644 --- a/lib/src/interceptors/import.dart +++ b/lib/src/interceptors/import.dart @@ -46,7 +46,7 @@ class FormField { } } -@InterceptorClass(writesResponse: true) +@InterceptorClass() class DecodeFormData extends Interceptor { const DecodeFormData(); @@ -91,7 +91,7 @@ class DecodeFormData extends Interceptor { } } -@InterceptorClass(writesResponse: true) +@InterceptorClass() class DecodeUrlEncodedForm extends Interceptor { final Encoding encoding; @@ -107,7 +107,7 @@ class DecodeUrlEncodedForm extends Interceptor { } } -@InterceptorClass(writesResponse: true) +@InterceptorClass() class DecodeJson extends Interceptor { final Encoding encoding; diff --git a/test/jaguar/websocket/websocket.dart b/test/jaguar/websocket/websocket.dart index 977cf4c4..12a3ca40 100644 --- a/test/jaguar/websocket/websocket.dart +++ b/test/jaguar/websocket/websocket.dart @@ -4,7 +4,7 @@ import 'dart:io'; import 'dart:async'; import 'package:test/test.dart'; import 'package:jaguar/jaguar.dart'; -import 'package:jaguar/testing.dart'; +//TODO import 'package:jaguar/testing.dart'; part 'websocket.g.dart'; @@ -18,6 +18,7 @@ class ExampleApi extends Object with _$JaguarExampleApi { void main() { group('route', () { + /* TODO JaguarMock mock; setUp(() { Configuration config = new Configuration(); @@ -26,5 +27,6 @@ void main() { }); tearDown(() {}); + */ }); } diff --git a/test/jaguar/websocket/websocket.g.dart b/test/jaguar/websocket/websocket.g.dart index f66b3ce8..856603af 100644 --- a/test/jaguar/websocket/websocket.g.dart +++ b/test/jaguar/websocket/websocket.g.dart @@ -19,9 +19,8 @@ abstract class _$JaguarExampleApi implements ApiInterface { match = _routes[0].match(request.uri.path, request.method, '/api', pathParams); if (match) { - dynamic rRouteResponse; WebSocket ws = await WebSocketTransformer.upgrade(request); - rRouteResponse = await websocket( + await websocket( ws, ); return true; From 8cc2cc66141b8842c87b55174bdd9d33d0847973 Mon Sep 17 00:00:00 2001 From: Ravi Teja Gudapati Date: Tue, 8 Nov 2016 01:46:55 +0100 Subject: [PATCH 4/6] WIP: param and state injection to interceptors --- example/forum/forum.dart | 4 +- example/forum/forum.g.dart | 55 +++++++++++--- example/forum/interceptor.dart | 22 +++++- lib/generator/writer/import.dart | 5 +- .../interceptor_class_instantiator.dart | 75 ++++++++++++++++++- lib/src/annotations/inputs.dart | 1 - 6 files changed, 143 insertions(+), 19 deletions(-) diff --git a/example/forum/forum.dart b/example/forum/forum.dart index 3ad32ebc..517b95bf 100644 --- a/example/forum/forum.dart +++ b/example/forum/forum.dart @@ -75,9 +75,9 @@ class ForumApi extends Object with _$JaguarForumApi { methods: const ['GET'], statusCode: 201, headers: const {"sample-header": "made-with.jaguar"}) - @MongoDb('test', id: 'Test') + @MongoDb('test', id: 'Test', state: const MongoDbState()) @MongoDb('admin', id: 'Admin') - @Login() + @Login(const LoginState()) @EncodeToJson() Future fetch() async { return new User('dummy@dummy.com', 'Dummy', 'password', 27); diff --git a/example/forum/forum.g.dart b/example/forum/forum.g.dart index 8ca6039e..d4ce56b2 100644 --- a/example/forum/forum.g.dart +++ b/example/forum/forum.g.dart @@ -66,11 +66,20 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[0].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbTest = new MongoDb('test', id: 'Test'); + MongoDb iMongoDbTest = new MongoDb( + 'test', + id: 'Test', + state: const MongoDbState(), + ); await iMongoDbTest.pre(); - MongoDb iMongoDbAdmin = new MongoDb('admin', id: 'Admin'); + MongoDb iMongoDbAdmin = new MongoDb( + 'admin', + id: 'Admin', + ); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); - Login iLogin = new Login(); + Login iLogin = new Login( + const LoginState(), + ); iLogin.pre( rMongoDbAdmin, ); @@ -91,7 +100,10 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[1].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb('admin', id: 'Admin'); + MongoDb iMongoDbAdmin = new MongoDb( + 'admin', + id: 'Admin', + ); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -109,7 +121,10 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[2].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb('admin', id: 'Admin'); + MongoDb iMongoDbAdmin = new MongoDb( + 'admin', + id: 'Admin', + ); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -134,7 +149,10 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[3].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb('admin', id: 'Admin'); + MongoDb iMongoDbAdmin = new MongoDb( + 'admin', + id: 'Admin', + ); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -158,7 +176,10 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[4].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb('admin', id: 'Admin'); + MongoDb iMongoDbAdmin = new MongoDb( + 'admin', + id: 'Admin', + ); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -189,7 +210,10 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[5].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb('admin', id: 'Admin'); + MongoDb iMongoDbAdmin = new MongoDb( + 'admin', + id: 'Admin', + ); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -227,7 +251,10 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[6].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb('admin', id: 'Admin'); + MongoDb iMongoDbAdmin = new MongoDb( + 'admin', + id: 'Admin', + ); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -261,7 +288,10 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[7].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb('admin', id: 'Admin'); + MongoDb iMongoDbAdmin = new MongoDb( + 'admin', + id: 'Admin', + ); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -283,7 +313,10 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[8].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb('admin', id: 'Admin'); + MongoDb iMongoDbAdmin = new MongoDb( + 'admin', + id: 'Admin', + ); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( diff --git a/example/forum/interceptor.dart b/example/forum/interceptor.dart index 9960ced5..93f80985 100644 --- a/example/forum/interceptor.dart +++ b/example/forum/interceptor.dart @@ -57,11 +57,21 @@ class User implements ViewSerializer, ModelSerializer { Map toModelMap() => toMap()..['pwdH'] = passwordHash; } +class MongoDbState { + final String whatever; + + const MongoDbState({this.whatever}); +} + @InterceptorClass() class MongoDb extends Interceptor { final String dbName; - const MongoDb(this.dbName, {String id}) : super(id: id); + final MongoDbState state; + + const MongoDb(this.dbName, {String id, this.state}) : super(id: id); + + static MongoDbState createState() => new MongoDbState(); Future pre() async { return new Db(); @@ -70,9 +80,17 @@ class MongoDb extends Interceptor { Future post() async {} } +class LoginState { + final String whatever; + + const LoginState({this.whatever}); +} + @InterceptorClass() class Login extends Interceptor { - const Login(); + final LoginState state; + + const Login([this.state]); @Input(MongoDb, id: 'Admin') void pre(Db db) {} diff --git a/lib/generator/writer/import.dart b/lib/generator/writer/import.dart index 53c0a003..1a4e0583 100644 --- a/lib/generator/writer/import.dart +++ b/lib/generator/writer/import.dart @@ -1,6 +1,9 @@ library jaguar.generator.writer; import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/src/dart/element/element.dart'; +import 'package:analyzer/src/dart/ast/ast.dart'; +import 'package:analyzer/src/generated/utilities_dart.dart'; import 'package:jaguar/generator/parser/import.dart'; import 'package:jaguar/generator/internal/element/import.dart'; @@ -103,7 +106,7 @@ class Writer { } void _writeRouteCall(RouteInfo route) { - if(!route.isWebSocket) { + if (!route.isWebSocket) { if (!route.returnsVoid) { sb.write(route.returnTypeIntended.displayName + " rRouteResponse;"); } diff --git a/lib/generator/writer/interceptor_class_instantiator.dart b/lib/generator/writer/interceptor_class_instantiator.dart index a31008e3..8890bdba 100644 --- a/lib/generator/writer/interceptor_class_instantiator.dart +++ b/lib/generator/writer/interceptor_class_instantiator.dart @@ -11,12 +11,83 @@ class InterceptorClassDecl { StringBuffer _w = new StringBuffer(); InterceptorClassDecl(this._r, this._i) { - _generate(); + _generateExperimental(); } void _generate() { _w.write(_i.instantiationString); } + void _generateExperimental() { + _w.write(_i.interceptor.name + " "); + _w.write(_i.genInstanceName + " = new "); + + ElementAnnotationImpl element = _i.elememt; + ConstructorElementImpl construct = element.element; + AnnotationImpl ast = element.annotationAst; + _w.write(ast.name); + _w.write('('); + + bool hasAssignedState = false; + + Map optionalParams = {}; + + int index = 0; + for (index = 0; index < construct.parameters.length; index++) { + ParameterElement param = construct.parameters[index]; + + if (param.parameterKind.isOptional) { + break; + } + + final astItem = ast.arguments.arguments[index]; + + _w.write(astItem); + _w.write(', '); + } + + if (index < construct.parameters.length && + index < ast.arguments.arguments.length) { + if (construct.parameters[index].parameterKind == ParameterKind.NAMED) { + for (; index < ast.arguments.arguments.length; index++) { + NamedExpressionImpl astItem = ast.arguments.arguments[index]; + + _w.write(astItem); + _w.write(', '); + + if (astItem.name.label.name == 'state') { + hasAssignedState = true; + } + } + } else { + for (; index < ast.arguments.arguments.length; index++) { + ParameterElement param = construct.parameters[index]; + + final astItem = ast.arguments.arguments[index]; + + _w.write(astItem); + _w.write(', '); + + if (param.name == 'state') { + hasAssignedState = true; + } + } + } + } + + if (!hasAssignedState) { + //Check if interceptor has state and can be assigned + print('check'); + //TODO + } else { + print('no check'); + } + + //TODO + + _w.write(')'); + _w.writeln(";"); + } + String get code => _w.toString(); -} \ No newline at end of file +} diff --git a/lib/src/annotations/inputs.dart b/lib/src/annotations/inputs.dart index a7ba4a2c..1e89b848 100644 --- a/lib/src/annotations/inputs.dart +++ b/lib/src/annotations/inputs.dart @@ -24,7 +24,6 @@ class InputHeaders { const InputHeaders(); } - /// Annotation to request input of cookie class InputCookie { /// Key of the cookie From aad51b0b12063fd69888d51b1eb1ca9196f60ecc Mon Sep 17 00:00:00 2001 From: Ravi Teja Gudapati Date: Tue, 8 Nov 2016 13:55:29 +0100 Subject: [PATCH 5/6] WIP: interceptor state injection --- example/forum/forum.g.dart | 54 +++++++------------ .../internal/element/constructor_element.dart | 46 ++++++++++++++++ lib/generator/internal/element/import.dart | 4 ++ lib/generator/parser/interceptor/dual.dart | 39 ++++++++++++++ .../interceptor_class_instantiator.dart | 7 ++- 5 files changed, 110 insertions(+), 40 deletions(-) create mode 100644 lib/generator/internal/element/constructor_element.dart diff --git a/example/forum/forum.g.dart b/example/forum/forum.g.dart index d4ce56b2..6025811d 100644 --- a/example/forum/forum.g.dart +++ b/example/forum/forum.g.dart @@ -72,10 +72,8 @@ abstract class _$JaguarForumApi implements ApiInterface { state: const MongoDbState(), ); await iMongoDbTest.pre(); - MongoDb iMongoDbAdmin = new MongoDb( - 'admin', - id: 'Admin', - ); + MongoDb iMongoDbAdmin = + new MongoDb('admin', id: 'Admin', state: MongoDb.createState()); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login( const LoginState(), @@ -100,10 +98,8 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[1].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb( - 'admin', - id: 'Admin', - ); + MongoDb iMongoDbAdmin = + new MongoDb('admin', id: 'Admin', state: MongoDb.createState()); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -121,10 +117,8 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[2].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb( - 'admin', - id: 'Admin', - ); + MongoDb iMongoDbAdmin = + new MongoDb('admin', id: 'Admin', state: MongoDb.createState()); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -149,10 +143,8 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[3].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb( - 'admin', - id: 'Admin', - ); + MongoDb iMongoDbAdmin = + new MongoDb('admin', id: 'Admin', state: MongoDb.createState()); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -176,10 +168,8 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[4].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb( - 'admin', - id: 'Admin', - ); + MongoDb iMongoDbAdmin = + new MongoDb('admin', id: 'Admin', state: MongoDb.createState()); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -210,10 +200,8 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[5].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb( - 'admin', - id: 'Admin', - ); + MongoDb iMongoDbAdmin = + new MongoDb('admin', id: 'Admin', state: MongoDb.createState()); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -251,10 +239,8 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[6].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb( - 'admin', - id: 'Admin', - ); + MongoDb iMongoDbAdmin = + new MongoDb('admin', id: 'Admin', state: MongoDb.createState()); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -288,10 +274,8 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[7].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb( - 'admin', - id: 'Admin', - ); + MongoDb iMongoDbAdmin = + new MongoDb('admin', id: 'Admin', state: MongoDb.createState()); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( @@ -313,10 +297,8 @@ abstract class _$JaguarForumApi implements ApiInterface { match = _routes[8].match(request.uri.path, request.method, '/api', pathParams); if (match) { - MongoDb iMongoDbAdmin = new MongoDb( - 'admin', - id: 'Admin', - ); + MongoDb iMongoDbAdmin = + new MongoDb('admin', id: 'Admin', state: MongoDb.createState()); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); Login iLogin = new Login(); iLogin.pre( diff --git a/lib/generator/internal/element/constructor_element.dart b/lib/generator/internal/element/constructor_element.dart new file mode 100644 index 00000000..a68f93a6 --- /dev/null +++ b/lib/generator/internal/element/constructor_element.dart @@ -0,0 +1,46 @@ +part of jaguar.generator.internal.element; + +class ConstructorElementWrap { + ConstructorElementWrap(this._wrapped) { + for (ParameterElement param in parameters) { + if (param.parameterKind.isOptional) { + _optionalParams.add(param); + + _areOptionalParamsPositional = + param.parameterKind == ParameterKind.POSITIONAL; + } else { + _requiredParams.add(param); + } + } + } + + final ConstructorElement _wrapped; + + List get metadata => _wrapped.metadata; + + List _requiredParams = []; + + List _optionalParams = []; + + bool _areOptionalParamsPositional = false; + + String get name => _wrapped.name; + + List get parameters => _wrapped.parameters; + + List get requiredParameters => _requiredParams; + + List get optionalParameters => _optionalParams; + + bool get areOptionalParamsPositional => _areOptionalParamsPositional; + + ParameterElement findOptionalParamByName(String paramName) { + for (ParameterElement param in _optionalParams) { + if (param.name == paramName) { + return param; + } + } + + return null; + } +} diff --git a/lib/generator/internal/element/import.dart b/lib/generator/internal/element/import.dart index bd523556..3400b3c4 100644 --- a/lib/generator/internal/element/import.dart +++ b/lib/generator/internal/element/import.dart @@ -6,6 +6,8 @@ import 'package:analyzer/dart/constant/value.dart'; import 'package:analyzer/src/dart/element/element.dart'; import 'package:analyzer/src/generated/utilities_dart.dart'; +part 'constructor_element.dart'; + /// An element that has a name and library abstract class NamedElement { /// Name of the element @@ -31,6 +33,8 @@ class MethodElementWrap { final MethodElement _wrapped; + bool get isStatic => _wrapped.isStatic; + List get metadata => _wrapped.metadata; List _requiredParams = []; diff --git a/lib/generator/parser/interceptor/dual.dart b/lib/generator/parser/interceptor/dual.dart index 62031b46..a71aac3a 100644 --- a/lib/generator/parser/interceptor/dual.dart +++ b/lib/generator/parser/interceptor/dual.dart @@ -27,6 +27,10 @@ class InterceptorClassDef { }); } + //TODO check in unnamedConstructor is null? + ConstructorElementWrap get constructor => + new ConstructorElementWrap(clazz.unnamedConstructor); + /// Debug printer String toString() { return "$pre $post"; @@ -114,4 +118,39 @@ class InterceptorClassInfo implements InterceptorInfo { return false; } + + bool get canCreateState { + MethodElement meth = dual.clazz.getMethod('createState'); + + if (meth == null) { + return false; + } + + MethodElementWrap methWrapped = new MethodElementWrap(meth); + + if (!methWrapped.isStatic) { + return false; + } + + if (methWrapped.requiredParameters.length != 0) { + //TODO warn? throw error? + return false; + } + + if (methWrapped.returnType.isVoid) { + //TODO warn? throw error? + return false; + } + + return true; + } + + bool get needsState { + //TODO right now, we don't allow positional parameters + if (dual.constructor.areOptionalParamsPositional) { + return false; + } + + return dual.constructor.findOptionalParamByName('state') != null; + } } diff --git a/lib/generator/writer/interceptor_class_instantiator.dart b/lib/generator/writer/interceptor_class_instantiator.dart index 8890bdba..0529cb82 100644 --- a/lib/generator/writer/interceptor_class_instantiator.dart +++ b/lib/generator/writer/interceptor_class_instantiator.dart @@ -77,10 +77,9 @@ class InterceptorClassDecl { if (!hasAssignedState) { //Check if interceptor has state and can be assigned - print('check'); - //TODO - } else { - print('no check'); + if (_i.canCreateState && _i.needsState) { + _w.write('state: ${_i.interceptor.name}.createState()'); + } } //TODO From 08ca5fdd52b709acaf4a9f74d9713c62ed63c774 Mon Sep 17 00:00:00 2001 From: Ravi Teja Gudapati Date: Wed, 9 Nov 2016 00:06:24 +0100 Subject: [PATCH 6/6] Interceptor params --- README.md | 4 +- example/forum/forum.g.dart | 4 +- jaguar.yaml | 1 + .../internal/element/constructor_element.dart | 46 ---- lib/generator/internal/element/import.dart | 220 ------------------ .../parser/exception_handler/import.dart | 2 +- lib/generator/parser/group/import.dart | 2 +- lib/generator/parser/import.dart | 2 +- .../parser/interceptor/clazz/clazz_def.dart | 40 ++++ .../{dual.dart => clazz/clazz_info.dart} | 63 ++--- .../{func.dart => func/func_def.dart} | 16 -- .../parser/interceptor/func/func_info.dart | 17 ++ lib/generator/parser/interceptor/import.dart | 9 +- lib/generator/parser/interceptor/type.dart | 25 ++ lib/generator/parser/route/import.dart | 2 +- lib/generator/writer/import.dart | 2 +- .../interceptor_class_instantiator.dart | 35 +-- lib/src/annotations/interceptors.dart | 4 +- lib/src/console/import.dart | 3 - lib/src/snap/import.dart | 17 -- pubspec.yaml | 4 +- test/interceptor/param/param.dart | 62 +++++ test/interceptor/param/param.g.dart | 41 ++++ test/jaguar/group/main.dart | 2 +- test/test_all.dart | 2 + 25 files changed, 250 insertions(+), 375 deletions(-) delete mode 100644 lib/generator/internal/element/constructor_element.dart delete mode 100644 lib/generator/internal/element/import.dart create mode 100644 lib/generator/parser/interceptor/clazz/clazz_def.dart rename lib/generator/parser/interceptor/{dual.dart => clazz/clazz_info.dart} (66%) rename lib/generator/parser/interceptor/{func.dart => func/func_def.dart} (89%) create mode 100644 lib/generator/parser/interceptor/func/func_info.dart delete mode 100644 lib/src/console/import.dart delete mode 100644 lib/src/snap/import.dart create mode 100644 test/interceptor/param/param.dart create mode 100644 test/interceptor/param/param.g.dart diff --git a/README.md b/README.md index e94b06e7..b6ae3370 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,9 @@ Jaguar, a server framework built for **speed, simplicity and extensiblity**. 3. Extensible interceptor infrastructure 4. Generates API console client to try your server without writing single line of client code -5. Mock HTTP requests and use dependency injection to test your API +5. Tests are first class citizens in jaguar +i. Mock HTTP requests and Websocket requests +ii. Use dependency injection to test your API 6. Optional Firebase/Parse like no code or little code servers Even though Jaguar is feature rich, it is simpler and easy to get started. diff --git a/example/forum/forum.g.dart b/example/forum/forum.g.dart index 6025811d..583c4fe2 100644 --- a/example/forum/forum.g.dart +++ b/example/forum/forum.g.dart @@ -75,9 +75,7 @@ abstract class _$JaguarForumApi implements ApiInterface { MongoDb iMongoDbAdmin = new MongoDb('admin', id: 'Admin', state: MongoDb.createState()); Db rMongoDbAdmin = await iMongoDbAdmin.pre(); - Login iLogin = new Login( - const LoginState(), - ); + Login iLogin = new Login(); iLogin.pre( rMongoDbAdmin, ); diff --git a/jaguar.yaml b/jaguar.yaml index b8b97441..661998da 100644 --- a/jaguar.yaml +++ b/jaguar.yaml @@ -4,3 +4,4 @@ apis: - 'test/jaguar/route/route.dart' - 'test/jaguar/group/main.dart' - 'test/jaguar/websocket/websocket.dart' +- 'test/interceptor/param/param.dart' diff --git a/lib/generator/internal/element/constructor_element.dart b/lib/generator/internal/element/constructor_element.dart deleted file mode 100644 index a68f93a6..00000000 --- a/lib/generator/internal/element/constructor_element.dart +++ /dev/null @@ -1,46 +0,0 @@ -part of jaguar.generator.internal.element; - -class ConstructorElementWrap { - ConstructorElementWrap(this._wrapped) { - for (ParameterElement param in parameters) { - if (param.parameterKind.isOptional) { - _optionalParams.add(param); - - _areOptionalParamsPositional = - param.parameterKind == ParameterKind.POSITIONAL; - } else { - _requiredParams.add(param); - } - } - } - - final ConstructorElement _wrapped; - - List get metadata => _wrapped.metadata; - - List _requiredParams = []; - - List _optionalParams = []; - - bool _areOptionalParamsPositional = false; - - String get name => _wrapped.name; - - List get parameters => _wrapped.parameters; - - List get requiredParameters => _requiredParams; - - List get optionalParameters => _optionalParams; - - bool get areOptionalParamsPositional => _areOptionalParamsPositional; - - ParameterElement findOptionalParamByName(String paramName) { - for (ParameterElement param in _optionalParams) { - if (param.name == paramName) { - return param; - } - } - - return null; - } -} diff --git a/lib/generator/internal/element/import.dart b/lib/generator/internal/element/import.dart deleted file mode 100644 index 3400b3c4..00000000 --- a/lib/generator/internal/element/import.dart +++ /dev/null @@ -1,220 +0,0 @@ -library jaguar.generator.internal.element; - -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/dart/constant/value.dart'; -import 'package:analyzer/src/dart/element/element.dart'; -import 'package:analyzer/src/generated/utilities_dart.dart'; - -part 'constructor_element.dart'; - -/// An element that has a name and library -abstract class NamedElement { - /// Name of the element - String get name; - - /// Library the element is in - String get libraryName; -} - -class MethodElementWrap { - MethodElementWrap(this._wrapped) { - for (ParameterElement param in parameters) { - if (param.parameterKind.isOptional) { - _optionalParams.add(param); - - _areOptionalParamsPositional = - param.parameterKind == ParameterKind.POSITIONAL; - } else { - _requiredParams.add(param); - } - } - } - - final MethodElement _wrapped; - - bool get isStatic => _wrapped.isStatic; - - List get metadata => _wrapped.metadata; - - List _requiredParams = []; - - List _optionalParams = []; - - bool _areOptionalParamsPositional = false; - - String get prototype { - StringBuffer sb = new StringBuffer(); - sb.write(returnType.toString() + " "); - sb.write(name + "("); - - if (_requiredParams.length != 0) { - String paramsStr = _requiredParams - .map((ParameterElement pel) => pel.toString()) - .join(","); - - sb.write(paramsStr); - - if (_optionalParams.length != 0) { - sb.write(','); - } - } - - if (_optionalParams.length != 0) { - if (_areOptionalParamsPositional) { - sb.write('['); - } else { - sb.write('{'); - } - - String paramsStr = _optionalParams.map((ParameterElement pel) { - final str = pel.toString(); - return str.substring(1, str.length - 1); - }).join(","); - - sb.write(paramsStr); - - if (_areOptionalParamsPositional) { - sb.write(']'); - } else { - sb.write('}'); - } - } - - sb.writeln(");"); - return sb.toString(); - } - - String get name => _wrapped.name; - - List get parameters => _wrapped.parameters; - - List get requiredParameters => _requiredParams; - - List get optionalParameters => _optionalParams; - - bool get areOptionalParamsPositional => _areOptionalParamsPositional; - - DartType get returnType => _wrapped.returnType; - - DartTypeWrap get returnTypeWithoutFuture => - new DartTypeWrap(returnType.flattenFutures(_wrapped.context.typeSystem)); -} - -class DartTypeWrap { - final DartType _wrapped; - - DartTypeWrap(this._wrapped); - - bool get isVoid => _wrapped.isVoid; - - bool get isDartAsyncFuture => _wrapped.isDartAsyncFuture; - - bool get isDynamic => _wrapped.isDynamic; - - bool get isInt => compare(kIntTypeName, kCoreLibraryName); - - bool get isDouble => compare(kDoubleTypeName, kCoreLibraryName); - - bool get isNum => compare(kNumTypeName, kCoreLibraryName); - - bool get isBool => compare(kBoolTypeName, kCoreLibraryName); - - bool get isString => compare(kStringTypeName, kCoreLibraryName); - - bool get isBuiltin => isInt || isDouble || isNum || isBool || isString; - - String get displayName => _wrapped.displayName; - - String get name => _wrapped.name; - - String get libraryName => _wrapped.element.library.name; - - bool isType(DartTypeWrap other) { - if (libraryName != other.libraryName) { - return false; - } - - if (name != other.name) { - return false; - } - - return true; - } - - static const String kCoreLibraryName = 'dart.core'; - - static const String kIntTypeName = 'int'; - - static const String kDoubleTypeName = 'double'; - - static const String kNumTypeName = 'num'; - - static const String kBoolTypeName = 'bool'; - - static const String kStringTypeName = 'String'; - - bool compare(String aName, String aLibraryName) => - aName == name && aLibraryName == libraryName; - - String toString() => _wrapped.toString(); -} - -class ParameterElementWrap { - final ParameterElement _wrapped; - - ParameterElementWrap(this._wrapped) { - _wrapped.computeConstantValue(); - _type = new DartTypeWrap(_wrapped.type); - } - - DartTypeWrap _type; - - DartTypeWrap get type => _type; - - dynamic get toValueIfBuiltin { - if (!_type.isBuiltin) { - return null; - } - - DartObject value = _wrapped.constantValue; - - if (value == null) { - return null; - } - - if (_type.isInt) { - return value.toIntValue(); - } else if (_type.isDouble) { - return value.toDoubleValue(); - } else if (_type.isString) { - return value.toStringValue(); - } else if (_type.isBool) { - return value.toBoolValue(); - } - - return null; - } - - String get name => _wrapped.name; -} - -class AnnotationElementWrap { - final ElementAnnotation _wrapped; - - AnnotationElementWrap(this._wrapped) { - _wrapped.computeConstantValue(); - } - - DartObject get constantValue => _wrapped.constantValue; - - String get libraryName => constantValue.type.element.library.displayName; - - String get displayName => constantValue.type.displayName; - - String get instantiationString { - String lRet = (_wrapped as ElementAnnotationImpl).annotationAst.toSource(); - lRet = lRet.substring(1); - return lRet; - } -} diff --git a/lib/generator/parser/exception_handler/import.dart b/lib/generator/parser/exception_handler/import.dart index e0b806c1..5294dfa9 100644 --- a/lib/generator/parser/exception_handler/import.dart +++ b/lib/generator/parser/exception_handler/import.dart @@ -3,7 +3,7 @@ library jaguar.generator.parser.exception_handler; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; -import 'package:jaguar/generator/internal/element/import.dart'; +import 'package:source_gen_help/import.dart'; class ExceptionHandlerInfo { final AnnotationElementWrap _handler; diff --git a/lib/generator/parser/group/import.dart b/lib/generator/parser/group/import.dart index bc800a42..3296bf45 100644 --- a/lib/generator/parser/group/import.dart +++ b/lib/generator/parser/group/import.dart @@ -9,7 +9,7 @@ import 'package:jaguar/generator/parser/route/import.dart'; import 'package:jaguar/generator/parser/interceptor/import.dart'; import 'package:jaguar/generator/parser/exception_handler/import.dart'; -import 'package:jaguar/generator/internal/element/import.dart'; +import 'package:source_gen_help/import.dart'; ant.Group parseGroup(Element element) { return element.metadata.map((ElementAnnotation annot) { diff --git a/lib/generator/parser/import.dart b/lib/generator/parser/import.dart index f294107a..b6ac8cae 100644 --- a/lib/generator/parser/import.dart +++ b/lib/generator/parser/import.dart @@ -1,6 +1,6 @@ library jaguar.generator.info; -import 'package:jaguar/generator/internal/element/import.dart'; +import 'package:source_gen_help/import.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; diff --git a/lib/generator/parser/interceptor/clazz/clazz_def.dart b/lib/generator/parser/interceptor/clazz/clazz_def.dart new file mode 100644 index 00000000..95a2f975 --- /dev/null +++ b/lib/generator/parser/interceptor/clazz/clazz_def.dart @@ -0,0 +1,40 @@ +part of jaguar.generator.parser.interceptor; + +/// Holds information about pre and post interceptors in interceptor class +class InterceptorClassDef { + /// The class element of the interceptor + ClassElement clazz; + + /// Pre interceptor info + InterceptorFuncDef pre; + + /// Post interceptor info + InterceptorFuncDef post; + + DartType get returnType => pre?.returnType; + + DartType get returnsFutureFlattened => pre?.returnsFutureFlattened; + + /// Default constructor. Constructs [InterceptorClassDef] for a given class + InterceptorClassDef(this.clazz) { + /// Find pre and post interceptors in class + clazz.methods.forEach((MethodElement method) { + if (method.name == 'pre') { + pre = new InterceptorFuncDef(method); + } else if (method.name == 'post') { + post = new InterceptorFuncDef(method); + } + }); + } + + //TODO check in unnamedConstructor is null? + ConstructorElementWrap get constructor => + new ConstructorElementWrap(clazz.unnamedConstructor); + + /// Debug printer + String toString() { + return "$pre $post"; + } +} + +class InterceptorParamInfo {} diff --git a/lib/generator/parser/interceptor/dual.dart b/lib/generator/parser/interceptor/clazz/clazz_info.dart similarity index 66% rename from lib/generator/parser/interceptor/dual.dart rename to lib/generator/parser/interceptor/clazz/clazz_info.dart index a71aac3a..af2ce6fd 100644 --- a/lib/generator/parser/interceptor/dual.dart +++ b/lib/generator/parser/interceptor/clazz/clazz_info.dart @@ -1,44 +1,7 @@ part of jaguar.generator.parser.interceptor; -/// Holds information about pre and post interceptors in interceptor class -class InterceptorClassDef { - /// The class element of the interceptor - ClassElement clazz; - - /// Pre interceptor info - InterceptorFuncDef pre; - - /// Post interceptor info - InterceptorFuncDef post; - - DartType get returnType => pre?.returnType; - - DartType get returnsFutureFlattened => pre?.returnsFutureFlattened; - - /// Default constructor. Constructs [InterceptorClassDef] for a given class - InterceptorClassDef(this.clazz) { - /// Find pre and post interceptors in class - clazz.methods.forEach((MethodElement method) { - if (method.name == 'pre') { - pre = new InterceptorFuncDef(method); - } else if (method.name == 'post') { - post = new InterceptorFuncDef(method); - } - }); - } - - //TODO check in unnamedConstructor is null? - ConstructorElementWrap get constructor => - new ConstructorElementWrap(clazz.unnamedConstructor); - - /// Debug printer - String toString() { - return "$pre $post"; - } -} - class InterceptorClassInfo implements InterceptorInfo { - ElementAnnotation elememt; + ElementAnnotation element; ant.InterceptorClass _defined; @@ -67,17 +30,33 @@ class InterceptorClassInfo implements InterceptorInfo { return type.isSupertypeOf(result); } else { DartType flattenedType = - result.flattenFutures(elememt.context.typeSystem); + result.flattenFutures(element.context.typeSystem); return type.isSupertypeOf(flattenedType); } } ///Create dual interceptor info for given interceptor usage - InterceptorClassInfo(this.elememt, this._defined) { + InterceptorClassInfo(this.element, this._defined) { final ClassElement clazz = - elememt.element.getAncestor((Element el) => el is ClassElement); - interceptor = new InterceptorAnnotationInstance(elememt); + element.element.getAncestor((Element el) => el is ClassElement); + interceptor = new InterceptorAnnotationInstance(element); dual = new InterceptorClassDef(clazz); + + _validate(); + } + + void _validate() { + interceptor.params.forEach((String key, DartTypeWrap type) { + ParameterElement param = dual.constructor.findOptionalParamByName(key); + if (param == null) { + throw new Exception('Unknown interceptor param $key!'); + } + + if (!type.isAssignableTo(new DartTypeWrap(param.type))) { + throw new Exception( + 'Cannot assign ${type.displayName} to ${param.type.displayName}!'); + } + }); } String toString() { diff --git a/lib/generator/parser/interceptor/func.dart b/lib/generator/parser/interceptor/func/func_def.dart similarity index 89% rename from lib/generator/parser/interceptor/func.dart rename to lib/generator/parser/interceptor/func/func_def.dart index 9573d32d..a5134f54 100644 --- a/lib/generator/parser/interceptor/func.dart +++ b/lib/generator/parser/interceptor/func/func_def.dart @@ -83,19 +83,3 @@ class InterceptorFuncDef { return false; } } - -class InterceptorFuncInfo implements InterceptorInfo { - InterceptorFuncDef definition; - - bool isPost; - - DartType result; - - List inputs = []; - - bool writesResponse; - - bool shouldKeepQueryParam; - - InterceptorFuncInfo(this.definition, {this.isPost: false}); -} diff --git a/lib/generator/parser/interceptor/func/func_info.dart b/lib/generator/parser/interceptor/func/func_info.dart new file mode 100644 index 00000000..ec38e648 --- /dev/null +++ b/lib/generator/parser/interceptor/func/func_info.dart @@ -0,0 +1,17 @@ +part of jaguar.generator.parser.interceptor; + +class InterceptorFuncInfo implements InterceptorInfo { + InterceptorFuncDef definition; + + bool isPost; + + DartType result; + + List inputs = []; + + bool writesResponse; + + bool shouldKeepQueryParam; + + InterceptorFuncInfo(this.definition, {this.isPost: false}); +} diff --git a/lib/generator/parser/interceptor/import.dart b/lib/generator/parser/interceptor/import.dart index c8113527..218bec43 100644 --- a/lib/generator/parser/interceptor/import.dart +++ b/lib/generator/parser/interceptor/import.dart @@ -1,5 +1,6 @@ library jaguar.generator.parser.interceptor; +import 'dart:mirrors'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/src/dart/element/element.dart'; @@ -8,12 +9,14 @@ import 'package:analyzer/dart/constant/value.dart'; import 'package:jaguar/src/annotations/import.dart' as ant; import 'package:source_gen/src/annotation.dart'; -import 'package:jaguar/generator/internal/element/import.dart'; +import 'package:source_gen_help/import.dart'; import 'package:jaguar/generator/parser/import.dart'; -part 'func.dart'; -part 'dual.dart'; +part 'clazz/clazz_info.dart'; +part 'clazz/clazz_def.dart'; +part 'func/func_def.dart'; +part 'func/func_info.dart'; part 'type.dart'; abstract class InterceptorInfo { diff --git a/lib/generator/parser/interceptor/type.dart b/lib/generator/parser/interceptor/type.dart index e3d42a07..0a93a120 100644 --- a/lib/generator/parser/interceptor/type.dart +++ b/lib/generator/parser/interceptor/type.dart @@ -17,10 +17,35 @@ class InterceptorAnnotationInstance { String get name => _type.name; + final Map params = {}; + + void _populateParams() { + DartObject object = _constVal.getField('(super)')?.getField('params'); + if (object is! DartObject) { + return; + } + + Map map = object.toMapValue(); + + if (map is! Map) { + return; + } + + map.forEach((DartObject key, DartObject val) { + final String name = key.toSymbolValue(); + if (name == 'state' || name == 'params') { + throw new Exception( + 'Cannot provide state and params param to interceptor!'); + } + params[key.toSymbolValue()] = new DartTypeWrap(val.toTypeValue()); + }); + } + InterceptorAnnotationInstance(this.element) { _constVal = element.computeConstantValue(); _type = new DartTypeWrap(_constVal.type); _id = _constVal.getField('(super)')?.getField('id')?.toStringValue(); + _populateParams(); } String get instantiationString { diff --git a/lib/generator/parser/route/import.dart b/lib/generator/parser/route/import.dart index 4aa1e411..6a7be5db 100644 --- a/lib/generator/parser/route/import.dart +++ b/lib/generator/parser/route/import.dart @@ -8,7 +8,7 @@ import 'package:jaguar/src/annotations/import.dart' as ant; import 'package:jaguar/generator/parser/interceptor/import.dart'; import 'package:jaguar/generator/parser/import.dart'; -import 'package:jaguar/generator/internal/element/import.dart'; +import 'package:source_gen_help/import.dart'; import 'package:jaguar/generator/parser/exception_handler/import.dart'; part 'route_info.dart'; diff --git a/lib/generator/writer/import.dart b/lib/generator/writer/import.dart index 1a4e0583..f9e3e200 100644 --- a/lib/generator/writer/import.dart +++ b/lib/generator/writer/import.dart @@ -6,7 +6,7 @@ import 'package:analyzer/src/dart/ast/ast.dart'; import 'package:analyzer/src/generated/utilities_dart.dart'; import 'package:jaguar/generator/parser/import.dart'; -import 'package:jaguar/generator/internal/element/import.dart'; +import 'package:source_gen_help/import.dart'; part 'pre_dual_inter.dart'; part 'post_dual_inter.dart'; diff --git a/lib/generator/writer/interceptor_class_instantiator.dart b/lib/generator/writer/interceptor_class_instantiator.dart index 0529cb82..e717d257 100644 --- a/lib/generator/writer/interceptor_class_instantiator.dart +++ b/lib/generator/writer/interceptor_class_instantiator.dart @@ -22,16 +22,15 @@ class InterceptorClassDecl { _w.write(_i.interceptor.name + " "); _w.write(_i.genInstanceName + " = new "); - ElementAnnotationImpl element = _i.elememt; - ConstructorElementImpl construct = element.element; + ElementAnnotationImpl element = _i.element; + ConstructorElementWrap construct = + new ConstructorElementWrap(element.element); AnnotationImpl ast = element.annotationAst; _w.write(ast.name); _w.write('('); bool hasAssignedState = false; - Map optionalParams = {}; - int index = 0; for (index = 0; index < construct.parameters.length; index++) { ParameterElement param = construct.parameters[index]; @@ -49,29 +48,33 @@ class InterceptorClassDecl { if (index < construct.parameters.length && index < ast.arguments.arguments.length) { if (construct.parameters[index].parameterKind == ParameterKind.NAMED) { + Map paramsAssigned = {}; + for (; index < ast.arguments.arguments.length; index++) { NamedExpressionImpl astItem = ast.arguments.arguments[index]; _w.write(astItem); _w.write(', '); - if (astItem.name.label.name == 'state') { - hasAssignedState = true; - } - } - } else { - for (; index < ast.arguments.arguments.length; index++) { - ParameterElement param = construct.parameters[index]; + final String name = astItem.name.label.name; - final astItem = ast.arguments.arguments[index]; + paramsAssigned[name] = construct.findOptionalParamByName(name); - _w.write(astItem); - _w.write(', '); - - if (param.name == 'state') { + if (name == 'state') { hasAssignedState = true; } } + + _i.interceptor.params.forEach((String name, DartTypeWrap type) { + if (construct.findOptionalParamByName(name) != null) { + //TODO check if it is assignable + _w.write(name + ': '); + _w.write('new ' + type.displayName + '(),'); + } + }); + } else { + hasAssignedState = true; + //We don't inject in names params } } diff --git a/lib/src/annotations/interceptors.dart b/lib/src/annotations/interceptors.dart index e8d842a1..b1ec5914 100644 --- a/lib/src/annotations/interceptors.dart +++ b/lib/src/annotations/interceptors.dart @@ -30,5 +30,7 @@ class Interceptor { /// Id of the interceptor final String id; - const Interceptor({this.id}); + final Map params; + + const Interceptor({this.id, this.params}); } diff --git a/lib/src/console/import.dart b/lib/src/console/import.dart deleted file mode 100644 index bea40c85..00000000 --- a/lib/src/console/import.dart +++ /dev/null @@ -1,3 +0,0 @@ -library jaguar.src.console; - -class Route {} diff --git a/lib/src/snap/import.dart b/lib/src/snap/import.dart deleted file mode 100644 index 3bbf6d11..00000000 --- a/lib/src/snap/import.dart +++ /dev/null @@ -1,17 +0,0 @@ -library jaguar.src.snap; - -/* TODO -import 'dart:async'; - -class Resource { - const Resource(); - - Future getByID() async { - //TODO - } - - Future> getAll() async { - //TODO - } -} -*/ diff --git a/pubspec.yaml b/pubspec.yaml index ffa1ba77..281f5306 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,9 @@ dependencies: mime: ">=0.9.3 <0.10.0" logging: intl: + cookies: + source_gen_help: + git: https://github.com/Jaguar-dart/source_gen_help.git dev_dependencies: test: 0.12.15+10 - cookies: diff --git a/test/interceptor/param/param.dart b/test/interceptor/param/param.dart new file mode 100644 index 00000000..f841e3b0 --- /dev/null +++ b/test/interceptor/param/param.dart @@ -0,0 +1,62 @@ +library test.interceptor.param; + +import 'dart:io'; +import 'dart:async'; +import 'package:test/test.dart'; +import 'package:jaguar/jaguar.dart'; +import 'package:jaguar/testing.dart'; + +part 'param.g.dart'; + +abstract class Checker { + String get who; +} + +class CheckerImpl implements Checker { + String get who => 'CheckerImpl'; + + CheckerImpl(); +} + +@InterceptorClass() +class WithParam extends Interceptor { + final CheckerImpl checker; + + const WithParam({this.checker, Map params}) + : super(params: params); + + String pre() { + return checker.who; + } +} + +@Api(path: '/api') +class ExampleApi extends Object with _$JaguarExampleApi { + @Route('/user', methods: const ['GET']) + @WithParam(params: const {#checker: CheckerImpl}) + @Input(WithParam) + String getUser(String who) => who; +} + +void main() { + group('Interceptor.Param', () { + JaguarMock mock; + setUp(() { + Configuration config = new Configuration(); + config.addApi(new ExampleApi()); + mock = new JaguarMock(config); + }); + + tearDown(() {}); + + test('ParamInjection', () async { + Uri uri = new Uri.http('localhost:8080', '/api/user'); + MockHttpRequest rq = new MockHttpRequest(uri); + MockHttpResponse response = await mock.handleRequest(rq); + + expect(response.mockContent, 'CheckerImpl'); + expect(response.headers.toMap, {}); + expect(response.statusCode, 200); + }); + }); +} diff --git a/test/interceptor/param/param.g.dart b/test/interceptor/param/param.g.dart new file mode 100644 index 00000000..fbd1029c --- /dev/null +++ b/test/interceptor/param/param.g.dart @@ -0,0 +1,41 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of test.interceptor.param; + +// ************************************************************************** +// Generator: ApiGenerator +// Target: class ExampleApi +// ************************************************************************** + +abstract class _$JaguarExampleApi implements ApiInterface { + static const List _routes = const [ + const Route('/user', methods: const ['GET']) + ]; + + String getUser(String who); + + Future handleApiRequest(HttpRequest request) async { + PathParams pathParams = new PathParams(); + bool match = false; + + match = + _routes[0].match(request.uri.path, request.method, '/api', pathParams); + if (match) { + WithParam iWithParam = new WithParam( + params: const {#checker: CheckerImpl}, + checker: new CheckerImpl(), + ); + String rWithParam = iWithParam.pre(); + String rRouteResponse; + rRouteResponse = getUser( + rWithParam, + ); + request.response.statusCode = 200; + request.response.write(rRouteResponse.toString()); + await request.response.close(); + return true; + } + + return false; + } +} diff --git a/test/jaguar/group/main.dart b/test/jaguar/group/main.dart index ce24583a..850e823f 100644 --- a/test/jaguar/group/main.dart +++ b/test/jaguar/group/main.dart @@ -33,7 +33,7 @@ class ExampleApi extends Object } void main() { - group('route', () { + group('Group', () { JaguarMock mock; setUp(() { Configuration config = new Configuration(); diff --git a/test/test_all.dart b/test/test_all.dart index f3716f36..44c3efc1 100644 --- a/test/test_all.dart +++ b/test/test_all.dart @@ -1,9 +1,11 @@ import 'jaguar/route/route.dart' as route; import 'jaguar/group/main.dart' as groupNormal; import 'jaguar/websocket/websocket.dart' as websocket; +import 'interceptor/param/param.dart' as interceptorParam; void main() { route.main(); groupNormal.main(); websocket.main(); + interceptorParam.main(); }