Skip to content

Commit

Permalink
ListCommand: --explain option
Browse files Browse the repository at this point in the history
  • Loading branch information
mabar committed Mar 2, 2024
1 parent 7da2e5a commit 82189bb
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 2 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased](https://github.com/orisai/scheduler/compare/1.0.0...v2.x)
## [Unreleased](https://github.com/orisai/scheduler/compare/2.0.0...v2.x)

### Added

- `ListCommand`
- adds `--explain` option to explain whole expression

## [2.0.0](https://github.com/orisai/scheduler/compare/1.0.0...2.0.0) - 2024-01-26

Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"php": ">=7.4.0 <8.3.0",
"dragonmantank/cron-expression": "^3.3",
"orisai/clock": "^1.2.0",
"orisai/cron-expression-explainer": "^1.0.0",
"orisai/exceptions": "^1.1.0",
"symfony/console": "^5.3.0|^6.0.0",
"symfony/lock": "^5.3.0|^6.0.0",
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,7 @@ List all scheduled jobs (in `expression / second (timezone) [id] name... next-du
- use `-v` to display absolute times
- use `--timezone` (or `-tz`) to display times in specified timezone instead of one used by application
- e.g. `--tz=UTC`
- use `--explain` to explain whole expression, including [seconds](#seconds) and [timezones](#timezones)

### Worker command

Expand Down
31 changes: 30 additions & 1 deletion src/Command/ListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use DateTimeInterface;
use DateTimeZone;
use Orisai\Clock\SystemClock;
use Orisai\CronExpressionExplainer\CronExpressionExplainer;
use Orisai\CronExpressionExplainer\DefaultCronExpressionExplainer;
use Orisai\Exceptions\Logic\InvalidArgument;
use Orisai\Scheduler\Job\JobSchedule;
use Orisai\Scheduler\Scheduler;
Expand All @@ -17,8 +19,10 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Terminal;
use function abs;
use function assert;
use function floor;
use function in_array;
use function is_bool;
use function max;
use function mb_strlen;
use function preg_match;
Expand All @@ -37,11 +41,18 @@ final class ListCommand extends Command

private ClockInterface $clock;

public function __construct(Scheduler $scheduler, ?ClockInterface $clock = null)
private CronExpressionExplainer $explainer;

public function __construct(
Scheduler $scheduler,
?ClockInterface $clock = null,
?CronExpressionExplainer $explainer = null
)
{
parent::__construct();
$this->scheduler = $scheduler;
$this->clock = $clock ?? new SystemClock();
$this->explainer = $explainer ?? new DefaultCronExpressionExplainer();
}

public static function getDefaultName(): string
Expand All @@ -60,12 +71,14 @@ protected function configure(): void
parent::configure();
$this->addOption('next', null, InputOption::VALUE_OPTIONAL, 'Sort jobs by their next execution time', false);
$this->addOption('timezone', 'tz', InputOption::VALUE_REQUIRED, 'The timezone times should be displayed in');
$this->addOption('explain', null, InputOption::VALUE_NONE, 'Explain expression');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$nextOption = $this->validateOptionNext($input);
$timeZone = $this->validateOptionTimeZone($input);
$explain = $this->validateOptionExplain($input);

$jobSchedules = $this->scheduler->getJobSchedules();

Expand Down Expand Up @@ -118,6 +131,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$nextDueDateLabel,
$nextDueDate,
));

if ($explain) {
$output->writeln($this->explainer->explain(
$jobSchedule->getExpression()->getExpression(),
$jobSchedule->getRepeatAfterSeconds(),
$jobSchedule->getTimeZone(),
));
}
}

return self::SUCCESS;
Expand Down Expand Up @@ -170,6 +191,14 @@ private function validateOptionTimeZone(InputInterface $input): DateTimeZone
return new DateTimeZone($option);
}

private function validateOptionExplain(InputInterface $input): bool
{
$option = $input->getOption('explain');
assert(is_bool($option));

return $option;
}

/**
* @param array<int|string, JobSchedule> $jobSchedules
* @param bool|int<1, max> $next
Expand Down
56 changes: 56 additions & 0 deletions tests/Unit/Command/ListCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,62 @@ public function testTimeZone(): void
0 1 * * * [1] Tests\Orisai\Scheduler\Doubles\CallbackList::__invoke() Next Due: 1970-01-02 01:00:00 +01:00
0 1 * * * (Australia/Sydney) [2] Tests\Orisai\Scheduler\Doubles\CallbackList::__invoke() Next Due: 1970-01-02 01:00:00 +01:00

MSG,
implode(
PHP_EOL,
array_map(
static fn (string $s): string => rtrim($s),
explode(PHP_EOL, $tester->getDisplay()),
),
),
);
self::assertSame($command::SUCCESS, $code);
}

public function testExplain(): void
{
$clock = new FrozenClock(1, new DateTimeZone('Europe/Prague'));
$scheduler = new SimpleScheduler(null, null, null, $clock);

$cbs = new CallbackList();
$scheduler->addJob(
new CallbackJob(Closure::fromCallable([$cbs, 'job1'])),
new CronExpression('* * * * *'),
null,
0,
new DateTimeZone('Europe/Prague'),
);
$scheduler->addJob(
new CallbackJob(Closure::fromCallable([$cbs, 'job1'])),
new CronExpression('*/30 7-15 * * 1-5'),
null,
0,
new DateTimeZone('America/New_York'),
);
$scheduler->addJob(
new CallbackJob(Closure::fromCallable($cbs)),
new CronExpression('* * * 4 *'),
null,
10,
);

$command = new ListCommand($scheduler, $clock);
$tester = new CommandTester($command);

putenv('COLUMNS=80');
$code = $tester->execute([
'--explain' => true,
]);

self::assertSame(
<<<'MSG'
* * * 4 * / 10 [2] Tests\Orisai\Scheduler\Doubles\CallbackList::__invoke() Next Due: 2 months
At every 10 seconds in April.
* * * * * [0] Tests\Orisai\Scheduler\Doubles\CallbackList::job1() Next Due: 59 seconds
At every minute in Europe/Prague time zone.
*/30 7-15 * * 1-5 (America/New_York) [1] Tests\Orisai\Scheduler\Doubles\CallbackList::job1() Next Due: 5 hours
At every 30th minute past every hour from 7 through 15 on every day-of-week from Monday through Friday in America/New_York time zone.

MSG,
implode(
PHP_EOL,
Expand Down
5 changes: 5 additions & 0 deletions tools/phpstan.baseline.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
parameters:
ignoreErrors:
-
message: "#^Parameter \\#1 \\$expression of method Orisai\\\\CronExpressionExplainer\\\\CronExpressionExplainer\\:\\:explain\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: ../src/Command/ListCommand.php

-
message: "#^Parameter \\#1 \\$input of function str_pad expects string, string\\|null given\\.$#"
count: 1
Expand Down

0 comments on commit 82189bb

Please sign in to comment.