Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non destructive sync option (keep-old) #91

Merged
merged 8 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ In a non-production environment you should manually run `schedule-monitor:sync`.

**Note:** Running the sync command will remove any other cron monitors that you've defined other than the application schedule.

If you would like to use non-destructive syncs to Oh Dear so that you can monitor other cron tasks outside of Laravel, you can use the `--keep-old` flag. This will only push new tasks to Oh Dear, rather than a full sync. Note that this will not remove any tasks from Oh Dear that are no longer in your schedule.

## Usage

To monitor your schedule you should first run `schedule-monitor:sync`. This command will take a look at your schedule and create an entry for each task in the `monitored_scheduled_tasks` table.
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"orchestra/testbench": "^7.0|^8.0",
"pestphp/pest": "^1.20",
"pestphp/pest-plugin-laravel": "^1.2",
"spatie/pest-plugin-snapshots": "^1.1",
"spatie/phpunit-snapshot-assertions": "^4.2",
"spatie/test-time": "^1.2"
},
Expand Down
78 changes: 55 additions & 23 deletions src/Commands/SyncCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class SyncCommand extends Command
{
use UsesScheduleMonitoringModels;

public $signature = 'schedule-monitor:sync';
public $signature = 'schedule-monitor:sync {--keep-old}';

public $description = 'Sync the schedule of the app with the schedule monitor';

Expand All @@ -27,8 +27,8 @@ public function handle()
]));

$this
->syncScheduledTasksWithDatabase()
->syncMonitoredScheduledTaskWithOhDear();
->storeScheduledTasksInDatabase()
->storeMonitoredScheduledTasksInOhDear();

$monitoredScheduledTasksCount = $this->getMonitoredScheduleTaskModel()->count();

Expand All @@ -37,7 +37,7 @@ public function handle()
]));
}

protected function syncScheduledTasksWithDatabase(): self
protected function storeScheduledTasksInDatabase(): self
{
render(view('schedule-monitor::alert', [
'message' => 'Start syncing schedule with database...',
Expand All @@ -57,14 +57,16 @@ protected function syncScheduledTasksWithDatabase(): self
);
});

$this->getMonitoredScheduleTaskModel()->query()
->whereNotIn('id', $monitoredScheduledTasks->pluck('id'))
->delete();
if (! $this->option('keep-old')) {
$this->getMonitoredScheduleTaskModel()->query()
->whereNotIn('id', $monitoredScheduledTasks->pluck('id'))
->delete();
}

return $this;
}

protected function syncMonitoredScheduledTaskWithOhDear(): self
protected function storeMonitoredScheduledTasksInOhDear(): self
{
if (! class_exists(OhDear::class)) {
return $this;
Expand Down Expand Up @@ -93,6 +95,32 @@ protected function syncMonitoredScheduledTaskWithOhDear(): self
'message' => 'Start syncing schedule with Oh Dear...',
]));

$cronChecks = $this->option('keep-old')
? $this->pushMonitoredScheduledTaskToOhDear($siteId)
: $this->syncMonitoredScheduledTaskWithOhDear($siteId);

render(view('schedule-monitor::alert', [
'message' => 'Successfully synced schedule with Oh Dear!',
'class' => 'text-green',
]));

collect($cronChecks)
->each(
function (CronCheck $cronCheck) {
if (! $monitoredScheduledTask = $this->getMonitoredScheduleTaskModel()->findForCronCheck($cronCheck)) {
return;
}

$monitoredScheduledTask->update(['ping_url' => $cronCheck->pingUrl]);
$monitoredScheduledTask->markAsRegisteredOnOhDear();
}
);

return $this;
}

protected function syncMonitoredScheduledTaskWithOhDear(int $siteId): array
{
$monitoredScheduledTasks = $this->getMonitoredScheduleTaskModel()->get();

$cronChecks = $monitoredScheduledTasks
Expand All @@ -110,23 +138,27 @@ protected function syncMonitoredScheduledTaskWithOhDear(): self

$cronChecks = app(OhDear::class)->site($siteId)->syncCronChecks($cronChecks);

render(view('schedule-monitor::alert', [
'message' => 'Successfully synced schedule with Oh Dear!',
'class' => 'text-green',
]));

collect($cronChecks)
->each(
function (CronCheck $cronCheck) {
if (! $monitoredScheduledTask = $this->getMonitoredScheduleTaskModel()->findForCronCheck($cronCheck)) {
return;
}
return $cronChecks;
}

$monitoredScheduledTask->update(['ping_url' => $cronCheck->pingUrl]);
$monitoredScheduledTask->markAsRegisteredOnOhDear();
}
protected function pushMonitoredScheduledTaskToOhDear(int $siteId): array
{
$tasksToRegister = $this->getMonitoredScheduleTaskModel()
->whereNull('registered_on_oh_dear_at')
->get();

$cronChecks = [];
foreach ($tasksToRegister as $taskToRegister) {
$cronChecks[] = app(OhDear::class)->createCronCheck(
siteId: $siteId,
name: $taskToRegister->name,
cronExpression: $taskToRegister->cron_expression,
graceTimeInMinutes: $taskToRegister->grace_time_in_minutes,
description: '',
serverTimezone: $taskToRegister->timezone,
);
}

return $this;
return $cronChecks;
}
}
69 changes: 68 additions & 1 deletion tests/Commands/SyncCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Spatie\ScheduleMonitor\Tests\TestClasses\TestJob;
use Spatie\ScheduleMonitor\Tests\TestClasses\TestKernel;
use Spatie\TestTime\TestTime;
use function Spatie\Snapshots\{assertMatchesSnapshot};

