Skip to content

Commit

Permalink
VACMS-13847: Create ContentReleaseStrategy plugin system. (#14160)
Browse files Browse the repository at this point in the history
* VACMS-13847: Create ContentReleaseStrategy plugin system.

* Adds strategy plugin system.

* Expands tests, fixes issues from copypasta mistakes.

* GitHubRepositoryDispatch stuff, tests, etc.

* Adds LocalFilesystemBuildFile strategy plugin and tests.

* Adds stuff to support watchdog_exception() call.
  • Loading branch information
ndouglas authored Jun 27, 2023
1 parent ee7c20c commit c18721c
Show file tree
Hide file tree
Showing 32 changed files with 1,459 additions and 8 deletions.
1 change: 1 addition & 0 deletions config/sync/core.extension.yml
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ module:
va_gov_build_trigger: 0
va_gov_bulk: 0
va_gov_clp: 0
va_gov_content_release: 0
va_gov_dashboards: 0
va_gov_db: 0
va_gov_entity_browser: 0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Drupal\va_gov_consumers\Exception;

/**
* Exception thrown when GitHub repository dispatch fails.
*/
class GitHubRepositoryDispatchException extends \Exception {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Drupal\va_gov_consumers\GitHub;

use Drupal\va_gov_consumers\Exception\GitHubRepositoryDispatchException;
use GuzzleHttp\ClientInterface as HttpClientInterface;
use Psr\Http\Message\ResponseInterface;

Expand Down Expand Up @@ -173,16 +174,32 @@ public function triggerWorkflow(string $workflowName, string $ref, array $params
* {@inheritDoc}
*/
public function listWorkflowRuns(string $workflowName, array $params = []) : array {
$response = $this->listWorkflowRunsRaw($workflowName, $params);
$data = json_decode($response->getBody()->getContents(), TRUE);
return $data;
try {
$response = $this->listWorkflowRunsRaw($workflowName, $params);
if ($response->getStatusCode() !== 200) {
throw new GitHubRepositoryDispatchException('Listing workflow runs failed with status code: ' . $response->getStatusCode());
}
$data = json_decode($response->getBody()->getContents(), TRUE);
return $data;
}
catch (\Throwable $exception) {
throw new GitHubRepositoryDispatchException('Listing workflow runs failed.', $exception->getCode(), $exception);
}
}

/**
* {@inheritDoc}
*/
public function repositoryDispatchWorkflow(string $eventType, object $clientPayload = NULL) : void {
$this->repositoryDispatchWorkflowRaw($eventType, $clientPayload);
try {
$response = $this->repositoryDispatchWorkflowRaw($eventType, $clientPayload);
if ($response->getStatusCode() !== 204) {
throw new GitHubRepositoryDispatchException('Repository dispatch failed with status code: ' . $response->getStatusCode());
}
}
catch (\Throwable $exception) {
throw new GitHubRepositoryDispatchException('Repository dispatch failed.', $exception->getCode(), $exception);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public function triggerWorkflow(string $workflowName, string $ref, array $params
* @param array $params
* A list of named params to pass to the action as arguments. Keys should
* match the action input names.
*
* @throws \Drupal\va_gov_consumers\Exception\GitHubRepositoryDispatchException
* If listing workflow runs fails or returns an unexpected status code.
*/
public function listWorkflowRuns(string $workflowName, array $params = []) : array;

Expand All @@ -53,6 +56,9 @@ public function listWorkflowRuns(string $workflowName, array $params = []) : arr
* A custom webhook event name. Must be 100 characters or fewer.
* @param object $clientPayload
* Optional extra data to send as the payload with the dispatch.
*
* @throws \Drupal\va_gov_consumers\Exception\GitHubRepositoryDispatchException
* If the repository dispatch fails or returns an unexpected status code.
*/
public function repositoryDispatchWorkflow(string $eventType, object $clientPayload = NULL) : void;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Drupal\va_gov_content_release\Annotation;

use Drupal\Component\Annotation\Plugin;

/**
* Defines the annotation object for content release strategy plugins.
*
* @see plugin_api
* @see \Drupal\va_gov_content_release\Strategy\Plugin\StrategyPluginInterface
* @see \Drupal\va_gov_content_release\Strategy\Plugin\StrategyPluginManager
*
* @Annotation
*/
class ContentReleaseStrategy extends Plugin {

/**
* The plugin ID.
*
* @var string
*/
public $id;

/**
* The human-readable name of the environemnt.
*
* @var \Drupal\Core\Annotation\Translation
*/
public $label;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Drupal\va_gov_content_release\Exception;

/**
* Exception thrown when a content release is already in progress.
*/
class ContentReleaseInProgressException extends \Exception {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Drupal\va_gov_content_release\Exception;

/**
* Exception thrown when GitHub repository dispatch fails.
*/
class GitHubRepositoryDispatchException extends \Exception {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Drupal\va_gov_content_release\Exception;

/**
* Exception thrown when the strategy encounters an error and cannot continue.
*/
class StrategyErrorException extends \Exception {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Drupal\va_gov_content_release\Exception;

/**
* Exception thrown when the strategy cannot be found.
*/
class UnknownStrategyException extends \Exception {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

namespace Drupal\va_gov_content_release\GitHub;

use Drupal\va_gov_consumers\GitHub\GitHubClientInterface;
use Drupal\va_gov_content_release\Exception\ContentReleaseInProgressException;
use Drupal\va_gov_content_release\Exception\GitHubRepositoryDispatchException;

/**
* The GitHub repository dispatch service.
*
* This service is used to dispatch repository dispatch events to GitHub, to
* check whether a current workflow is pending, and to make these operations
* testable.
*
* @see \Drupal\va_gov_content_release\GitHub\GitHubRepositoryDispatchInterface
*/
class GitHubRepositoryDispatch implements GitHubRepositoryDispatchInterface {

/**
* The GitHub client.
*
* @var \Drupal\va_gov_consumers\GitHub\GitHubClientInterface
*/
protected $client;

/**
* Constructor.
*
* @param \Drupal\va_gov_consumers\GitHub\GitHubClientInterface $client
* The GitHub client.
*/
public function __construct(GitHubClientInterface $client) {
$this->client = $client;
}

/**
* Build parameters for determining whether a workflow is pending.
*
* @param int|null $time
* The time to use for the 'created' parameter. Defaults to the current
* time.
*
* @return array
* The parameters.
*/
public function buildPendingWorkflowParams(int $time = NULL) : array {
$time = $time ?? time();
$sinceTimestamp = $time - (2 * 60 * 60);
return [
'status' => 'pending',
'created' => '>=' . date('c', $sinceTimestamp),
];
}

/**
* {@inheritDoc}
*/
public function dispatch() : void {
try {
if ($this->isPending()) {
throw new ContentReleaseInProgressException('A workflow is already pending.');
}
$this->client->repositoryDispatchWorkflow(static::EVENT_TYPE);
}
catch (ContentReleaseInProgressException $exception) {
throw $exception;
}
catch (\Throwable $throwable) {
throw new GitHubRepositoryDispatchException('Repository dispatch failed.', $throwable->getCode(), $throwable);
}
}

/**
* {@inheritDoc}
*/
public function isPending() : bool {
try {
$parameters = $this->buildPendingWorkflowParams();
$workflowRuns = $this->client->listWorkflowRuns(static::EVENT_TYPE . '.yml', $parameters);
return !empty($workflowRuns['total_count']) && $workflowRuns['total_count'] > 0;
}
catch (\Throwable $throwable) {
throw new GitHubRepositoryDispatchException('Failed to get workflow runs.', $throwable->getCode(), $throwable);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Drupal\va_gov_content_release\GitHub;

/**
* An interface for the GitHub repository dispatch service.
*
* This service is used to dispatch repository dispatch events to GitHub, to
* check whether a current workflow is pending, and to make these operations
* testable.
*
* @see \Drupal\va_gov_content_release\GitHub\GitHubRepositoryDispatch
*/
interface GitHubRepositoryDispatchInterface {

// The event type for content release.
const EVENT_TYPE = 'content-release';

/**
* Dispatch a repository dispatch event to trigger content release.
*
* @throws \Drupal\va_gov_content_release\Exception\GitHubRepositoryDispatchException
* If the repository dispatch fails.
*/
public function dispatch() : void;

/**
* Check whether a workflow is pending.
*
* @return bool
* TRUE if a workflow is pending, FALSE otherwise.
*
* @throws \Drupal\va_gov_content_release\Exception\GitHubRepositoryDispatchException
* If the request fails.
*/
public function isPending() : bool;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Drupal\va_gov_content_release\Plugin\Strategy;

use Drupal\va_gov_content_release\Exception\StrategyErrorException;
use Drupal\va_gov_content_release\Strategy\Plugin\StrategyPluginBase;

/**
* Exception test strategy.
*
* This always throws an exception.
*
* @ContentReleaseStrategy(
* id = "test_exception",
* label = @Translation("Exception Test")
* )
*/
class ExceptionTest extends StrategyPluginBase {

/**
* {@inheritDoc}
*/
public function triggerContentRelease() : void {
throw new StrategyErrorException('This is a test exception.');
}

}
Loading

0 comments on commit c18721c

Please sign in to comment.