<?php
// IGNORE THIS STUFF UP HERE - Just to keep syntax highlighting from breaking my awesome pseudo-code
$newTask = array();
$asyncTaskController = new stdClass();
$asyncTaskController->store = function($x){};
// IGNORE THIS STUFF UP HERE - Just to keep syntax highlighting from breaking my awesome pseudo-code ?>
<script>
/**
* AsyncTask - What it looks like
*/
var newTask = {
tmpId: 'some-client-side-uuid',
id: null, // null, then eventually, a real id once created on server
taskType: 'SiteEnvironmentBackup',
status: 'PENDING', // <PENDING | ACTIVE | FINISHED | FAILED | CANCELLED>
data: { /** mixed data, specific to task & task type */ }
};
</script>
<?php
/**
* Step 1 - UI Sends CreateAsyncTask request, controller stores it
*
* Store task in DB
* Dispatch AsyncTaskCreated event w/ new real ID
* Find class responsible for handling given task type
* Call "start" on said class
* - ie. new App\SiteEnvironmentBackup( $newTask )->step1();
* $newTask->status = 'ACTIVE';
* $newTask->save();
*/
$asyncTaskController->store( $newTask );
/**
* Workflow Needed from here
*
* Multi-Step, Sequential, Asyncronous Workflow
*
* ie.
*
* ->step1() makes request to Orchestration server to start new
* SiteEnvironmentBackup "Job". Request immediately returns a "JobID"
*
* When Job is finished, Orch Server pings Laravel to on
* /api/orch_job_finished with Job Result Data
*
* ->step2() is called, analyizes last Job Result Data, makes request
* to yet another external server asyncronously
*
* Process continues until we run out of steps, then
* App\SiteEnvironmentBackup( ... )->verifyResults() is called
*
* If results are valid, $newTask->status = 'FINISHED';, otherwise,
* $newTask->status = 'FAILED'; & $newTask->failureData = { ... };
*
* Goals:
* - Would like an expressive way of defining this
* "Multi-Step, Sequential, Asyncronous Workflow" within a single
* class, even if other classes are needed to modularize the logic
*
* Foreseen Issues:
* - With potentially 100 different taskTypes, I don't want to
* register event listeners for all of them globally as they will
* only be used when a task of that type is running. Is this a real
* issue?
* - Not sure how to make sure each "step" in the given class has
* access to the data from the previous - Likely store it on the
* $newTask itself in the DB
* - Need to keep track of which "Step" a task is on
* - When Orch server pings Laravel on /api/orch_job_finished
* (for ex.), need to have some way of querying DB for AsyncTasks
* that are on a "step" that depend on that "Job" being finished,
* then need to call App\SiteEnvironmentBackup( ... )->next()
*
* Possible Syntax Ideas:
*/
class MultiStepTask {
protected $task;
public function __construct( AsyncTask $task ) {
$this->task = $task;
}
public function start() {
// Calls first step, moves step cursor
}
public function next() {
// Calls subsequent steps, moves step cursor
}
public function verifyResults() { return true; }
}
class SiteEnvironmentBackup extends MultiStepTask {
protected $steps = [
'checkPlanQuota',
'startBackupJob',
'verifyBackupValidity'
];
/**
* Will need access to Service Container - ie. OrchService $orch
*/
public function __construct( AsyncTask $task, OrchService $orch ) {
parent::__construct( $task );
//...
}
public function checkPlanQuota() {
//...
}
public function startBackupJob() {
//...
}
public function verifyBackupValidity() {
//...
}
/**
* Optional to define this, parent class returns true if not
*/
public function verifyResults() {
//...
}
}
$taskHandler = new SiteEnvironmentBackup( $newTask );
$taskHandler->start();
// $newTask->status = 'ACTIVE';
// Orch server pings Laravel
$taskHandler->next();
$taskHandler->next(); //...
$valid = $taskHandler->verifyResults();
if ( $valid ) {
// $newTask->status = 'FINISHED';
// Dispatch AsyncTaskFinished event
} else {
// $newTask->status = 'FAILED';
// Dispatch AsyncTaskFailed event
}