Add 'packages/configuration/' from commit 'a7902feaa0a360848777b1e7b7c20d9ff848d19b'

git-subtree-dir: packages/configuration
git-subtree-mainline: c77d25f6d0
git-subtree-split: a7902feaa0
This commit is contained in:
Tobe O 2020-02-15 18:28:56 -05:00
commit bda70d18e3
15 changed files with 446 additions and 0 deletions

79
packages/configuration/.gitignore vendored Normal file
View file

@ -0,0 +1,79 @@
# Created by .ignore support plugin (hsz.mobi)
### JetBrains template
.idea
*.iml
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/workspace.xml
.idea/tasks.xml
.idea/dictionaries
.idea/vcs.xml
.idea/jsLibraryMappings.xml
# Sensitive or high-churn files:
.idea/dataSources.ids
.idea/dataSources.xml
.idea/dataSources.local.xml
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
# Gradle:
.idea/gradle.xml
.idea/libraries
# Mongo Explorer plugin:
.idea/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### Dart template
# See https://www.dartlang.org/tools/private-files.html
# Files and directories created by pub
.buildlog
.packages
.project
.pub/
build/
**/packages/
# Files created by dart2js
# (Most Dart developers will use pub build to compile Dart, use/modify these
# rules if you intend to use dart2js directly
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
# differentiate from explicit Javascript files)
*.dart.js
*.part.js
*.js.deps
*.js.map
*.info.json
# Directory created by dartdoc
doc/api/
# Don't commit pubspec lock file
# (Library packages only! Remove pattern if developing an application package)
pubspec.lock
.dart_tool

View file

@ -0,0 +1,3 @@
language: dart
dart:
- stable

View file

