Skip to content

Commit

Permalink
Merge pull request #92 from tarfin-labs/WB-805-event-machine-feat-val…
Browse files Browse the repository at this point in the history
…idator-add-support-for-status-events-in-root-keys

Wb 805 event machine feat validator add support for status events in root keys
  • Loading branch information
deligoez authored Dec 24, 2024
2 parents 1146561 + 2a45f7d commit 376fc97
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 14 deletions.
73 changes: 60 additions & 13 deletions src/StateConfigValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class StateConfigValidator
{
/** Allowed keys at different levels of the machine configuration */
private const ALLOWED_ROOT_KEYS = [
'id', 'version', 'initial', 'context', 'states', 'on', 'type',
'id', 'version', 'initial', 'status_events', 'context', 'states', 'on', 'type',
'meta', 'entry', 'exit', 'description', 'scenarios_enabled',
'should_persist', 'delimiter',
];
Expand Down Expand Up @@ -53,7 +53,11 @@ private static function normalizeArrayOrString(mixed $value): ?array
/**
* Validates the machine configuration structure.
*
* @throws InvalidArgumentException
* This method serves as the entry point for configuration validation.
* It validates the root level configuration and recursively validates
* all nested states and their transitions.
*
* @throws InvalidArgumentException When configuration validation fails
*/
public static function validate(?array $config): void
{
Expand All @@ -77,7 +81,11 @@ public static function validate(?array $config): void

// Validate root level transitions if they exist
if (isset($config['on'])) {
self::validateTransitionsConfig(transitionsConfig: $config['on'], path: 'root');
self::validateTransitionsConfig(
transitionsConfig: $config['on'],
path: 'root',
parentState: $config
);
}
}

Expand All @@ -104,7 +112,18 @@ private static function validateRootConfig(array $config): void
/**
* Validates a single state's configuration.
*
* @throws InvalidArgumentException
* This method performs comprehensive validation of a state's configuration including:
* - Checking for directly defined transitions
* - Validating state keys
* - Validating state type
* - Validating entry/exit actions
* - Processing nested states
* - Validating transitions
*
* The validation order is important: we validate nested states first,
* then process the transitions to ensure proper context.
*
* @throws InvalidArgumentException When state configuration is invalid
*/
private static function validateStateConfig(?array $stateConfig, string $path): void
{
Expand Down Expand Up @@ -142,7 +161,7 @@ private static function validateStateConfig(?array $stateConfig, string $path):
self::validateFinalState(stateConfig: $stateConfig, path: $path);
}

// Validate nested states
// Process nested states first to ensure proper context
if (isset($stateConfig['states'])) {
if (!is_array($stateConfig['states'])) {
throw new InvalidArgumentException(
Expand All @@ -151,13 +170,20 @@ private static function validateStateConfig(?array $stateConfig, string $path):
}

foreach ($stateConfig['states'] as $childKey => $childState) {
self::validateStateConfig(stateConfig: $childState, path: "{$path}.{$childKey}");
self::validateStateConfig(
stateConfig: $childState,
path: "{$path}.{$childKey}"
);
}
}

// Validate transitions under 'on'
// Validate transitions after processing nested states
if (isset($stateConfig['on'])) {
self::validateTransitionsConfig(transitionsConfig: $stateConfig['on'], path: $path);
self::validateTransitionsConfig(
transitionsConfig: $stateConfig['on'],
path: $path,
parentState: $stateConfig
);
}
}

Expand Down Expand Up @@ -217,20 +243,41 @@ private static function validateStateActions(array $stateConfig, string $path):
}

/**
* Validates transitions configuration.
* Validates the transitions configuration for a state.
*
* @throws InvalidArgumentException
* This method processes both standard event names and Event class names as transition triggers.
* It ensures that all transitions are properly formatted and contain valid configuration.
*
* @throws InvalidArgumentException When transitions configuration is invalid
*/
private static function validateTransitionsConfig(mixed $transitionsConfig, string $path): void
{
private static function validateTransitionsConfig(
mixed $transitionsConfig,
string $path,
?array $parentState = null
): void {
if (!is_array($transitionsConfig)) {
throw new InvalidArgumentException(
message: "State '{$path}' has invalid 'on' definition. 'on' must be an array of transitions."
);
}

foreach ($transitionsConfig as $eventName => $transition) {
self::validateTransition(transition: $transition, path: $path, eventName: $eventName);
// Handle both Event classes and string event names
if (is_string($eventName) && class_exists($eventName) && is_subclass_of($eventName, EventBehavior::class)) {
self::validateTransition(
transition: $transition,
path: $path,
eventName: $eventName::getType()
);

continue;
}

self::validateTransition(
transition: $transition,
path: $path,
eventName: $eventName
);
}
}

Expand Down
2 changes: 1 addition & 1 deletion tests/StateConfigValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
'another_invalid' => 'value',
]))->toThrow(
exception: InvalidArgumentException::class,
exceptionMessage: 'Invalid root level configuration keys: invalid_key, another_invalid. Allowed keys are: id, version, initial, context, states, on, type, meta, entry, exit, description, scenarios_enabled, should_persist, delimiter'
exceptionMessage: 'Invalid root level configuration keys: invalid_key, another_invalid. Allowed keys are: id, version, initial, status_events, context, states, on, type, meta, entry, exit, description, scenarios_enabled, should_persist, delimiter'
);
});

Expand Down

0 comments on commit 376fc97

Please sign in to comment.