platform/packages/configuration/lib/angel_configuration.dart

142 lines
4.1 KiB
Dart
Raw Normal View History

2016-04-22 01:17:31 +00:00
library angel_configuration;
2020-05-03 15:24:22 +00:00
import 'dart:async';
2016-04-22 01:17:31 +00:00
import 'package:angel_framework/angel_framework.dart';
2017-06-14 18:48:13 +00:00
import 'package:dotenv/dotenv.dart' as dotenv;
2017-09-25 13:50:56 +00:00
import 'package:file/file.dart';
2017-08-16 00:29:13 +00:00
import 'package:merge_map/merge_map.dart';
2016-04-22 01:17:31 +00:00
import 'package:yaml/yaml.dart';
2020-05-03 15:24:22 +00:00
Future<void> _loadYamlFile(Map map, File yamlFile, Map<String, String> env,
void Function(String msg) warn) async {
2016-05-02 23:35:21 +00:00
if (await yamlFile.exists()) {
2017-06-14 18:48:13 +00:00
var config = loadYaml(await yamlFile.readAsString());
2017-09-25 13:50:56 +00:00
2017-06-14 18:48:13 +00:00
if (config is! Map) {
2018-12-31 15:41:56 +00:00
warn(
'The configuration at "${yamlFile.absolute.path}" is not a Map. Refusing to load it.');
2017-06-14 18:48:13 +00:00
return;
}
2017-08-16 00:29:13 +00:00
2020-05-03 15:24:22 +00:00
var out = {};
2017-08-16 00:29:13 +00:00
2016-04-22 01:17:31 +00:00
for (String key in config.keys) {
2018-12-31 15:41:56 +00:00
out[key] = _applyEnv(config[key], env ?? {}, warn);
2016-04-22 01:17:31 +00:00
}
2017-08-16 00:29:13 +00:00
2018-12-31 15:41:56 +00:00
map.addAll(mergeMap(
2017-08-16 00:29:13 +00:00
[
2018-12-31 15:41:56 +00:00
map,
2017-08-16 00:29:13 +00:00
out,
],
acceptNull: true,
));
2016-04-22 01:17:31 +00:00
}
}
2020-05-03 15:24:22 +00:00
Object _applyEnv(
var v, Map<String, String> env, void Function(String msg) warn) {
2017-06-14 18:48:13 +00:00
if (v is String) {
if (v.startsWith(r'$') && v.length > 1) {
var key = v.substring(1);
2020-05-03 15:24:22 +00:00
if (env.containsKey(key)) {
2017-06-14 18:48:13 +00:00
return env[key];
2020-05-03 15:24:22 +00:00
} else {
2018-12-31 15:41:56 +00:00
warn(
2017-06-14 18:48:13 +00:00
'Your configuration calls for loading the value of "$key" from the system environment, but it is not defined. Defaulting to `null`.');
return null;
}
2020-05-03 15:24:22 +00:00
} else {
2017-06-14 18:48:13 +00:00
return v;
2020-05-03 15:24:22 +00:00
}
2017-06-14 18:48:13 +00:00
} else if (v is Iterable) {
2018-12-31 15:41:56 +00:00
return v.map((x) => _applyEnv(x, env ?? {}, warn)).toList();
2017-06-14 18:48:13 +00:00
} else if (v is Map) {
return v.keys
2018-12-31 15:41:56 +00:00
.fold<Map>({}, (out, k) => out..[k] = _applyEnv(v[k], env ?? {}, warn));
2020-05-03 15:24:22 +00:00
} else {
2017-06-14 18:48:13 +00:00
return v;
2020-05-03 15:24:22 +00:00
}
2017-06-14 18:48:13 +00:00
}
2018-12-31 15:41:56 +00:00
/// Loads [configuration], and returns a [Map].
///
/// You can override [onWarning]; otherwise, configuration errors will throw.
Future<Map> loadStandaloneConfiguration(FileSystem fileSystem,
2020-05-03 15:24:22 +00:00
{String directoryPath = './config',
2018-12-31 15:41:56 +00:00
String overrideEnvironmentName,
String envPath,
2020-05-03 15:24:22 +00:00
void Function(String message) onWarning}) async {
var sourceDirectory = fileSystem.directory(directoryPath);
2018-12-31 15:41:56 +00:00
var env = dotenv.env;
var envFile = sourceDirectory.childFile(envPath ?? '.env');
if (await envFile.exists()) {
dotenv.load(envFile.absolute.uri.toFilePath());
}
2020-05-03 15:24:22 +00:00
var environmentName = env['ANGEL_ENV'] ?? 'development';
2018-12-31 15:41:56 +00:00
if (overrideEnvironmentName != null) {
environmentName = overrideEnvironmentName;
}
2020-05-03 15:24:22 +00:00
onWarning ??= (String message) => throw StateError(message);
2018-12-31 15:41:56 +00:00
var out = {};
var defaultYaml = sourceDirectory.childFile('default.yaml');
await _loadYamlFile(out, defaultYaml, env, onWarning);
2020-05-03 15:24:22 +00:00
var configFilePath = '$environmentName.yaml';
2018-12-31 15:41:56 +00:00
var configFile = sourceDirectory.childFile(configFilePath);
await _loadYamlFile(out, configFile, env, onWarning);
return out;
}
2017-06-14 18:48:13 +00:00
/// Dynamically loads application configuration from configuration files.
2017-08-16 00:29:13 +00:00
///
/// You can modify which [directoryPath] to search in, or explicitly
/// load from a [overrideEnvironmentName].
///
/// You can also specify a custom [envPath] to load system configuration from.
2018-07-12 16:40:54 +00:00
AngelConfigurer configuration(FileSystem fileSystem,
2020-05-03 15:24:22 +00:00
{String directoryPath = './config',
2017-08-16 00:29:13 +00:00
String overrideEnvironmentName,
String envPath}) {
2016-05-02 23:35:21 +00:00
return (Angel app) async {
2020-05-03 15:24:22 +00:00
var sourceDirectory = fileSystem.directory(directoryPath);
2017-06-14 18:48:13 +00:00
var env = dotenv.env;
2017-09-25 13:50:56 +00:00
var envFile = sourceDirectory.childFile(envPath ?? '.env');
2017-06-14 18:48:13 +00:00
if (await envFile.exists()) {
try {
dotenv.load(envFile.absolute.uri.toFilePath());
} catch (_) {
2017-09-25 13:50:56 +00:00
app.logger?.warning(
2017-06-14 18:48:13 +00:00
'WARNING: Found an environment configuration at ${envFile.absolute.path}, but it was invalidly formatted. Refusing to load it.');
}
}
2020-05-03 15:24:22 +00:00
var environmentName = env['ANGEL_ENV'] ?? 'development';
2016-04-22 01:17:31 +00:00
if (overrideEnvironmentName != null) {
environmentName = overrideEnvironmentName;
}
2018-12-31 15:41:56 +00:00
void warn(String message) {
app.logger?.warning('WARNING: $message');
}
2017-09-25 13:50:56 +00:00
var defaultYaml = sourceDirectory.childFile('default.yaml');
2018-12-31 15:41:56 +00:00
await _loadYamlFile(app.configuration, defaultYaml, env, warn);
2016-04-22 01:17:31 +00:00
2020-05-03 15:24:22 +00:00
var configFilePath = '$environmentName.yaml';
2017-09-25 13:50:56 +00:00
var configFile = sourceDirectory.childFile(configFilePath);
2016-04-22 01:17:31 +00:00
2018-12-31 15:41:56 +00:00
await _loadYamlFile(app.configuration, configFile, env, warn);
2016-04-22 01:17:31 +00:00
};
2017-06-14 18:48:13 +00:00
}