@ -0,0 +1,18 @@
# 2.1.0
* Add `loadStandaloneConfiguration`.
# 2.0.0
* Use Angel 2.
# 1.2.0-rc.0
* Removed the `Configuration` class.
* Removed the `ConfigurationTransformer` class.
* Use `Map` casting to prevent runtime cast errors.
# 1.1.0 (Retroactive changelog)
* Use `package:file`.
# 1.0.5
* Now using `package:merge_map` to merge configurations. Resolves
[#5](https://github.com/angel-dart/configuration/issues/5).
* You can now specify a custom `envPath`.

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 angel-dart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,74 @@
# configuration
[![Pub](https://img.shields.io/pub/v/angel_configuration.svg)](https://pub.dartlang.org/packages/angel_configuration)
[![build status](https://travis-ci.org/angel-dart/configuration.svg)](https://travis-ci.org/angel-dart/configuration)
Automatic YAML configuration loader for Angel.
# About
Any web app needs different configuration for development and production. This plugin will search
for a `config/default.yaml` file. If it is found, configuration from it is loaded into `app.configuration`.
Then, it will look for a `config/$ANGEL_ENV` file. (i.e. config/development.yaml). If this found, all of its
configuration be loaded, and will override anything loaded from the `default.yaml` file. This allows for your
app to work under different conditions without you re-coding anything. :)
# Installation
In `pubspec.yaml`:
```yaml
dependencies:
angel_configuration: ^2.0.0
```
# Usage
**Example Configuration**
```yaml
# Define normal YAML objects
some_key: foo
this_is_a_map:
a_string: "string"
another_string: "string"
```
You can also load configuration from the environment:
```yaml
# Loaded from the environment
system_path: $PATH
```
If a `.env` file is present in your configuration directory, then it will be loaded before
applying YAML configuration.
**Server-side**
Call `configuration()`. The loaded configuration will be available in your application's
`configuration` map.
`configuration` also accepts a `sourceDirectory` or `overrideEnvironmentName` parameter.
The former will allow you to search in a directory other than `config`, and the latter lets you
override `$ANGEL_ENV` by specifying a specific configuration name to look for (i.e. `production`).
This package uses
[`package:merge_map`](https://github.com/thosakwe/merge_map)
internally, so existing configurations can be deeply merged.
Example:
```yaml
# default.yaml
foo:
bar: baz
quux: hello
# production.yaml
foo:
quux: goodbye
yellow: submarine
# Propagates to:
foo:
bar: baz
quux: goodbye
yellow: submarine
```

View file

@ -0,0 +1,3 @@
analyzer:
strong-mode:
implicit-casts: true

View file

@ -0,0 +1,9 @@
import 'package:angel_configuration/angel_configuration.dart';
import 'package:angel_framework/angel_framework.dart';
import 'package:file/local.dart';
main() async {
var app = new Angel();
var fs = const LocalFileSystem();
await app.configure(configuration(fs));
}

View file

@ -0,0 +1,136 @@
library angel_configuration;
import 'package:angel_framework/angel_framework.dart';
import 'package:dotenv/dotenv.dart' as dotenv;
import 'package:file/file.dart';
import 'package:merge_map/merge_map.dart';
import 'package:yaml/yaml.dart';
_loadYamlFile(Map map, File yamlFile, Map<String, String> env,
void warn(String msg)) async {
if (await yamlFile.exists()) {
var config = loadYaml(await yamlFile.readAsString());
if (config is! Map) {
warn(
'The configuration at "${yamlFile.absolute.path}" is not a Map. Refusing to load it.');
return;
}
Map<String, dynamic> out = {};
for (String key in config.keys) {
out[key] = _applyEnv(config[key], env ?? {}, warn);
}
map.addAll(mergeMap(
[
map,
out,
],
acceptNull: true,
));
}
}
_applyEnv(var v, Map<String, String> env, void warn(String msg)) {
if (v is String) {
if (v.startsWith(r'$') && v.length > 1) {
var key = v.substring(1);
if (env.containsKey(key))
return env[key];
else {
warn(
'Your configuration calls for loading the value of "$key" from the system environment, but it is not defined. Defaulting to `null`.');
return null;
}
} else
return v;
} else if (v is Iterable) {
return v.map((x) => _applyEnv(x, env ?? {}, warn)).toList();
} else if (v is Map) {
return v.keys
.fold<Map>({}, (out, k) => out..[k] = _applyEnv(v[k], env ?? {}, warn));
} else
return v;
}
/// Loads [configuration], and returns a [Map].
///
/// You can override [onWarning]; otherwise, configuration errors will throw.
Future<Map> loadStandaloneConfiguration(FileSystem fileSystem,
{String directoryPath: "./config",
String overrideEnvironmentName,
String envPath,
void onWarning(String message)}) async {
Directory sourceDirectory = fileSystem.directory(directoryPath);
var env = dotenv.env;
var envFile = sourceDirectory.childFile(envPath ?? '.env');
if (await envFile.exists()) {
dotenv.load(envFile.absolute.uri.toFilePath());
}
String environmentName = env['ANGEL_ENV'] ?? 'development';
if (overrideEnvironmentName != null) {
environmentName = overrideEnvironmentName;
}
onWarning ??= (String message) => throw new StateError(message);
var out = {};
var defaultYaml = sourceDirectory.childFile('default.yaml');
await _loadYamlFile(out, defaultYaml, env, onWarning);
String configFilePath = "$environmentName.yaml";
var configFile = sourceDirectory.childFile(configFilePath);
await _loadYamlFile(out, configFile, env, onWarning);
return out;
}
/// Dynamically loads application configuration from configuration files.
///
/// 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.
AngelConfigurer configuration(FileSystem fileSystem,
{String directoryPath: "./config",
String overrideEnvironmentName,
String envPath}) {
return (Angel app) async {
Directory sourceDirectory = fileSystem.directory(directoryPath);
var env = dotenv.env;
var envFile = sourceDirectory.childFile(envPath ?? '.env');
if (await envFile.exists()) {
try {
dotenv.load(envFile.absolute.uri.toFilePath());
} catch (_) {
app.logger?.warning(
'WARNING: Found an environment configuration at ${envFile.absolute.path}, but it was invalidly formatted. Refusing to load it.');
}
}
String environmentName = env['ANGEL_ENV'] ?? 'development';
if (overrideEnvironmentName != null) {
environmentName = overrideEnvironmentName;
}
void warn(String message) {
app.logger?.warning('WARNING: $message');
}
var defaultYaml = sourceDirectory.childFile('default.yaml');
await _loadYamlFile(app.configuration, defaultYaml, env, warn);
String configFilePath = "$environmentName.yaml";
var configFile = sourceDirectory.childFile(configFilePath);
await _loadYamlFile(app.configuration, configFile, env, warn);
};
}

View file

@ -0,0 +1 @@
external dynamic config(String key);

View file

@ -0,0 +1,16 @@
name: angel_configuration
description: Automatic YAML application configuration loader for Angel, with .env support.
version: 2.1.0
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_configuration
environment:
sdk: ">=2.0.0-dev <3.0.0"
dependencies:
angel_framework: ^2.0.0-alpha
dotenv: ^1.0.0
file: ^5.0.0
merge_map: ^1.0.0
yaml: ^2.0.0
dev_dependencies:
io: ^0.3.2
test: ^1.0.0

View file

@ -0,0 +1,68 @@
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_configuration/angel_configuration.dart';
import 'package:file/local.dart';
import 'package:io/ansi.dart';
import 'package:test/test.dart';
main() async {
// Note: Set ANGEL_ENV to 'development'
var app = new Angel();
var fileSystem = const LocalFileSystem();
await app.configure(configuration(
fileSystem,
directoryPath: './test/config',
));
test('standalone', () async {
var config = await loadStandaloneConfiguration(
fileSystem,
directoryPath: './test/config',
onWarning: (msg) {
print(yellow.wrap('STANDALONE WARNING: $msg'));
},
);
print('Standalone: $config');
expect(config, {
"angel": {"framework": "cool"},
"must_be_null": null,
"artist": "Timberlake",
"merge": {"map": true, "hello": "world"},
"set_via": "default",
"hello": "world",
"foo": {"version": "bar"}
});
});
test('can load based on ANGEL_ENV', () async {
expect(app.configuration['hello'], equals('world'));
expect(app.configuration['foo']['version'], equals('bar'));
});
test('will load default.yaml if exists', () {
expect(app.configuration["set_via"], equals("default"));
});
test('will load .env if exists', () {
expect(app.configuration['artist'], 'Timberlake');
expect(app.configuration['angel'], {'framework': 'cool'});
});
test('non-existent environment defaults to null', () {
expect(app.configuration.keys, contains('must_be_null'));
expect(app.configuration['must_be_null'], null);
});
test('can override ANGEL_ENV', () async {
await app.configure(configuration(fileSystem,
directoryPath: './test/config', overrideEnvironmentName: 'override'));
expect(app.configuration['hello'], equals('goodbye'));
expect(app.configuration['foo']['version'], equals('baz'));
});
test('merges configuration', () async {
await app.configure(configuration(fileSystem,
directoryPath: './test/config', overrideEnvironmentName: 'override'));
expect(app.configuration['merge'], {'map': true, 'hello': 'goodbye'});
});
}

View file

@ -0,0 +1,2 @@
ANGEL_FRAMEWORK=cool
JUSTIN=Timberlake

View file

@ -0,0 +1,8 @@
set_via: default
artist: $JUSTIN
angel:
framework: $ANGEL_FRAMEWORK
must_be_null: $NONEXISTENT_KEY_FOO_BAR_BAZ_QUUX
merge:
map: true
hello: world

View file

@ -0,0 +1,3 @@
hello: world
foo:
version: bar

View file

@ -0,0 +1,5 @@
hello: goodbye
foo:
version: baz
merge:
hello: goodbye