Skip to content

Commit

Permalink
Merge pull request #64 from ash-jc-allen/feature/run-inline
Browse files Browse the repository at this point in the history
Add `runInline` method
  • Loading branch information
ash-jc-allen authored Apr 26, 2024
2 parents 20a1c8d + a3e1e6b commit 11b2646
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 0 deletions.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,33 @@ $configValidator = new ConfigValidator();
$configValidator->run([], 'app/Custom/Validation');
```

##### Running the Validator with Inline Rules

There may be times when you want to run the validator with inline rules instead of using the rules defined in your config validation files. This can be useful if you want to run a one-off validation check, or validate the config values inside a package you maintain.

To do this, you can use the `runInline` method like so:

```php
use AshAllenDesign\ConfigValidator\Services\ConfigValidator;
use AshAllenDesign\ConfigValidator\Services\Rule;

$configValidator = new ConfigValidator();

$configValidator->runInline([
'app' => [
Rule::make('env')->rules(['in:local,production']),
Rule::make('debug')->rules(['boolean']),
],
'mail' => [
Rule::make('driver')->rules(['in:smtp,sendmail,mailgun,ses,postmark,log,array']),
],
]);
```

In the example above, we're running the validator with inline rules for the `app` and `mail` config files. The rules are the same as the ones we would define in the config validation files.

The behaviour of the `runInline` method is the same as the `run` method. It will throw an exception if the validation fails, or return a boolean value if the `throwExceptionOnFailure` method has been set to `false`.

#### Using the Command

The library comes with a useful command that you can use to validate your config. To use it, you can run the following in
Expand Down
17 changes: 17 additions & 0 deletions src/Services/ConfigValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,23 @@ public function run(array $configFiles = [], string $validationFolderPath = null
return $this->runValidator();
}

/**
* Validate config values with rules that are passed in inline rather
* than being read from a file in the filesystem.
*
* @param array<string,array<Rule>> $ruleGroups
*
* @throws InvalidConfigValueException
*/
public function runInline(array $ruleGroups): bool
{
foreach ($ruleGroups as $configKey => $rules) {
$this->validationRepository->push($configKey, $rules);
}

return $this->runValidator();
}

/**
* Validate the config values against the config rules
* that have been set. If throwExceptionOnFailure is
Expand Down
176 changes: 176 additions & 0 deletions tests/Unit/Services/ConfigValidator/RunInlineTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<?php

declare(strict_types=1);

namespace AshAllenDesign\ConfigValidator\Tests\Unit\Services\ConfigValidator;

use AshAllenDesign\ConfigValidator\Exceptions\InvalidConfigValueException;
use AshAllenDesign\ConfigValidator\Services\ConfigValidator;
use AshAllenDesign\ConfigValidator\Services\Rule;
use AshAllenDesign\ConfigValidator\Tests\Unit\Stubs\IsFooBar;
use AshAllenDesign\ConfigValidator\Tests\Unit\TestCase;
use Illuminate\Support\Facades\Config;

final class RunInlineTest extends TestCase
{
/** @test */
public function validator_can_be_run_inline_and_pass(): void
{
// Set valid config values that will pass all the validation rules.
Config::set('mail.from.address', '[email protected]');
Config::set('mail.from.to', 'Ashley Allen');
Config::set('mail.host', 'a random string');
Config::set('mail.port', 1234);
Config::set('cache.prefix', 'foobar');

$configValidator = new ConfigValidator();

$this->assertTrue(
$configValidator->runInline([
'mail' => $this->mailRules(),
'cache' => $this->cacheRules(),
]),
);
}

/** @test */
public function exception_is_thrown_if_the_validation_fails_with_a_custom_rule_message(): void
{
Config::set('mail.host', null);

$this->expectException(InvalidConfigValueException::class);
$this->expectExceptionMessage('This is a custom message.');

$configValidator = new ConfigValidator();
$configValidator->throwExceptionOnFailure(true)->runInline([
'mail' => $this->mailRules(),
]);
}

/** @test */
public function exception_is_thrown_if_the_validation_fails(): void
{
Config::set('cache.default', null);

$this->expectException(InvalidConfigValueException::class);

// The validation failed message structure changed in Laravel 10.
// So we need to check for both messages depending on the
// Laravel framework version.
if (version_compare(app()->version(), '10.0.0', '>=')) {
$this->expectExceptionMessage('The cache.default field must be a string.');
} else {
$this->expectExceptionMessage('The cache.default must be a string.');
}

$configValidator = new ConfigValidator();
$configValidator->runInline([
'cache' => $this->cacheRules(),
]);
}

/** @test */
public function validation_error_messages_can_be_returned(): void
{
// Set valid config values that will pass all of the validation rules.
Config::set('mail.from.to', 'Ashley Allen');
Config::set('mail.host', 'a random string');

// Set invalid config values that will have their error messages stored.
Config::set('mail.from.address', 'INVALID');
Config::set('cache.default', null);
Config::set('cache.prefix', null);
Config::set('mail.port', 'INVALID');
Config::set('mail.field_with_underscores', 'INVALID');

$configValidator = new ConfigValidator();

$this->assertEquals([], $configValidator->errors());

try {
$configValidator->runInline([
'mail' => $this->mailRules(),
'cache' => $this->cacheRules(),
]);
} catch (InvalidConfigValueException $e) {
// Suppress the exception so that we can continue
// testing the error output.
}

// The validation failed message structure changed in Laravel 10.
// So we need to check for both messages depending on the
// Laravel framework version.
if (version_compare(app()->version(), '10.0.0', '>=')) {
$this->assertEquals([
'cache.default' => [
'The cache.default field must be a string.',
'The cache.default field is required.',
],
'cache.prefix' => [
'The cache.prefix must be equal to foobar.',
],
'mail.from.address' => [
'The mail.from.address field must be a valid email address.',
],
'mail.port' => [
'The mail.port field must be an integer.',
],
'mail.field_with_underscores' => [
'The mail.field_with_underscores field must be an integer.',
],
], $configValidator->errors());
} else {
$this->assertEquals([
'cache.default' => [
'The cache.default must be a string.',
'The cache.default field is required.',
],
'cache.prefix' => [
'The cache.prefix must be equal to foobar.',
],
'mail.from.address' => [
'The mail.from.address must be a valid email address.',
],
'mail.port' => [
'The mail.port must be an integer.',
],
'mail.field_with_underscores' => [
'The mail.field_with_underscores must be an integer.',
],
], $configValidator->errors());
}
}

/** @test */
public function exception_is_not_thrown_if_it_is_disabled_before_running_the_validator(): void
{
// Set invalid config values that will have their error messages stored.
Config::set('cache.default', null);

$configValidator = new ConfigValidator();
$this->assertFalse(
$configValidator->throwExceptionOnFailure(false)->runInline([
'cache' => $this->cacheRules(),
]),
);
}

private function mailRules(): array
{
return [
Rule::make('host')->rules(['string'])->messages(['string' => 'This is a custom message.']),
Rule::make('port')->rules(['integer']),
Rule::make('from.address')->rules(['email', 'required']),
Rule::make('from.to')->rules(['string', 'required']),
Rule::make('field_with_underscores')->rules(['integer', 'nullable']),
];
}

private function cacheRules(): array
{
return [
Rule::make('default')->rules(['string', 'required', 'in:apc,array,database,file,memcached,redis,dynamodb']),
Rule::make('prefix')->rules([new IsFooBar()]),
];
}
}

0 comments on commit 11b2646

Please sign in to comment.