platform/packages/seeder/lib/angel_seeder.dart
2021-06-20 20:37:20 +08:00

141 lines
4.1 KiB
Dart

import 'dart:math';
import 'package:angel_framework/angel_framework.dart';
import 'package:faker/faker.dart';
export 'package:faker/faker.dart';
/// Generates data using a [Faker].
typedef FakerCallback = Function(Faker faker);
/// Used to seed nested objects.
typedef SeederCallback<T> = Function(T created,
Function(Pattern path, SeederConfiguration configuration, {bool? verbose}));
/// Seeds the given service in development.
AngelConfigurer seed<T>(
Pattern servicePath,
SeederConfiguration<T> configuration, {
bool verbose = false,
}) {
return (Angel app) async {
if (configuration.runInProduction != true) return;
if (!app.services.containsKey(servicePath)) {
throw ArgumentError(
"App does not contain a service at path '$servicePath'.");
}
if (configuration.disabled == true) {
print("Service '$servicePath' will not be seeded.");
return;
}
var service = app.findService(servicePath);
var faker = Faker();
Map _buildTemplate(Map data) {
return data.keys.fold({}, (map, key) {
var value = data[key];
if (value is FakerCallback) {
return map..[key] = value(faker);
} else if (value is Function) {
return map..[key] = value();
} else if (value is Map) {
return map..[key] = _buildTemplate(value);
} else {
return map..[key] = value;
}
});
}
Future<Null> Function(SeederConfiguration configuration) _buildSeeder(
Service? service,
{bool? verbose}) {
return (SeederConfiguration configuration) async {
if (configuration.delete == true) await service!.remove(null);
var count = configuration.count;
var rnd = Random();
if (count < 1) count = 1;
for (var i = 0; i < count; i++) {
Future _gen(template) async {
var data = template;
if (data is Map) {
data = _buildTemplate(data);
} else if (data is Faker) {
data = template(faker);
}
var params = <String, dynamic>{}..addAll(configuration.params);
var result = await service!.create(data, params);
if (configuration.callback != null) {
await configuration.callback!(result,
(Pattern path, SeederConfiguration configuration,
{bool? verbose}) {
return _buildSeeder(app.findService(path),
verbose: verbose == true)(configuration);
});
}
}
if (configuration.template != null) {
await _gen(configuration.template);
} else if (configuration.templates.isNotEmpty == true) {
var template = configuration.templates
.elementAt(rnd.nextInt(configuration.templates.length));
await _gen(template);
} else {
throw ArgumentError(
'Configuration for service \'$servicePath\' must define at least one template.');
}
}
if (verbose == true) {
print('Created $count object(s) in service \'$servicePath\'.');
}
};
}
await _buildSeeder(service, verbose: verbose == true)(configuration);
};
}
/// Configures the seeder.
class SeederConfiguration<T> {
/// Optional callback on creation.
final SeederCallback<T>? callback;
/// Number of objects to seed.
final int count;
/// If `true`, all records in the service are deleted before seeding.
final bool delete;
/// If `true`, seeding will not occur.
final bool disabled;
/// Optional service parameters to be passed.
final Map<String, dynamic> params;
/// Unless this is `true`, the seeder will not run in production.
final bool runInProduction;
/// A data template to build from.
final template;
/// A set of templates to choose from.
final Iterable templates;
SeederConfiguration(
{this.callback,
this.count = 1,
this.delete = true,
this.disabled = false,
this.params = const {},
this.runInProduction = false,
this.template,
this.templates = const []});
}