import 'dart:io'; import 'package:args/command_runner.dart'; import 'package:code_builder/code_builder.dart'; import 'package:console/console.dart'; import 'package:dart_style/dart_style.dart'; import 'package:inflection/inflection.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:recase/recase.dart'; import 'service_generators/service_generators.dart'; import 'deprecated.dart'; import 'init.dart' show preBuild; class ServiceCommand extends Command { final TextPen _pen = new TextPen(); @override String get name => 'service'; @override String get description => 'Creates a new service within the given project.'; @override run() async { warnDeprecated(this.name, _pen); var pubspec = await Pubspec.load(Directory.current); var name = await readInput('Name of Service (not plural): '); var chooser = new Chooser( serviceGenerators.map((g) => g.name).toList(), message: 'What type of service would you like to create? '); var type = await chooser.choose(); print('Wrap this service in a TypedService? (slight performance cost)'); chooser = new Chooser(['Yes', 'No']); var typed = (await chooser.choose()) == 'Yes'; var generator = serviceGenerators.firstWhere((g) => g.name == type, orElse: () => null); if (generator == null) { _pen.blue(); _pen('${Icon.STAR} \'$type\' services are not yet implemented. :('); _pen(); } else { var rc = new ReCase(name); name = rc.pascalCase; var lower = rc.snakeCase; var servicesDir = new Directory('lib/src/services'); var serviceFile = new File.fromUri(servicesDir.uri.resolve('$lower.dart')); var testDir = new Directory('test/services'); var testFile = new File.fromUri(testDir.uri.resolve('${lower}_test.dart')); if (!await servicesDir.exists()) await servicesDir.create(recursive: true); if (!await testDir.exists()) await testDir.create(recursive: true); var fmt = new DartFormatter(); await serviceFile .writeAsString(_generateService(generator, name, lower, typed)); await testFile.writeAsString(_generateTests(pubspec, lower, fmt)); var runConfig = new File('./.idea/runConfigurations/${name}_Tests.xml'); if (!await runConfig.exists()) { await runConfig.create(recursive: true); await runConfig.writeAsString(_generateRunConfiguration(name, lower)); } if (generator.createsModel == true || typed == true) { await _generateModel(pubspec, name, lower, fmt); } if (generator.createsValidator == true) { await _generateValidator(pubspec, lower, rc, fmt); } if (generator.exportedInServiceLibrary == true || typed == true) { var serviceLibrary = new File('lib/src/models/models.dart'); await serviceLibrary.writeAsString("\nexport '$lower.dart';", mode: FileMode.APPEND); } if (generator.shouldRunBuild == true) { await preBuild(Directory.current); } _pen.green(); _pen('${Icon.CHECKMARK} Successfully generated service $name.'); _pen(); } } String _generateService( ServiceGenerator generator, String name, String lower, bool typed) { var lib = new LibraryBuilder(); /* import 'package:angel_framework/angel_framework.dart'; import '../models/$lower.dart'; export '../models/$lower.dart'; */ lib.addMember(new ImportBuilder('package:angel_common/angel_common.dart')); generator.applyToLibrary(lib, name, lower); if (generator.createsModel == true || typed) { lib ..addMember(new ImportBuilder('../models/$lower.dart')) ..addMember(new ExportBuilder('../models/$lower.dart')); } // configureServer() {} var configureServer = new MethodBuilder('configureServer', returnType: new TypeBuilder('AngelConfigurer')); generator.applyToConfigureServer(configureServer, name, lower); // return (Angel app) async {} var closure = new MethodBuilder.closure(modifier: MethodModifier.asAsync) ..addPositional(parameter('app', [new TypeBuilder('Angel')])); generator.beforeService(closure, name, lower); // app.use('/api/todos', new MapService()); var service = generator.createInstance(closure, name, lower); if (typed == true) { service = new TypeBuilder('TypedService', genericTypes: [new TypeBuilder(name)]) .newInstance([service]); } closure.addStatement(reference('app') .invoke('use', [literal('/api/${pluralize(lower)}'), service])); if (generator.injectsSingleton == true) { closure.addStatement(varField('service', value: reference('app') .invoke('service', [literal('/api/${pluralize(lower)}')]).castAs( new TypeBuilder('HookedService')))); closure.addStatement(reference('app') .property('container') .invoke('singleton', [reference('service').property('inner')])); } configureServer.addStatement(closure.asReturn()); lib.addMember(configureServer); return prettyToSource(lib.buildAst()); } _generateModel( Pubspec pubspec, String name, String lower, DartFormatter fmt) async { var file = new File('lib/src/models/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); await file.writeAsString(fmt.format(''' library ${pubspec.name}.models.$lower; import 'package:angel_model/angel_model.dart'; class $name extends Model { @override String id; String name, description; @override DateTime createdAt, updatedAt; $name({this.id, this.name, this.description, this.createdAt, this.updatedAt}); } ''')); } _generateValidator( Pubspec pubspec, String lower, ReCase rc, DartFormatter fmt) async { var file = new File('lib/src/validators/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); await file.writeAsString(fmt.format(''' library ${pubspec.name}.validtors.$lower; import 'package:angel_validate/angel_validate.dart'; final Validator ${rc.camelCase} = new Validator({ 'name': [isString, isNotEmpty], 'description': [isString, isNotEmpty] }); final Validator create${rc.pascalCase} = ${rc.camelCase}.extend({}) ..requiredFields.addAll(['name', 'description']); ''')); } _generateRunConfiguration(String name, String lower) { return ''' ''' .trim(); } _generateTests(Pubspec pubspec, String lower, DartFormatter fmt) { return fmt.format(''' import 'dart:io'; import 'package:${pubspec.name}/${pubspec.name}.dart'; import 'package:angel_common/angel_common.dart'; import 'package:angel_test/angel_test.dart'; import 'package:test/test.dart'; main() async { Angel app; TestClient client; setUp(() async { app = await createServer(); client = await connectTo(app); }); tearDown(() async { await client.close(); app = null; }); test('index via REST', () async { var response = await client.get('/api/${pluralize(lower)}'); expect(response, hasStatus(HttpStatus.OK)); }); test('Index ${pluralize(lower)}', () async { var ${pluralize(lower)} = await client.service('api/${pluralize(lower)}').index(); print(${pluralize(lower)}); }); } '''); } }