Skip to content

Commit

Permalink
feat: filename constraint
Browse files Browse the repository at this point in the history
  • Loading branch information
mcharytoniuk committed Feb 13, 2024
1 parent d73680c commit 346ff03
Show file tree
Hide file tree
Showing 18 changed files with 225 additions and 28 deletions.
6 changes: 3 additions & 3 deletions config.ini.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[app]
env = development
esbuild_metafile = %DM_ROOT%/esbuild-meta-app.json
esbuild_metafile = null
scheme = https
url = http://localhost:9501

Expand Down Expand Up @@ -79,8 +79,8 @@ sitemap = %DM_ROOT%/docs/build/sitemap.xml
host = 127.0.0.1
port = 9501
log_level = SWOOLE_LOG_DEBUG
ssl_cert_file = %DM_ROOT%/ssl/origin.crt
ssl_key_file = %DM_ROOT%/ssl/origin.key
ssl_cert_file = null
ssl_key_file = null

[translator]
base_directory = %DM_APP_ROOT%/lang
Expand Down
4 changes: 4 additions & 0 deletions docs/pages/docs/changelog/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ title: Changelog

# Changelog

## v0.23.0

- Feature: filename constraint in {{docs/features/validation/constraints/index}}

## v0.22.0

- Change: switch to absolute paths in {{docs/features/configuration/index}}
Expand Down
16 changes: 15 additions & 1 deletion docs/pages/docs/features/validation/constraints/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ new ConstConstraint(constValue: $constValue);
}
```

Accepts exactly the provided value
Accepts exactly the provided value.

### Enum

Expand All @@ -95,6 +95,20 @@ new EnumConstraint(values: $values);
}
```

### Filename

Checks if a given file exists and is readable.

```php
new FilenameConstraint();
```
```json
{
"type": "string",
"enum": [...]
}
```

### Integer

