Skip to content

Commit 2a20743

Browse files
committed
Read configuration in *config.ini* if available
Preparation work for #27
1 parent a7253f8 commit 2a20743

File tree

5 files changed

+147
-5
lines changed

5 files changed

+147
-5
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ $ db.users.insert({handle: "import", pass: "4323135e32ac4..."});
2828
# ...
2929
```
3030

31+
Store the connection string in a configuration file named *config.ini*:
32+
33+
```ini
34+
[mongo]
35+
uri=mongodb+srv://[USER]:[PASSWORD]@[PROJECT].[ORG].mongodb.net
36+
```
37+
3138
Then, run composer to install PHP and JavaScript dependencies.
3239

3340
```bash
@@ -40,7 +47,6 @@ $ composer up
4047
Now, Dialog can be run locally.
4148

4249
```bash
43-
$ export MONGO_URI=mongodb+srv://[USER]:[PASSWORD]@[PROJECT].[ORG].mongodb.net
4450
$ xp serve
4551
# ...
4652
```

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
},
2626

2727
"scripts": {
28-
"dev": "xp -supervise web -p dev -m develop -a 0.0.0.0:8080 de.thekid.dialog.App ./import",
29-
"serve": "xp -supervise web -p dev -a 0.0.0.0:8080 de.thekid.dialog.App ./import",
28+
"dev": "xp -supervise web -c . -p dev -m develop -a 0.0.0.0:8080 de.thekid.dialog.App ./import",
29+
"serve": "xp -supervise web -c . -p dev -a 0.0.0.0:8080 de.thekid.dialog.App ./import",
3030
"import": "xp cmd de.thekid.dialog.import.LocalDirectory",
3131
"post-update-cmd": "xp bundle -m src/main/webapp/assets/manifest.json src/main/webapp/assets/"
3232
}

src/main/php/de/thekid/dialog/App.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ class App extends Application {
1515

1616
/** Returns routing for this web application */
1717
public function routes() {
18-
$conn= new MongoConnection($this->environment->variable('MONGO_URI'));
19-
$repository= new Repository($conn->database($this->environment->variable('MONGO_DB') ?? 'dialog'));
18+
$preferences= new Preferences($this->environment, 'config');
19+
$conn= new MongoConnection($preferences->get('mongo', 'uri'));
20+
$repository= new Repository($conn->database($preferences->optional('mongo', 'db', 'dialog')));
2021
$storage= new Path($this->environment->arguments()[0]);
2122
$new= fn($class) => $class->newInstance($repository, $storage);
2223

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php namespace de\thekid\dialog;
2+
3+
use util\NoSuchElementException;
4+
use web\Environment;
5+
6+
/** @test de.thekid.dialog.unittest.PreferencesTest */
7+
class Preferences {
8+
private $sources;
9+
10+
/** Creates a new Preferences instances from an environment and a config file name */
11+
public function __construct(Environment $env, string $config) {
12+
$this->sources= ['env' => fn($section, $name) => $env->variable(strtoupper("{$section}_{$name}"))];
13+
14+
$prop= $env->properties($config);
15+
if ($prop->exists()) {
16+
$this->sources['config']= fn($section, $name) => $prop->readString($section, $name, null);
17+
}
18+
}
19+
20+
/**
21+
* Gets a configuration value. Throws an exception if the value is
22+
* absent.
23+
*
24+
* @throws util.NoSuchElementException
25+
*/
26+
public function get(string $section, string $name): string {
27+
foreach ($this->sources as $read) {
28+
if (null !== ($value= $read($section, $name))) return $value;
29+
}
30+
31+
throw new NoSuchElementException(sprintf(
32+
'Missing configuration value <%s::%s> in %s',
33+
$section,
34+
$name,
35+
implode(', ', array_keys($this->sources)),
36+
));
37+
}
38+
39+
/**
40+
* Gets a configuration value. Returns a given default (or NULL) if
41+
* the value is absent.
42+
*/
43+
public function optional(string $section, string $name, ?string $default= null): ?string {
44+
foreach ($this->sources as $read) {
45+
if (null !== ($value= $read($section, $name))) return $value;
46+
}
47+
return $default;
48+
}
49+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php namespace de\thekid\dialog\unittest;
2+
3+
use de\thekid\dialog\Preferences;
4+
use unittest\{Assert, Before, Expect, Test, Values};
5+
use util\NoSuchElementException;
6+
use util\{Properties, RegisteredPropertySource};
7+
use web\Environment;
8+
9+
class PreferencesTest {
10+
private $config, $environment;
11+
12+
#[Before]
13+
public function environment() {
14+
$this->config= new class('testing') extends Properties {
15+
public function use($sections) { $this->_data= $sections; }
16+
public function exists(): bool { return null !== $this->_data; }
17+
};
18+
19+
$source= new RegisteredPropertySource('config', $this->config);
20+
$this->environment= new class('test', '.', '.', [$source], [], []) extends Environment {
21+
private $variables;
22+
23+
public function export($variables) {
24+
$this->variables= $variables;
25+
return $this;
26+
}
27+
28+
public function variable($name) {
29+
return $this->variables[$name] ?? null;
30+
}
31+
};
32+
}
33+
34+
#[Test]
35+
public function can_create() {
36+
new Preferences($this->environment, 'config');
37+
}
38+
39+
#[Test, Values(['get', 'optional'])]
40+
public function uses_environment($op) {
41+
$this->config->use(null);
42+
$fixture= new Preferences($this->environment->export(['MONGO_URI' => 'set']), 'config');
43+
44+
Assert::equals('set', $fixture->{$op}('mongo', 'uri'));
45+
}
46+
47+
#[Test, Values(['get', 'optional'])]
48+
public function uses_properties($op) {
49+
$this->config->use(['mongo' => ['uri' => 'configured']]);
50+
$fixture= new Preferences($this->environment->export([]), 'config');
51+
52+
Assert::equals('configured', $fixture->{$op}('mongo', 'uri'));
53+
}
54+
55+
#[Test, Values(['get', 'optional'])]
56+
public function environment_has_precedence_over_configuration($op) {
57+
$this->config->use(['mongo' => ['uri' => 'configured']]);
58+
$fixture= new Preferences($this->environment->export(['MONGO_URI' => 'set']), 'config');
59+
60+
Assert::equals('set', $fixture->{$op}('mongo', 'uri'));
61+
}
62+
63+
#[Test, Expect(class: NoSuchElementException::class, withMessage: '/Missing.+mongo::uri/')]
64+
public function get_raises_exception_if_not_found() {
65+
$this->config->use(null);
66+
$fixture= new Preferences($this->environment->export([]), 'config');
67+
68+
$fixture->get('mongo', 'uri');
69+
}
70+
71+
#[Test]
72+
public function optional_returns_null_if_not_found() {
73+
$this->config->use(null);
74+
$fixture= new Preferences($this->environment->export([]), 'config');
75+
76+
Assert::null($fixture->optional('mongo', 'uri'));
77+
}
78+
79+
#[Test]
80+
public function optional_can_return_default_if_not_found() {
81+
$this->config->use(null);
82+
$fixture= new Preferences($this->environment->export([]), 'config');
83+
84+
Assert::equals('default', $fixture->optional('mongo', 'uri', 'default'));
85+
}
86+
}

0 commit comments

Comments
 (0)