Replace magic strings across the automation domain with backed PHP enums
(HttpMethod, AuthType, DelayUnit, ScheduleField) and mirrored TS consts
(http-method, auth-type, delay-unit, schedule-field, condition-operator,
publish-mode), plus the existing Condition\Handle / Operator / Publish\Mode.
Also:
- require scheduled_offset via concrete-index required_if instead of
defaulting to 60 when the publish mode is scheduled
- fail the webhook node explicitly when the resolved url is empty
- localize node failure messages (fetch_rss/http/webhook)
- cast resolver/strtoupper inputs to string so a present-null config value
degrades gracefully instead of crashing
- list automations with config('app.pagination.default'), drop the perPage param
64 lines
2.2 KiB
PHP
64 lines
2.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Actions\Automation\Node;
|
|
|
|
use App\DataTransferObjects\Automation\NodeRunResult;
|
|
use App\Enums\Automation\Condition\Handle;
|
|
use App\Enums\Automation\Condition\Operator;
|
|
use App\Models\AutomationRun;
|
|
use App\Services\Automation\ExpressionResolver;
|
|
use Throwable;
|
|
|
|
class RunConditionNode
|
|
{
|
|
private const MAX_REGEX_LENGTH = 200;
|
|
|
|
public function __construct(private ExpressionResolver $resolver) {}
|
|
|
|
public function __invoke(AutomationRun $run, array $config): NodeRunResult
|
|
{
|
|
$context = $run->resolverContext();
|
|
$field = $this->resolver->resolve((string) data_get($config, 'field', ''), $context);
|
|
$operator = Operator::from(data_get($config, 'operator', Operator::Equals->value));
|
|
$value = $this->resolver->resolve((string) data_get($config, 'value', ''), $context);
|
|
|
|
$matched = match ($operator) {
|
|
Operator::Contains => str_contains($field, $value),
|
|
Operator::NotContains => ! str_contains($field, $value),
|
|
Operator::Equals => $field === $value,
|
|
Operator::NotEquals => $field !== $value,
|
|
Operator::Matches => $this->safeRegexMatch($value, $field),
|
|
Operator::GreaterThan => is_numeric($field) && is_numeric($value) && (float) $field > (float) $value,
|
|
Operator::LessThan => is_numeric($field) && is_numeric($value) && (float) $field < (float) $value,
|
|
};
|
|
|
|
return NodeRunResult::completed(
|
|
output: ['condition' => ['resolved_field' => $field, 'matched' => $matched]],
|
|
nextHandle: ($matched ? Handle::Yes : Handle::No)->value,
|
|
);
|
|
}
|
|
|
|
private function safeRegexMatch(string $pattern, string $subject): bool
|
|
{
|
|
if (strlen($pattern) > self::MAX_REGEX_LENGTH) {
|
|
return false;
|
|
}
|
|
|
|
$escaped = str_replace('~', '\~', $pattern);
|
|
$regex = "~{$escaped}~u";
|
|
|
|
try {
|
|
$result = @preg_match($regex, $subject);
|
|
} catch (Throwable) {
|
|
return false;
|
|
}
|
|
|
|
if ($result === false || preg_last_error() !== PREG_NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
return $result === 1;
|
|
}
|
|
}
|