platform/packages/seeder/lib/angel_seeder.dart

142 lines
4.1 KiB
Dart
Raw Normal View History

2021-02-21 02:47:23 +00:00
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].
2021-06-20 12:37:20 +00:00
typedef FakerCallback = Function(Faker faker);
2021-02-21 02:47:23 +00:00
/// Used to seed nested objects.
2021-06-20 12:37:20 +00:00
typedef SeederCallback<T> = Function(T created,
Function(Pattern path, SeederConfiguration configuration, {bool? verbose}));
2021-02-21 02:47:23 +00:00
/// Seeds the given service in development.
AngelConfigurer seed<T>(
Pattern servicePath,
SeederConfiguration<T> configuration, {
2021-06-20 12:37:20 +00:00
bool verbose = false,
2021-02-21 02:47:23 +00:00
}) {
return (Angel app) async {
if (configuration.runInProduction != true) return;
2021-06-20 12:37:20 +00:00
if (!app.services.containsKey(servicePath)) {
throw ArgumentError(
2021-02-21 02:47:23 +00:00
"App does not contain a service at path '$servicePath'.");
2021-06-20 12:37:20 +00:00
}
2021-02-21 02:47:23 +00:00
if (configuration.disabled == true) {
print("Service '$servicePath' will not be seeded.");
return;
}
var service = app.findService(servicePath);
2021-06-20 12:37:20 +00:00
var faker = Faker();
2021-02-21 02:47:23 +00:00
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();
2021-06-20 12:37:20 +00:00
} else if (value is Map) {
2021-02-21 02:47:23 +00:00
return map..[key] = _buildTemplate(value);
2021-06-20 12:37:20 +00:00
} else {
2021-02-21 02:47:23 +00:00
return map..[key] = value;
2021-06-20 12:37:20 +00:00
}
2021-02-21 02:47:23 +00:00
});
}
2021-06-20 12:37:20 +00:00
Future<Null> Function(SeederConfiguration configuration) _buildSeeder(
Service? service,
{bool? verbose}) {
2021-02-21 02:47:23 +00:00
return (SeederConfiguration configuration) async {
2021-06-20 12:37:20 +00:00
if (configuration.delete == true) await service!.remove(null);
2021-02-21 02:47:23 +00:00
2021-06-20 12:37:20 +00:00
var count = configuration.count;
var rnd = Random();
2021-02-21 02:47:23 +00:00
if (count < 1) count = 1;
2021-06-20 12:37:20 +00:00
for (var i = 0; i < count; i++) {
Future _gen(template) async {
2021-02-21 02:47:23 +00:00
var data = template;
if (data is Map) {
data = _buildTemplate(data);
} else if (data is Faker) {
data = template(faker);
}
2021-06-20 12:37:20 +00:00
var params = <String, dynamic>{}..addAll(configuration.params);
var result = await service!.create(data, params);
2021-02-21 02:47:23 +00:00
if (configuration.callback != null) {
2021-06-20 12:37:20 +00:00
await configuration.callback!(result,
2021-02-21 02:47:23 +00:00
(Pattern path, SeederConfiguration configuration,
2021-06-20 12:37:20 +00:00
{bool? verbose}) {
2021-02-21 02:47:23 +00:00
return _buildSeeder(app.findService(path),
verbose: verbose == true)(configuration);
});
}
}
if (configuration.template != null) {
await _gen(configuration.template);
2021-06-20 12:37:20 +00:00
} else if (configuration.templates.isNotEmpty == true) {
2021-02-21 02:47:23 +00:00
var template = configuration.templates
.elementAt(rnd.nextInt(configuration.templates.length));
await _gen(template);
2021-06-20 12:37:20 +00:00
} else {
throw ArgumentError(
2021-02-21 02:47:23 +00:00
'Configuration for service \'$servicePath\' must define at least one template.');
2021-06-20 12:37:20 +00:00
}
2021-02-21 02:47:23 +00:00
}
2021-06-20 12:37:20 +00:00
if (verbose == true) {
2021-02-21 02:47:23 +00:00
print('Created $count object(s) in service \'$servicePath\'.');
2021-06-20 12:37:20 +00:00
}
2021-02-21 02:47:23 +00:00
};
}
await _buildSeeder(service, verbose: verbose == true)(configuration);
};
}
/// Configures the seeder.
class SeederConfiguration<T> {
/// Optional callback on creation.
2021-06-20 12:37:20 +00:00
final SeederCallback<T>? callback;
2021-02-21 02:47:23 +00:00
/// 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.
2021-06-20 12:37:20 +00:00
final Map<String, dynamic> params;
2021-02-21 02:47:23 +00:00
/// 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,
2021-06-20 12:37:20 +00:00
this.count = 1,
this.delete = true,
this.disabled = false,
this.params = const {},
this.runInProduction = false,
2021-02-21 02:47:23 +00:00
this.template,
2021-06-20 12:37:20 +00:00
this.templates = const []});
2021-02-21 02:47:23 +00:00
}