beforeEach(function () {
TestTime::freeze('Y-m-d H:i:s', '2020-01-01 00:00:00');
Expand Down Expand Up @@ -76,7 +77,73 @@
'timezone' => 'Asia/Kolkata',
]);

$this->assertMatchesSnapshot($this->ohDear->getSyncedCronCheckAttributes());
assertMatchesSnapshot($this->ohDear->getSyncedCronCheckAttributes());
});

it('can use the keep old option to non destructively update the schedule with db and oh dear', function () {
MonitoredScheduledTask::create([
'name' => 'dummy-1',
'type' => 'command',
'cron_expression' => '* * * * *',
'ping_url' => 'https://ping.ohdear.app/test-ping-url-dummy-1',
'registered_on_oh_dear_at' => now()->format('Y-m-d H:i:s'),
'grace_time_in_minutes' => 5,
'last_pinged_at' => null,
'last_started_at' => null,
'last_finished_at' => null,
'timezone' => 'UTC',
]);

TestKernel::registerScheduledTasks(function (Schedule $schedule) {
$schedule->command('dummy-2')->hourly();
$schedule->command('dummy-3')->daily();
});

$this->artisan(SyncCommand::class, ['--keep-old' => true]);

$monitoredScheduledTasks = MonitoredScheduledTask::get();
expect($monitoredScheduledTasks)->toHaveCount(3);

$this->assertDatabaseHas('monitored_scheduled_tasks', [
'name' => 'dummy-1',
'type' => 'command',
'cron_expression' => '* * * * *',
'ping_url' => 'https://ping.ohdear.app/test-ping-url-dummy-1',
'registered_on_oh_dear_at' => now()->format('Y-m-d H:i:s'),
'grace_time_in_minutes' => 5,
'last_pinged_at' => null,
'last_started_at' => null,
'last_finished_at' => null,
'timezone' => 'UTC',
]);

$this->assertDatabaseHas('monitored_scheduled_tasks', [
'name' => 'dummy-2',
'type' => 'command',
'cron_expression' => '0 * * * *',
'ping_url' => 'https://ping.ohdear.app/test-ping-url-dummy-2',
'registered_on_oh_dear_at' => now()->format('Y-m-d H:i:s'),
'grace_time_in_minutes' => 5,
'last_pinged_at' => null,
'last_started_at' => null,
'last_finished_at' => null,
'timezone' => 'UTC',
]);

$this->assertDatabaseHas('monitored_scheduled_tasks', [
'name' => 'dummy-3',
'type' => 'command',
'cron_expression' => '0 0 * * *',
'ping_url' => 'https://ping.ohdear.app/test-ping-url-dummy-3',
'registered_on_oh_dear_at' => now()->format('Y-m-d H:i:s'),
'grace_time_in_minutes' => 5,
'last_pinged_at' => null,
'last_started_at' => null,
'last_finished_at' => null,
'timezone' => 'UTC',
]);

assertMatchesSnapshot($this->ohDear->getSyncedCronCheckAttributes());
});

it('will not monitor commands without a name', function () {
Expand Down
24 changes: 24 additions & 0 deletions tests/TestClasses/FakeOhDear.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,30 @@ public function getSyncedCronCheckAttributes(): array
{
return $this->syncedCronCheckAttributes;
}

public function createCronCheck(
int $siteId,
string $name,
string $cronExpression,
int $graceTimeInMinutes,
$description,
string $serverTimezone
): CronCheck {
$attributes = [
'name' => $name,
'type' => 'cron',
'cron_expression' => $cronExpression,
'grace_time_in_minutes' => $graceTimeInMinutes,
'description' => $description ?? '',
'server_timezone' => $serverTimezone,
];

$attributes['ping_url'] = 'https://ping.ohdear.app/test-ping-url-' . urlencode($attributes['name']);

$this->syncedCronCheckAttributes[] = $attributes;

return new CronCheck($attributes, $this);
}
}

class FakeSite extends Site
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-
name: dummy-2
type: cron
cron_expression: '0 * * * *'
grace_time_in_minutes: 5
description: ''
server_timezone: UTC
ping_url: 'https://ping.ohdear.app/test-ping-url-dummy-2'
-
name: dummy-3
type: cron
cron_expression: '0 0 * * *'
grace_time_in_minutes: 5
description: ''
server_timezone: UTC
ping_url: 'https://ping.ohdear.app/test-ping-url-dummy-3'