Add ability to permanently delete workflow

[MAILPOET-4540]
This commit is contained in:
John Oleksowicz
2022-09-20 10:29:17 -05:00
committed by David Remer
parent a4c8caa664
commit 4e82c5334b
6 changed files with 143 additions and 1 deletions

View File

@@ -0,0 +1,42 @@
<?php declare(strict_types = 1);
namespace MailPoet\Automation\Engine\Endpoints\Workflows;
use MailPoet\API\REST\Request;
use MailPoet\API\REST\Response;
use MailPoet\Automation\Engine\API\Endpoint;
use MailPoet\Automation\Engine\Data\Workflow;
use MailPoet\Automation\Engine\Exceptions\InvalidStateException;
use MailPoet\Automation\Engine\Storage\WorkflowStorage;
use MailPoet\Validator\Builder;
class WorkflowsDeleteEndpoint extends Endpoint {
/** @var WorkflowStorage */
private $workflowStorage;
public function __construct(
WorkflowStorage $workflowStorage
) {
$this->workflowStorage = $workflowStorage;
}
public function handle(Request $request): Response {
$workflowId = $request->getParam('id');
if (!is_int($workflowId)) {
throw InvalidStateException::create();
}
$existingWorkflow = $this->workflowStorage->getWorkflow($workflowId);
if (!$existingWorkflow instanceof Workflow) {
throw InvalidStateException::create();
}
$this->workflowStorage->deleteWorkflow($existingWorkflow);
return new Response(null);
}
public static function getRequestSchema(): array {
return [
'id' => Builder::integer()->required(),
];
}
}

View File