```php
Expand Down
2 changes: 1 addition & 1 deletion src/ApplicationConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/
public function __construct(
public Environment $environment,
public string $esbuildMetafile,
public ?string $esbuildMetafile,
public string $scheme,
public string $url,
) {}
Expand Down
9 changes: 6 additions & 3 deletions src/Command/Watch.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int
*/
$childCommandName = $input->getArgument('name');

$directories = [
$files = [
DM_APP_ROOT,
DM_APP_ROOT.'/../config.ini',
$this->applicationConfiguration->esbuildMetafile,
DM_RESONANCE_ROOT,
];

if (is_string($this->applicationConfiguration->esbuildMetafile)) {
$files[] = $this->applicationConfiguration->esbuildMetafile;
}

$this->restartChildCommand($childCommandName);

foreach (new InotifyIterator($directories) as $event) {
foreach (new InotifyIterator($files) as $event) {
$this->logger->info(sprintf('watch_file_changed(%s)', $event['name']));

$this->restartChildCommand($childCommandName);
Expand Down
99 changes: 99 additions & 0 deletions src/Constraint/FilenameConstraint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

declare(strict_types=1);

namespace Distantmagic\Resonance\Constraint;

use Distantmagic\Resonance\Constraint;
use Distantmagic\Resonance\ConstraintDefaultValue;
use Distantmagic\Resonance\ConstraintPath;
use Distantmagic\Resonance\ConstraintReason;
use Distantmagic\Resonance\ConstraintResult;
use Distantmagic\Resonance\ConstraintResultStatus;

final readonly class FilenameConstraint extends Constraint
{
public function __construct(
?ConstraintDefaultValue $defaultValue = null,
bool $isNullable = false,
bool $isRequired = true,
) {
parent::__construct(
defaultValue: $defaultValue,
isNullable: $isNullable,
isRequired: $isRequired,
);
}

public function default(mixed $defaultValue): self
{
return new self(
defaultValue: new ConstraintDefaultValue($defaultValue),
isNullable: $this->isNullable,
isRequired: $this->isRequired,
);
}

public function nullable(): self
{
return new self(
defaultValue: $this->defaultValue ?? new ConstraintDefaultValue(null),
isNullable: true,
isRequired: $this->isRequired,
);
}

public function optional(): self
{
return new self(
defaultValue: $this->defaultValue,
isNullable: $this->isNullable,
isRequired: false,
);
}

protected function doConvertToJsonSchema(): array
{
return [
'type' => 'string',
'minLength' => 1,
];
}

protected function doValidate(mixed $notValidatedData, ConstraintPath $path): ConstraintResult
{
if (!is_string($notValidatedData) || empty($notValidatedData)) {
return new ConstraintResult(
castedData: $notValidatedData,
path: $path,
reason: ConstraintReason::InvalidDataType,
status: ConstraintResultStatus::Invalid,
);
}

if (!file_exists($notValidatedData)) {
return new ConstraintResult(
castedData: $notValidatedData,
path: $path,
reason: ConstraintReason::FileNotFound,
status: ConstraintResultStatus::Invalid,
);
}

if (!is_readable($notValidatedData)) {
return new ConstraintResult(
castedData: $notValidatedData,
path: $path,
reason: ConstraintReason::FileNotReadable,
status: ConstraintResultStatus::Invalid,
);
}

return new ConstraintResult(
castedData: $notValidatedData,
path: $path,
reason: ConstraintReason::Ok,
status: ConstraintResultStatus::Valid,
);
}
}
60 changes: 60 additions & 0 deletions src/Constraint/FilenameConstraintTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace Distantmagic\Resonance\Constraint;

use PHPUnit\Framework\TestCase;

/**
* @coversNothing
*
* @internal
*/
final class FilenameConstraintTest extends TestCase
{
public function test_invalid(): void
{
$constraint = new FilenameConstraint();

self::assertFalse($constraint->validate(__FILE__.'.not_exists')->status->isValid());
}

public function test_is_converted_optionally_to_json_schema(): void
{
$constraint = new FilenameConstraint();

self::assertEquals([
'type' => 'string',
'minLength' => 1,
], $constraint->optional()->toJsonSchema());
}

public function test_is_converted_to_json_schema(): void
{
$constraint = new FilenameConstraint();

self::assertEquals([
'type' => 'string',
'minLength' => 1,
], $constraint->toJsonSchema());
}

public function test_nullable_is_converted_to_json_schema(): void
{
$constraint = new FilenameConstraint();

self::assertEquals([
'type' => ['null', 'string'],
'minLength' => 1,
'default' => null,
], $constraint->nullable()->toJsonSchema());
}

public function test_validates(): void
{
$constraint = new FilenameConstraint();

self::assertTrue($constraint->validate(__FILE__)->status->isValid());
}
}
2 changes: 2 additions & 0 deletions src/ConstraintReason.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

enum ConstraintReason: string
{
case FileNotFound = 'file_not_found';
case FileNotReadable = 'file_not_readable';
case InvalidDataType = 'invalid_data_type';
case InvalidEnumValue = 'invalid_enum_value';
case InvalidFormat = 'invalid_format';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\Constraint;
use Distantmagic\Resonance\Constraint\EnumConstraint;
use Distantmagic\Resonance\Constraint\FilenameConstraint;
use Distantmagic\Resonance\Constraint\ObjectConstraint;
use Distantmagic\Resonance\Constraint\StringConstraint;
use Distantmagic\Resonance\Environment;
Expand All @@ -30,7 +31,7 @@ public function getConstraint(): Constraint
return new ObjectConstraint(
properties: [
'env' => new EnumConstraint(Environment::values()),
'esbuild_metafile' => (new StringConstraint())->default('esbuild-meta.json'),
'esbuild_metafile' => (new FilenameConstraint())->nullable(),
'scheme' => (new EnumConstraint(['http', 'https']))->default('https'),
'url' => new StringConstraint(),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Distantmagic\Resonance\Constraint;
use Distantmagic\Resonance\Constraint\BooleanConstraint;
use Distantmagic\Resonance\Constraint\EnumConstraint;
use Distantmagic\Resonance\Constraint\FilenameConstraint;
use Distantmagic\Resonance\Constraint\IntegerConstraint;
use Distantmagic\Resonance\Constraint\MapConstraint;
use Distantmagic\Resonance\Constraint\ObjectConstraint;
Expand Down Expand Up @@ -49,7 +50,7 @@ public function getConstraint(): Constraint
'pool_prefill' => (new BooleanConstraint())->default(true),
'pool_size' => new IntegerConstraint(),
'port' => (new IntegerConstraint())->nullable()->default(3306),
'unix_socket' => (new StringConstraint())->nullable(),
'unix_socket' => (new FilenameConstraint())->nullable(),
'username' => new StringConstraint(),
]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\Constraint;
use Distantmagic\Resonance\Constraint\FilenameConstraint;
use Distantmagic\Resonance\Constraint\ObjectConstraint;
use Distantmagic\Resonance\Constraint\StringConstraint;
use Distantmagic\Resonance\GrpcConfiguration;
use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;

Expand All @@ -26,7 +26,8 @@ public function getConstraint(): Constraint
{
return new ObjectConstraint(
properties: [
'protoc_bin' => new StringConstraint(),
'grpc_php_plugin_bin' => new FilenameConstraint(),
'protoc_bin' => new FilenameConstraint(),
],
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Distantmagic\Resonance\Attribute\GrantsFeature;
use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\Constraint;
use Distantmagic\Resonance\Constraint\FilenameConstraint;
use Distantmagic\Resonance\Constraint\MapConstraint;
use Distantmagic\Resonance\Constraint\ObjectConstraint;
use Distantmagic\Resonance\Constraint\StringConstraint;
Expand Down Expand Up @@ -39,8 +40,8 @@ public function getConstraint(): Constraint
'dkim_domain_name' => (new StringConstraint())->nullable(),
'dkim_selector' => (new StringConstraint())->nullable(),
'dkim_signing_key_passphrase' => (new StringConstraint())->nullable(),
'dkim_signing_key_private' => (new StringConstraint())->nullable(),
'dkim_signing_key_public' => (new StringConstraint())->nullable(),
'dkim_signing_key_private' => (new FilenameConstraint())->nullable(),
'dkim_signing_key_public' => (new FilenameConstraint())->nullable(),
'transport_dsn' => new StringConstraint(),
]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Distantmagic\Resonance\Attribute\GrantsFeature;
use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\Constraint;
use Distantmagic\Resonance\Constraint\FilenameConstraint;
use Distantmagic\Resonance\Constraint\ObjectConstraint;
use Distantmagic\Resonance\Constraint\StringConstraint;
use Distantmagic\Resonance\Feature;
Expand Down Expand Up @@ -36,10 +37,10 @@ public function getConstraint(): Constraint
{
return new ObjectConstraint(
properties: [
'encryption_key' => new StringConstraint(),
'encryption_key' => new FilenameConstraint(),
'jwt_signing_key_passphrase' => (new StringConstraint())->nullable(),
'jwt_signing_key_private' => new StringConstraint(),
'jwt_signing_key_public' => new StringConstraint(),
'jwt_signing_key_private' => new FilenameConstraint(),
'jwt_signing_key_public' => new FilenameConstraint(),
'session_key_authorization_request' => (new StringConstraint())->default('oauth2.authorization_request'),
'session_key_pkce' => (new StringConstraint())->default('oauth2.pkce'),
'session_key_state' => (new StringConstraint())->default('oauth2.state'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\Constraint;
use Distantmagic\Resonance\Constraint\FilenameConstraint;
use Distantmagic\Resonance\Constraint\ObjectConstraint;
use Distantmagic\Resonance\Constraint\StringConstraint;
use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
use Distantmagic\Resonance\SQLiteVSSConfiguration;

Expand All @@ -24,8 +24,8 @@ public function getConstraint(): Constraint
{
return new ObjectConstraint(
properties: [
'extension_vector0' => new StringConstraint(),
'extension_vss0' => new StringConstraint(),
'extension_vector0' => new FilenameConstraint(),
'extension_vss0' => new FilenameConstraint(),
],
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\Constraint;
use Distantmagic\Resonance\Constraint\FilenameConstraint;
use Distantmagic\Resonance\Constraint\ObjectConstraint;
use Distantmagic\Resonance\Constraint\StringConstraint;
use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
Expand All @@ -28,9 +29,9 @@ public function getConstraint(): Constraint
return new ObjectConstraint(
properties: [
'base_url' => new StringConstraint(),
'esbuild_metafile' => new StringConstraint(),
'input_directory' => new StringConstraint(),
'output_directory' => new StringConstraint(),
'esbuild_metafile' => new FilenameConstraint(),
'input_directory' => new FilenameConstraint(),
'output_directory' => new FilenameConstraint(),
'sitemap' => new StringConstraint(),
]
);
Expand Down
Loading

0 comments on commit 346ff03

Please sign in to comment.