@@ -8,6 +8,7 @@ use MailPoet\Automation\Engine\Control\TriggerHandler;
use MailPoet\Automation\Engine\Endpoints\System\DatabaseDeleteEndpoint;
use MailPoet\Automation\Engine\Endpoints\System\DatabasePostEndpoint;
use MailPoet\Automation\Engine\Endpoints\Workflows\WorkflowsCreateFromTemplateEndpoint;
use MailPoet\Automation\Engine\Endpoints\Workflows\WorkflowsDeleteEndpoint;
use MailPoet\Automation\Engine\Endpoints\Workflows\WorkflowsDuplicateEndpoint;
use MailPoet\Automation\Engine\Endpoints\Workflows\WorkflowsGetEndpoint;
use MailPoet\Automation\Engine\Endpoints\Workflows\WorkflowsPutEndpoint;
@@ -73,8 +74,9 @@ class Engine {
$this->wordPress->addAction(Hooks::API_INITIALIZE, function (API $api) {
$api->registerGetRoute('workflows', WorkflowsGetEndpoint::class);
$api->registerPutRoute('workflows/(?P<id>\d+)', WorkflowsPutEndpoint::class);
$api->registerPostRoute('workflows/create-from-template', WorkflowsCreateFromTemplateEndpoint::class);
$api->registerDeleteRoute('workflows/(?P<id>\d+)', WorkflowsDeleteEndpoint::class);
$api->registerPostRoute('workflows/(?P<id>\d+)/duplicate', WorkflowsDuplicateEndpoint::class);
$api->registerPostRoute('workflows/create-from-template', WorkflowsCreateFromTemplateEndpoint::class);
$api->registerPostRoute('system/database', DatabasePostEndpoint::class);
$api->registerDeleteRoute('system/database', DatabaseDeleteEndpoint::class);
$api->registerGetRoute('workflow-templates', WorkflowTemplatesGetEndpoint::class);

View File

@@ -59,6 +59,7 @@ class Migrator {
updated_at timestamp NOT NULL,
subjects longtext,
PRIMARY KEY (id),
INDEX (workflow_id),
INDEX (status)
);
");

View File

@@ -142,6 +142,44 @@ class WorkflowStorage {
}, (array)$data);
}
public function deleteWorkflow(Workflow $workflow): void {
$workflowTable = esc_sql($this->workflowTable);
$versionTable = esc_sql($this->versionsTable);
$workflowRunTable = esc_sql($this->wpdb->prefix . 'mailpoet_workflow_runs');
$workflowRunLogTable = esc_sql($this->wpdb->prefix . 'mailpoet_workflow_run_logs');
$workflowId = $workflow->getId();
$runLogsQuery = $this->wpdb->prepare(
"
DELETE FROM $workflowRunLogTable
WHERE workflow_run_id IN (
SELECT id FROM $workflowRunTable
WHERE workflow_id = %d
)
",
$workflowId
);
if (!is_string($runLogsQuery)) {
throw Exceptions\InvalidStateException::create();
}
$logsDeleted = $this->wpdb->query($runLogsQuery);
if (!is_int($logsDeleted)) {
throw Exceptions::databaseError($this->wpdb->last_error);
}
$runsDeleted = $this->wpdb->delete($this->wpdb->prefix . 'mailpoet_workflow_runs', ['workflow_id' => $workflowId]);
if (!is_int($runsDeleted)) {
throw Exceptions::databaseError($this->wpdb->last_error);
}
$versionsDeleted = $this->wpdb->delete($versionTable, ['workflow_id' => $workflowId]);
if (!is_int($versionsDeleted)) {
throw Exceptions::databaseError($this->wpdb->last_error);
}
$workflowDeleted = $this->wpdb->delete($workflowTable, ['id' => $workflowId]);
if (!is_int($workflowDeleted)) {
throw Exceptions::databaseError($this->wpdb->last_error);
}
}
public function truncate(): bool {
$workflowTable = esc_sql($this->workflowTable);
$versionTable = esc_sql($this->versionsTable);

View File

@@ -143,6 +143,7 @@ class ContainerConfigurator implements IContainerConfigurator {
$container->autowire(\MailPoet\Automation\Engine\Endpoints\Workflows\WorkflowsPutEndpoint::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Endpoints\Workflows\WorkflowsCreateFromTemplateEndpoint::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Endpoints\Workflows\WorkflowsDuplicateEndpoint::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Endpoints\Workflows\WorkflowsDeleteEndpoint::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Endpoints\System\DatabasePostEndpoint::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Endpoints\System\DatabaseDeleteEndpoint::class)->setPublic(true);
// Automation - core integration

View File

@@ -4,6 +4,10 @@ namespace MailPoet\Test\Automation\Engine\Storage;
use MailPoet\Automation\Engine\Data\Step;
use MailPoet\Automation\Engine\Data\Workflow;
use MailPoet\Automation\Engine\Data\WorkflowRun;
use MailPoet\Automation\Engine\Data\WorkflowRunLog;
use MailPoet\Automation\Engine\Storage\WorkflowRunLogStorage;
use MailPoet\Automation\Engine\Storage\WorkflowRunStorage;
use MailPoet\Automation\Engine\Storage\WorkflowStorage;
use MailPoet\Automation\Integrations\MailPoet\Triggers\SomeoneSubscribesTrigger;
@@ -73,6 +77,60 @@ class WorkflowStorageTest extends \MailPoetTest
$this->assertEmpty($this->testee->getActiveWorkflowsByTrigger($subscriberTrigger));
}
public function testItCanDeleteAWorkflow() {
$workflowToDelete = $this->createEmptyWorkflow();
$workflowToKeep = $this->createEmptyWorkflow();
expect($this->testee->getWorkflows())->count(2);
$this->testee->deleteWorkflow($workflowToDelete);
expect($this->testee->getWorkflows())->count(1);
expect($this->testee->getWorkflow($workflowToDelete->getId()))->null();
$workflowToKeepFromDatabase = $this->testee->getWorkflow($workflowToKeep->getId());
$this->assertInstanceOf(Workflow::class, $workflowToKeepFromDatabase);
expect($workflowToKeepFromDatabase->getVersionId())->notNull();
}
public function testItCanDeleteWorkflowsRelatedData() {
$workflowRunStorage = $this->diContainer->get(WorkflowRunStorage::class);
$workflowRunLogStorage = $this->diContainer->get(WorkflowRunLogStorage::class);
$workflows = [
'toDelete' => $this->createEmptyWorkflow(),
'toKeep' => $this->createEmptyWorkflow()
];
$runs = [
'toDelete' => [],
'toKeep' => []
];
$runLogs = [
'toDelete' => [],
'toKeep' => []
];
foreach ($workflows as $type => $workflow) {
for ($i = 0; $i < 2; $i++) {
$workflowRun = new WorkflowRun($workflow->getId(), $workflow->getVersionId(), 'trigger-key', []);
$runId = $workflowRunStorage->createWorkflowRun($workflowRun);
$runs[$type][] = $runId;
for ($i = 0; $i < 2; $i++) {
$log = new WorkflowRunLog($runId, "step-{$i}");
$logId = $workflowRunLogStorage->createWorkflowRunLog($log);
$runLogs[$type][] = $logId;
}
}
}
$this->testee->deleteWorkflow($workflows['toDelete']);
foreach ($runs['toDelete'] as $runId) {
expect($workflowRunStorage->getWorkflowRun($runId))->null();
}
foreach ($runs['toKeep'] as $runId) {
expect($workflowRunStorage->getWorkflowRun($runId))->notNull();
}
foreach ($runLogs['toDelete'] as $runLogId) {
expect($workflowRunLogStorage->getWorkflowRunLog($runLogId))->null();
}
foreach ($runLogs['toKeep'] as $runLogId) {
expect($workflowRunLogStorage->getWorkflowRunLog($runLogId))->notNull();
}
}
private function createEmptyWorkflow(string $name="test"): Workflow {
$workflow = new Workflow($name, [], new \WP_User());
$workflowId = $this->testee->createWorkflow($workflow);