Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F4792747
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
138 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/app/Actions/ActionsReport.php b/app/Actions/ActionsReport.php
index a1ae343..7879aa4 100644
--- a/app/Actions/ActionsReport.php
+++ b/app/Actions/ActionsReport.php
@@ -1,90 +1,88 @@
<?php
namespace Nasqueron\Notifications\Actions;
class ActionsReport {
/**
* List of actions
*
* @var Action[]
*/
public $actions = [];
/**
* Report created date
*
* @var int
*/
public $created;
/**
* The entry gate
*
* @var string
*/
public $gate;
/**
* The entry door
*
* @var string
*/
public $door;
/**
* Initializes a new instance of an actions report
*/
public function __construct () {
$this->created = time();
}
///
/// Properties
///
/**
* Sets the gate and the door for this report
*
* @param string $gate The gate
* @param string $door The door
*/
public function attachToGate (string $gate, string $door) : void {
$this->gate = $gate;
$this->door = $door;
}
/**
* Adds an action to the list of actions to report
*
* @param Action $action The action to add
*/
public function addAction (Action $action) : void {
$this->actions[] = $action;
}
/**
* Determines if one of the action has failed.
- *
- * @return bool
*/
public function containsError () : bool {
foreach ($this->actions as $action) {
if ($action->error !== null) {
return true;
}
}
return false;
}
///
/// Output
///
/**
* Gets a JSON string representation of the current instance
*/
public function __toString () : string {
return json_encode($this, JSON_PRETTY_PRINT);
}
}
diff --git a/app/Analyzers/BasePayloadAnalyzer.php b/app/Analyzers/BasePayloadAnalyzer.php
index f531724..fd5f69b 100644
--- a/app/Analyzers/BasePayloadAnalyzer.php
+++ b/app/Analyzers/BasePayloadAnalyzer.php
@@ -1,180 +1,174 @@
<?php
namespace Nasqueron\Notifications\Analyzers;
use BadMethodCallException;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Storage;
abstract class BasePayloadAnalyzer {
///
/// Constants
///
/**
* The name of the service, used to get specific classes and config
*/
const SERVICE_NAME = "UnknownService";
///
/// Private members
///
/**
* The project name, used to load specific configuration and offer defaults
* @var string
*/
protected $project;
/**
* The request content, as a structured data
* @var \stdClass
*/
protected $payload;
/**
* The configuration for the payload analyzer
* @var PayloadAnalyzerConfiguration
*/
protected $configuration;
///
/// Constructor
///
/**
* Creates a new JenkinsPayloadAnalyzer instance.
*
* @param string $project
* @param \stdClass $payload
*/
public function __construct(string $project, \stdClass $payload) {
$this->project = $project;
$this->payload = $payload;
$this->loadConfiguration();
}
///
/// Configuration
///
/**
* The default name of the configuration file
*/
const CONFIG_DEFAULT_FILE = 'default.json';
/**
* Gets the full path to the configuration file.
- *
- * @return string
*/
public function getConfigurationFileName () : string {
$dir = Config::get(
'services.'
. strtolower(static::SERVICE_NAME)
. '.analyzer.configDir'
);
$filename = $dir . '/' . $this->project . '.json';
if (!Storage::exists($filename)) {
return $dir . '/' . static::CONFIG_DEFAULT_FILE;
}
return $filename;
}
/**
- * Gets full qualified class name for configuration.
- *
- * @return string
+ * Gets full qualified class name for configuration
*/
private function getCandidateConfigurationClassName() : string {
return 'Nasqueron\Notifications\Analyzers\\' . static::SERVICE_NAME //ns
. "\\"
. static::SERVICE_NAME . 'PayloadAnalyzerConfiguration'; // class
}
/**
* Gets full qualified class name for configuration if existing,
* or PayloadAnalyzerConfiguration class if not.
*
* @return string The configuration class to use
*/
private function getConfigurationClassName () : string {
$class = $this->getCandidateConfigurationClassName();
if (class_exists($class)) {
return $class;
}
return PayloadAnalyzerConfiguration::class;
}
/**
* Loads configuration for the analyzer
*/
public function loadConfiguration () : void {
$fileName = $this->getConfigurationFileName();
$class = $this->getConfigurationClassName();
$mapper = new \JsonMapper();
$this->configuration = $mapper->map(
json_decode(Storage::get($fileName)),
new $class($this->project)
);
}
///
/// Properties
///
/**
* Gets the name of the item.
*
* @var string
*/
public function getItemName () : string {
throw new BadMethodCallException(<<<MSG
The getItemName method must be implemented in the analyzer class if used.
MSG
);
}
/**
* Determines if the event isn't related to a specific item,
* but to the general service.
- *
- * @return bool
*/
public function isAdministrativeEvent () : bool {
return false;
}
/**
* Gets the group for a specific payload.
*
* @return string The group, central part of the routing key
*/
public function getGroup () : string {
// Some events are organization-level only and can't be mapped
// to projects.
if ($this->isAdministrativeEvent()) {
return $this->configuration->administrativeGroup;
}
// If the payload is about some repository matching a table of
// symbols, we need to sort it to the right group.
$item = $this->getItemName();
foreach ($this->configuration->map as $mapping) {
if ($mapping->doesItemBelong($item)) {
return $mapping->group;
}
}
return $this->configuration->getDefaultGroup();
}
}
diff --git a/app/Analyzers/DockerHub/BaseEvent.php b/app/Analyzers/DockerHub/BaseEvent.php
index 106d021..d4e3c37 100644
--- a/app/Analyzers/DockerHub/BaseEvent.php
+++ b/app/Analyzers/DockerHub/BaseEvent.php
@@ -1,48 +1,44 @@
<?php
namespace Nasqueron\Notifications\Analyzers\DockerHub;
abstract class BaseEvent {
/**
* @var \stdClass
*/
protected $payload;
/**
* Initializes a new instance of the BaseEvent object.
*
* @param \stdClass $payload The payload to analyze
*/
public function __construct (\stdClass $payload) {
$this->payload = $payload;
}
///
/// Public methods
///
/**
* Gets notification payload.
*
* This method allows analyzer to edit the payload.
*/
- public function getPayload () {
+ public function getPayload () : \stdClass {
return $this->payload;
}
/**
* Gets notification text for this event.
- *
- * @return string
*/
- abstract public function getText();
+ abstract public function getText() : string;
/**
* Gets notification link related to this event.
- *
- * @return string
*/
- abstract public function getLink();
+ abstract public function getLink() : string;
}
diff --git a/app/Analyzers/DockerHub/BuildFailureEvent.php b/app/Analyzers/DockerHub/BuildFailureEvent.php
index 35ec1c8..6047a33 100644
--- a/app/Analyzers/DockerHub/BuildFailureEvent.php
+++ b/app/Analyzers/DockerHub/BuildFailureEvent.php
@@ -1,67 +1,58 @@
<?php
namespace Nasqueron\Notifications\Analyzers\DockerHub;
use Nasqueron\Notifications\Facades\Mailgun;
class BuildFailureEvent extends BaseEvent {
/**
* Initializes a new instance of the BuildFailureEvent object.
*
* @param \stdClass $payload The payload to analyze
*/
public function __construct (\stdClass $payload) {
parent::__construct($payload);
$this->payload = $this->getMailGunPayload();
}
/**
* Gets a MailGun message.
- *
- * @return \stdClass
*/
- private function getMailGunPayload () {
+ private function getMailGunPayload () : \stdClass {
return Mailgun::fetchMessageFromPayload($this->payload);
}
- /**
- * @return string
- */
- private function getMailBody () {
+ private function getMailBody () : string {
$bodyProperty = 'body-plain';
return $this->payload->$bodyProperty;
}
/**
* Extracts a regular expression from the mail body.
*
* @param $string Regular expression
* @return string
*/
- private function extractFromBody ($regex) {
+ private function extractFromBody ($regex) : string {
preg_match($regex, $this->getMailBody(), $matches);
return $matches[1];
}
/**
* Gets text from payload.
- *
- * @return string
*/
- public function getText() {
+ public function getText() : string {
$repo = $this->extractFromBody("@\"(.*?\/.*?)\"@");
return "Image build by Docker Hub registry failure for $repo";
}
/**
* Gets link from payload.
- *
- * @return string
*/
- public function getLink() {
+ public function getLink() : string {
return $this->extractFromBody("@(https\:\/\/hub.docker.com\/r.*)@");
}
}
diff --git a/app/Analyzers/DockerHub/PushEvent.php b/app/Analyzers/DockerHub/PushEvent.php
index 8be97bb..4d2980f 100644
--- a/app/Analyzers/DockerHub/PushEvent.php
+++ b/app/Analyzers/DockerHub/PushEvent.php
@@ -1,28 +1,24 @@
<?php
namespace Nasqueron\Notifications\Analyzers\DockerHub;
class PushEvent extends BaseEvent {
/**
* Gets text from payload.
- *
- * @return string
*/
- public function getText() {
+ public function getText() : string {
$repo = $this->payload->repository->repo_name;
$who = $this->payload->push_data->pusher;
return "New image pushed to Docker Hub registry for $repo by $who";
}
/**
* Gets link from payload.
- *
- * @return string
*/
- public function getLink() {
+ public function getLink() : string {
return $this->payload->repository->repo_url;
}
}
diff --git a/app/Analyzers/GitHub/Events/CommitCommentEvent.php b/app/Analyzers/GitHub/Events/CommitCommentEvent.php
index d24087b..ae19ef1 100644
--- a/app/Analyzers/GitHub/Events/CommitCommentEvent.php
+++ b/app/Analyzers/GitHub/Events/CommitCommentEvent.php
@@ -1,38 +1,34 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* CommitCommentEvent payload analyzer
*
* @link https://developer.github.com/v3/activity/events/types/#commitcommentevent
*/
class CommitCommentEvent extends Event {
/**
* Gets description for the payload
- *
- * @return string
*/
public function getDescription () : string {
$comment = $this->payload->comment;
return trans(
'GitHub.EventsDescriptions.CommitCommentEvent',
[
'author' => $comment->user->login,
'commit' => substr($comment->commit_id, 0, 8),
'excerpt' => self::cut($comment->body),
]
);
}
/**
* Gets link for the payload
- *
- * @return string
*/
public function getLink () : string {
return $this->payload->comment->html_url;
}
}
diff --git a/app/Analyzers/GitHub/Events/CreateEvent.php b/app/Analyzers/GitHub/Events/CreateEvent.php
index fb4bec3..a0b5023 100644
--- a/app/Analyzers/GitHub/Events/CreateEvent.php
+++ b/app/Analyzers/GitHub/Events/CreateEvent.php
@@ -1,78 +1,74 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* CreateEvent payload analyzer
*
* We don't support the repository ref type, as, according the documentation,
* "webhooks will not receive this event for created repositories".webhooks
*
* Another case when we won't receive the event is when at least four tags are
* pushed at once.
*
* @link https://developer.github.com/v3/activity/events/types/#createevent
*/
class CreateEvent extends Event {
use WithRef;
/**
* Gets description for the payload
- *
- * @return string
*/
public function getDescription () : string {
$repository = $this->payload->repository->full_name;
$type = $this->payload->ref_type;
$ref = $this->payload->ref;
if (!self::isValidRefType($type)) {
return trans(
'GitHub.EventsDescriptions.CreateEventUnknown',
[
'type' => $type,
'ref' => $ref,
]
);
}
return trans(
'GitHub.EventsDescriptions.CreateEvent',
[
'type' => $type,
'ref' => $ref,
'repository' => $repository,
]
);
}
/**
* Gets link segments for the type
*
* @return array
* @SuppressWarnings(PHPMD.UnusedPrivateMethod)
*/
private function getLinkRefSegments () : array {
return [
'tag' => '/releases/tag/',
'branch' => '/tree/',
];
}
/**
* Gets link for the payload
- *
- * @return string
*/
public function getLink () : string {
$type = $this->payload->ref_type;
$ref = $this->payload->ref;
$url = $this->payload->repository->html_url;
$url .= $this->getLinkRefSegment($type);
$url .= $ref;
return $url;
}
}
diff --git a/app/Analyzers/GitHub/Events/DefaultBranchEvent.php b/app/Analyzers/GitHub/Events/DefaultBranchEvent.php
index d6b5bca..265a434 100644
--- a/app/Analyzers/GitHub/Events/DefaultBranchEvent.php
+++ b/app/Analyzers/GitHub/Events/DefaultBranchEvent.php
@@ -1,41 +1,37 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* DefaultBranchEvent payload analyzer
*
* @link https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#repository
*/
class DefaultBranchEvent extends Event {
/**
* Gets description for the payload
- *
- * @return string
*/
public function getDescription () : string {
$repository = $this->payload->repository->full_name;
$old = $this->payload->changes->default_branch->from;
$new = $this->payload->repository->default_branch;
return trans(
'GitHub.EventsDescriptions.DefaultBranchEvent',
[
'old' => $old,
'new' => $new,
'repository' => $repository,
]
);
}
/**
* Gets link for the payload
- *
- * @return string
*/
public function getLink () : string {
return $this->payload->repository->html_url;
}
}
diff --git a/app/Analyzers/GitHub/Events/DeleteEvent.php b/app/Analyzers/GitHub/Events/DeleteEvent.php
index 20a8a6d..b2cb660 100644
--- a/app/Analyzers/GitHub/Events/DeleteEvent.php
+++ b/app/Analyzers/GitHub/Events/DeleteEvent.php
@@ -1,73 +1,67 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* DeleteEvent payload analyzer
*
* Another case when we won't receive the event is when at least four tags are
* deleted at once.
*
* @link https://developer.github.com/v3/activity/events/types/#deleteevent
*/
class DeleteEvent extends Event {
use WithRef;
/**
* Gets description for the payload
- *
- * @return string
*/
public function getDescription () : string {
$repository = $this->payload->repository->full_name;
$type = $this->payload->ref_type;
$ref = $this->payload->ref;
if (!self::isValidRefType($type)) {
return trans(
'GitHub.EventsDescriptions.DeleteEventUnknown',
[
'type' => $type,
'ref' => $ref,
]
);
}
return trans(
'GitHub.EventsDescriptions.DeleteEvent',
[
'type' => $type,
'ref' => $ref,
'repository' => $repository,
]
);
}
/**
* Gets link segments for the type
- *
- * @return Array
* @SuppressWarnings(PHPMD.UnusedPrivateMethod)
*/
- private function getLinkRefSegments () {
+ private function getLinkRefSegments () : array {
return [
'tag' => '/tags',
'branch' => '/branches',
];
}
/**
* Gets link for the payload
- *
- * @return string
*/
public function getLink () : string {
$type = $this->payload->ref_type;
$url = $this->payload->repository->html_url;
$url .= $this->getLinkRefSegment($type);
return $url;
}
}
diff --git a/app/Analyzers/GitHub/Events/Event.php b/app/Analyzers/GitHub/Events/Event.php
index 33c7606..77b474f 100644
--- a/app/Analyzers/GitHub/Events/Event.php
+++ b/app/Analyzers/GitHub/Events/Event.php
@@ -1,88 +1,87 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
class Event {
///
/// Properties
///
/**
* The payload
*
* @var \stdClass
*/
protected $payload;
///
/// Constructor
///
public function __construct ($payload) {
$this->payload = $payload;
}
///
/// Gets or initialize relevant class
///
/**
* Gets class name from the GitHub webhooks event name
*
* @param string $eventName The event name (e.g. commit_comment)
* @return string The event class name (e.g. CommitCommentEvent)
*/
- public static function getClass (string $eventName) {
+ public static function getClass (string $eventName) : string {
return __NAMESPACE__ . '\\' . self::toCamelCase($eventName) . 'Event';
}
private static function toCamelCase (string $string) : string {
return str_replace(" ", "", ucwords(str_replace("_", " ", $string)));
}
/**
* Gets an instance of the event class, from the
*
* @param string $eventName The event name (e.g. commit_comment)
- * @return Event
*/
- public static function forPayload (string $eventName, $payload) {
+ public static function forPayload (string $eventName, $payload) : Event {
$class = self::getClass($eventName);
if (!class_exists($class)) {
throw new \InvalidArgumentException(
"Class doesn't exist: $class (for $eventName)"
);
}
return new $class($payload);
}
///
/// Helper methods
///
/**
* Cuts a text
*
* @param string $text The text to cut
* @param int $strLen The amount of characters to allow [optional]
* @param string $symbol The symbol to append to a cut text [optional]
*/
public static function cut (
string $text,
int $strLen = 114,
string $symbol = '…'
- ) {
+ ) : string {
$len = strlen($text);
if ($len <= $strLen) {
return $text;
}
if ($strLen < 1) {
return $symbol;
}
return substr($text, 0, $strLen - 1) . $symbol;
}
}
diff --git a/app/Analyzers/GitHub/Events/ForkEvent.php b/app/Analyzers/GitHub/Events/ForkEvent.php
index 078a4fb..7609956 100644
--- a/app/Analyzers/GitHub/Events/ForkEvent.php
+++ b/app/Analyzers/GitHub/Events/ForkEvent.php
@@ -1,37 +1,33 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* ForkEvent payload analyzer
*
* Triggered when a repository is forked.
*
* @link https://developer.github.com/v3/activity/events/types/#forkevent
*/
class ForkEvent extends Event {
/**
* Gets description for the payload
- *
- * @return string
*/
public function getDescription () : string {
return trans(
'GitHub.EventsDescriptions.ForkEvent',
[
'repo_base' => $this->payload->repository->full_name,
'repo_fork' => $this->payload->forkee->full_name,
]
);
}
/**
* Gets link for the payload
- *
- * @return string
*/
public function getLink () : string {
return $this->payload->forkee->html_url;
}
}
diff --git a/app/Analyzers/GitHub/Events/IssueCommentEvent.php b/app/Analyzers/GitHub/Events/IssueCommentEvent.php
index 40f5556..5d293dc 100644
--- a/app/Analyzers/GitHub/Events/IssueCommentEvent.php
+++ b/app/Analyzers/GitHub/Events/IssueCommentEvent.php
@@ -1,64 +1,60 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* IssueCommentEvent payload analyzer
*
* @link https://developer.github.com/v3/activity/events/types/#issuecommentevent
*/
class IssueCommentEvent extends Event {
/**
* Determines if the action is valid.
*
* @param string $action The action to check
* @return bool true if the action is valid; otherwise, false
*/
- protected static function isValidAction (string $action) {
+ protected static function isValidAction (string $action) : bool {
$actions = ['created', 'edited', 'deleted'];
return in_array($action, $actions);
}
/**
* Gets description for the payload.
- *
- * @return string
*/
public function getDescription () : string {
$action = $this->payload->action;
if (!static::isValidAction($action)) {
return trans(
'GitHub.EventsDescriptions.IssueCommentEventUnknown',
['action' => $action]
);
}
$key = 'GitHub.EventsDescriptions.IssueCommentEventPerAction.';
$key .= $action;
$comment = $this->payload->comment;
$issue = $this->payload->issue;
return trans(
$key,
[
'author' => $comment->user->login,
'issueNumber' => $issue->number,
'issueTitle' => $issue->title,
'excerpt' => self::cut($comment->body),
]
);
}
/**
* Gets link for the payload.
- *
- * @return string
*/
public function getLink () : string {
return $this->payload->comment->html_url;
}
}
diff --git a/app/Analyzers/GitHub/Events/PingEvent.php b/app/Analyzers/GitHub/Events/PingEvent.php
index 7583108..fd562c6 100644
--- a/app/Analyzers/GitHub/Events/PingEvent.php
+++ b/app/Analyzers/GitHub/Events/PingEvent.php
@@ -1,35 +1,31 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* PingEvent payload analyzer
*
* @link https://developer.github.com/webhooks/#ping-event
*/
class PingEvent extends Event {
/**
* Gets description for the payload
- *
- * @return string
*/
public function getDescription () : string {
return trans(
'GitHub.EventsDescriptions.PingEvent',
[
'zen' => $this->payload->zen,
'hook_id' => $this->payload->hook_id,
]
);
}
/**
* Gets link for the payload
- *
- * @return string
*/
public function getLink () : string {
return '';
}
}
diff --git a/app/Analyzers/GitHub/Events/PullRequestEvent.php b/app/Analyzers/GitHub/Events/PullRequestEvent.php
index bbe0b1e..39369b2 100644
--- a/app/Analyzers/GitHub/Events/PullRequestEvent.php
+++ b/app/Analyzers/GitHub/Events/PullRequestEvent.php
@@ -1,91 +1,85 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* PingEvent payload analyzer
*
* @link https://developer.github.com/v3/activity/events/types/#pullrequestevent
*/
class PullRequestEvent extends Event {
/**
* Determines if the action is valid.
*
* @param string $action The action to check
* @return bool true if the action is valid; otherwise, false
*/
- protected static function isValidAction(string $action) {
+ protected static function isValidAction(string $action) : bool {
$actions = [
'assigned', 'unassigned',
'labeled', 'unlabeled',
'opened', 'closed',
'edited', 'reopened',
];
return in_array($action, $actions);
}
/**
* Gets description for the payload.
- *
- * @return string
*/
public function getDescription () : string {
$action = $this->payload->action;
if (!static::isValidAction($action)) {
return trans(
'GitHub.EventsDescriptions.PullRequestEventUnknown',
['action' => $action]
);
}
$key = 'GitHub.EventsDescriptions.PullRequestEventPerAction.';
$key .= $action;
return trans($key, $this->getLocalisationParameters());
}
/**
* Gets the parameters to pass to the localisation message
- *
- * @return array
*/
- private function getLocalisationParameters () {
+ private function getLocalisationParameters () : array {
$parameters = [
'author' => $this->payload->sender->login,
'number' => $this->payload->number,
'title' => $this->payload->pull_request->title,
];
switch ($this->payload->action) {
case 'assigned':
$parameters['assignee'] = $this->getLastAssignee();
break;
}
return $parameters;
}
/**
* @return string The last assignee username, or "" if there is no assignee.
*/
- private function getLastAssignee() {
+ private function getLastAssignee() : string {
$assignees = $this->payload->pull_request->assignees;
if (count($assignees) === 0) {
return ""; // No assignee.
}
$assignee = array_pop($assignees);
return $assignee->login;
}
/**
* Gets link for the payload.
- *
- * @return string
*/
public function getLink () : string {
return $this->payload->pull_request->html_url;
}
}
diff --git a/app/Analyzers/GitHub/Events/PushEvent.php b/app/Analyzers/GitHub/Events/PushEvent.php
index 6d005bb..b31d200 100644
--- a/app/Analyzers/GitHub/Events/PushEvent.php
+++ b/app/Analyzers/GitHub/Events/PushEvent.php
@@ -1,67 +1,63 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* PushEvent payload analyzer
*
* @link https://developer.github.com/v3/activity/events/types/#pushevent
*/
class PushEvent extends Event {
use WithCommit;
use WithRepoAndBranch;
/**
* Gets the description message key according the amount of commits
*
* @param int $count The count of commits
* @return string The l10n message key for description
*/
- private static function getDescriptionMessageKey (int $count) {
+ private static function getDescriptionMessageKey (int $count) : string {
$key = 'GitHub.EventsDescriptions.PushEvent';
if ($count === 0) {
return $key . '.0';
}
return $key . '.n';
}
/**
* Gets description for the payload
- *
- * @return string
*/
public function getDescription () : string {
$n = count($this->payload->commits);
if ($n === 1) {
// If only one commit is pushed at the time,
// we want a description for this commit.
return $this->getHeadCommitDescription();
}
// Otherwise, we want a description for the push.
return trans(self::getDescriptionMessageKey($n), [
'user' => $this->payload->pusher->name,
'count' => $n,
'repoAndBranch' => $this->getWhere(),
]);
}
/**
* Gets link for the payload
- *
- * @return string
*/
public function getLink () : string {
$n = count($this->payload->commits);
if ($n === 1) {
return $this->payload->head_commit->url;
}
return $this->payload->compare;
}
}
diff --git a/app/Analyzers/GitHub/Events/RepositoryEvent.php b/app/Analyzers/GitHub/Events/RepositoryEvent.php
index ffa9335..3b6e84c 100644
--- a/app/Analyzers/GitHub/Events/RepositoryEvent.php
+++ b/app/Analyzers/GitHub/Events/RepositoryEvent.php
@@ -1,66 +1,62 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* RepositoryEvent payload analyzer
*
* @link https://developer.github.com/v3/activity/events/types/#repositoryevent
*/
class RepositoryEvent extends Event {
/**
* Determines if the action is valid.
*
* @param string $action The action to check
* @return bool true if the action is valid; otherwise, false
*/
- protected static function isValidAction (string $action) {
+ protected static function isValidAction (string $action) : bool {
$actions = ['created', 'deleted', 'publicized', 'privatized'];
return in_array($action, $actions);
}
/**
* Gets description for the payload
- *
- * @return string
*/
public function getDescription () : string {
$action = $this->payload->action;
if (!static::isValidAction($action)) {
return trans(
'GitHub.EventsDescriptions.RepositoryEventUnknown',
['action' => $action]
);
}
$key = 'GitHub.EventsDescriptions.RepositoryEventPerAction.';
$key .= $action;
$repository = $this->payload->repository->full_name;
$message = trans($key, ['repository' => $repository]);
if ($this->payload->repository->fork) {
$message .= trans('GitHub.EventsDescriptions.RepositoryEventFork');
}
$description = (string)$this->payload->repository->description;
if ($description !== "") {
$message .= trans('GitHub.Separator');
$message .= $description;
}
return $message;
}
/**
* Gets link for the payload
- *
- * @return string
*/
public function getLink () : string {
return $this->payload->repository->html_url;
}
}
diff --git a/app/Analyzers/GitHub/Events/StatusEvent.php b/app/Analyzers/GitHub/Events/StatusEvent.php
index f7b5079..e23181d 100644
--- a/app/Analyzers/GitHub/Events/StatusEvent.php
+++ b/app/Analyzers/GitHub/Events/StatusEvent.php
@@ -1,65 +1,57 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* StatusEvent payload analyzer
*
* @link https://developer.github.com/v3/activity/events/types/#statusevent
*/
class StatusEvent extends Event {
/**
* Gets state localized message
- *
- * @return string
*/
- private function getState () {
+ private function getState () : string {
$state = $this->payload->state; // pending, success, failure, or error
$key = 'GitHub.StatusEventState.' . $state;
return trans($key);
}
/**
* Gets status result
- *
- * @return string
*/
- private function getStatusResult () {
+ private function getStatusResult () : string {
$glue = trans('GitHub.Separator');
$fragments = array_filter([
$this->payload->context,
$this->payload->description,
$this->getState(),
]);
return implode($glue, $fragments);
}
/**
* Gets description for the payload
- *
- * @return string
*/
public function getDescription () : string {
return trans('GitHub.EventsDescriptions.StatusEvent', [
'commit' => substr($this->payload->sha, 0, 8),
'status' => $this->getStatusResult(),
]);
}
/**
* Gets link for the payload
- *
- * @return string
*/
public function getLink () : string {
$url = $this->payload->target_url;
if ($url === null) {
return "";
}
return $url;
}
}
diff --git a/app/Analyzers/GitHub/Events/UnknownEvent.php b/app/Analyzers/GitHub/Events/UnknownEvent.php
index 9e38340..44eeecc 100644
--- a/app/Analyzers/GitHub/Events/UnknownEvent.php
+++ b/app/Analyzers/GitHub/Events/UnknownEvent.php
@@ -1,45 +1,41 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* Unknown event payload analyzer
*
* This is a fallack when no specific class exists for this event type.
*/
class UnknownEvent extends Event {
/**
* @var string
*/
private $eventType;
/**
* Initializes a new instance of the UnknownEvent class, an Event analyzer
* class to handle unknown events type.
*
* @param string $eventType The event type (e.g. push)
*/
public function __construct ($eventType, $payload = null) {
$this->eventType = $eventType;
parent::__construct($payload);
}
/**
* Gets description for the payload
- *
- * @return string
*/
public function getDescription () : string {
return "Some $this->eventType happened";
}
/**
* Gets link for the payload
- *
- * @return string
*/
public function getLink () : string {
return "";
}
}
diff --git a/app/Analyzers/GitHub/Events/WatchEvent.php b/app/Analyzers/GitHub/Events/WatchEvent.php
index c118d01..f929304 100644
--- a/app/Analyzers/GitHub/Events/WatchEvent.php
+++ b/app/Analyzers/GitHub/Events/WatchEvent.php
@@ -1,41 +1,37 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* WatchEvent payload analyzer
*
* Triggered when an user stars a repository.
*
* The current starring action on GitHub is the old watch action. To avoid
* to break code, former event name have been kept in the API.
*
* @link https://developer.github.com/changes/2012-09-05-watcher-api/
* @link https://developer.github.com/v3/activity/events/types/#watchevent
*/
class WatchEvent extends Event {
/**
* Gets description for the payload
- *
- * @return string
*/
public function getDescription () : string {
return trans(
'GitHub.EventsDescriptions.WatchEvent',
[
'user' => $this->payload->sender->login,
'repository' => $this->payload->repository->full_name,
]
);
}
/**
* Gets link for the payload
- *
- * @return string
*/
public function getLink () : string {
return $this->payload->sender->html_url;
}
}
diff --git a/app/Analyzers/GitHub/Events/WithCommit.php b/app/Analyzers/GitHub/Events/WithCommit.php
index 87fc4aa..780dadb 100644
--- a/app/Analyzers/GitHub/Events/WithCommit.php
+++ b/app/Analyzers/GitHub/Events/WithCommit.php
@@ -1,63 +1,60 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* Helper methods for events with a need to specify commit information
* (e.g. push)
*
* @link https://developer.github.com/v3/activity/events/types/#pushevent
*/
trait WithCommit {
/**
* Gets the title of the head commit
- * @return string
*/
- private function getHeadCommitTitle () {
+ private function getHeadCommitTitle () : string {
return static::getCommitTitle($this->payload->head_commit->message);
}
/**
* Extracts the commit title from the whole commit message.
*
* @param string $message The commit message
* @return string The commit title
*/
- public static function getCommitTitle (string $message) {
+ public static function getCommitTitle (string $message) : string {
// Discards extra lines
$pos = strpos($message, "\n");
if ($pos > 0) {
$message = substr($message, 0, $pos);
}
// Short messages are returned as is
// Longer messages are truncated
return self::cut($message, 72);
}
/**
* Gets the description text for the head commit.
- *
- * @return string
*/
- private function getHeadCommitDescription () {
+ private function getHeadCommitDescription () : string {
$commit = $this->payload->head_commit;
$committer = $commit->committer->username;
$author = $commit->author->username;
$message = trans('GitHub.Commits.Message', [
'committer' => $committer,
'title' => $this->getHeadCommitTitle(),
]);
if ($committer !== $author) {
$message .= trans('GitHub.Commits.Authored', [
'author' => $author,
]);
}
return $message;
}
}
diff --git a/app/Analyzers/GitHub/Events/WithRef.php b/app/Analyzers/GitHub/Events/WithRef.php
index 2454188..2d289a4 100644
--- a/app/Analyzers/GitHub/Events/WithRef.php
+++ b/app/Analyzers/GitHub/Events/WithRef.php
@@ -1,43 +1,43 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub\Events;
/**
* References helper methods for events using ref and ref_type fields.
* (e.g. create and delete)
*
* @link https://developer.github.com/v3/activity/events/types/#createevent
* @link https://developer.github.com/v3/activity/events/types/#deleteevent
*/
trait WithRef {
/**
* Determines if the ref type is valid.
*
* The ref type 'repository' is deemed invalid, as we shouldn't receive it.
*
* @param string $type The ref type to check
* @return bool true if the ref type id valid; otherwise, false
*/
- protected static function isValidRefType (string $type) {
+ protected static function isValidRefType (string $type) : bool {
$types = ['branch', 'tag'];
return in_array($type, $types);
}
/**
* Gets link ref segment for the payload
*
* @param string $type The reference type
* @return string the part of the URL for this reference type (e.g. /tree/)
*/
- protected function getLinkRefSegment (string $type) {
+ protected function getLinkRefSegment (string $type) : string {
$segments = $this->getLinkRefSegments();
if (!array_key_exists($type, $segments)) {
throw new \InvalidArgumentException;
}
return $segments[$type];
}
}
diff --git a/app/Analyzers/GitHub/GitHubPayloadAnalyzer.php b/app/Analyzers/GitHub/GitHubPayloadAnalyzer.php
index 58a18dd..6f63197 100644
--- a/app/Analyzers/GitHub/GitHubPayloadAnalyzer.php
+++ b/app/Analyzers/GitHub/GitHubPayloadAnalyzer.php
@@ -1,128 +1,121 @@
<?php
namespace Nasqueron\Notifications\Analyzers\GitHub;
use Nasqueron\Notifications\Analyzers\BasePayloadAnalyzer;
use Nasqueron\Notifications\Analyzers\GitHub\Events\Event;
use Nasqueron\Notifications\Analyzers\GitHub\Events\UnknownEvent;
class GitHubPayloadAnalyzer extends BasePayloadAnalyzer {
/**
* The name of the service, used to get specific classes and config
*/
const SERVICE_NAME = "GitHub";
///
/// Private members
///
/**
* The GitHub event triggering this request
* @var string
*/
private $event;
/**
* The payload analyzer event
*
* @var \Nasqueron\Notifications\Analyzers\GitHub\Events\Event
*/
private $analyzerEvent;
///
/// Constructor
///
/**
* Creates a new GitHubPayloadAnalyzer instance.
*
* @param string $project
* @param string $event
* @param \stdClass $payload
*/
public function __construct(
string $project,
string $event,
\stdClass $payload
) {
parent::__construct($project, $payload);
$this->event = self::getEvent($event, $payload);
try {
$this->analyzerEvent = Event::forPayload($event, $payload);
} catch (\InvalidArgumentException $ex) {
$this->analyzerEvent = new UnknownEvent($event);
}
}
private static function getEvent (string $event, \stdClass $payload) : string {
// Some payload uses a specialized event class.
if (
$event == "repository" && $payload->action == "edited" &&
property_exists($payload->changes, "default_branch")
) {
return "default_branch";
}
return $event;
}
///
/// Properties
///
/**
* Gets the name of the item, ie here of the name of the repository.
- *
- * @var string
*/
public function getItemName () : string {
if ($this->isAdministrativeEvent()) {
return '';
}
return $this->payload->repository->name;
}
///
/// Qualification of the payload
///
- /**
- * @return bool
- */
public function isAdministrativeEvent () : bool {
$administrativeEvents = [
'membership', // Member added to team
'ping', // Special ping pong event, fired on new hook
'repository', // Repository created
];
return in_array($this->event, $administrativeEvents);
}
///
/// Description of the payload
///
/**
* Gets a short textual description of the event.
- *
- * @return string
*/
public function getDescription () : string {
return $this->analyzerEvent->getDescription();
}
/**
* Gets a link to view the event on GitHub.
*
* @return string The most relevant URL
*/
public function getLink () : string {
return $this->analyzerEvent->getLink();
}
}
diff --git a/app/Analyzers/ItemGroupMapping.php b/app/Analyzers/ItemGroupMapping.php
index 4037b0d..cc995ad 100644
--- a/app/Analyzers/ItemGroupMapping.php
+++ b/app/Analyzers/ItemGroupMapping.php
@@ -1,65 +1,63 @@
<?php
namespace Nasqueron\Notifications\Analyzers;
use Illuminate\Support\Str;
/**
* Map items (repositories, projects, items, etc.) names to groups
*/
class ItemGroupMapping {
///
/// Properties
///
/**
* The group the mapped items belong to
*
* @var string
*/
public $group;
/**
* An array of the items to map, each item a string with the name of the
* repository, project or item used for mapping.
* The wildcard '*' is allowed to specify several items.
*
* @var array
*/
public $items = [];
///
/// Helper methods
///
/**
* Determines if the specified item matches a pattern.
*
* @param string $pattern The pattern, with * allowed as wildcard character
* @param string $item The item name to compare with the pattern
* @return bool
*/
public static function doesItemMatch (
string $pattern,
string $item
) : bool {
return Str::is($pattern, $item);
}
/**
* Determines if the specified item belong to this mapping
- *
- * @return bool
*/
public function doesItemBelong (string $actualItem) : bool {
foreach ($this->items as $candidateItem) {
if (static::doesItemMatch($candidateItem, $actualItem)) {
return true;
}
}
return false;
}
}
diff --git a/app/Analyzers/Jenkins/JenkinsPayloadAnalyzer.php b/app/Analyzers/Jenkins/JenkinsPayloadAnalyzer.php
index b004cee..7165fc5 100644
--- a/app/Analyzers/Jenkins/JenkinsPayloadAnalyzer.php
+++ b/app/Analyzers/Jenkins/JenkinsPayloadAnalyzer.php
@@ -1,82 +1,77 @@
<?php
namespace Nasqueron\Notifications\Analyzers\Jenkins;
use Nasqueron\Notifications\Analyzers\BasePayloadAnalyzer;
class JenkinsPayloadAnalyzer extends BasePayloadAnalyzer {
/**
* The name of the service, used to get specific classes and config
*/
const SERVICE_NAME = "Jenkins";
///
/// Payload custom properties
///
/**
* Gets the name of the item, ie here of the job.
*
* @var string
*/
public function getItemName () : string {
return $this->payload->name;
}
///
/// Notify only on failure helper methods
///
/**
* Tries to get build status.
*
* @param out string &$status
* @return bool indicates if the build status is defined in the payload
*/
private function tryGetBuildStatus (string &$status) : bool {
if (!isset($this->payload->build->status)) {
return false;
}
$status = $this->payload->build->status;
return true;
}
- /**
- * @return bool
- */
public function shouldNotifyOnlyOnFailure () : bool {
return in_array(
$this->getItemName(),
$this->configuration->notifyOnlyOnFailure
);
}
/**
* Determines if the build status is a failure.
- *
- * @return bool
*/
public function isFailure () : bool {
$status = "";
if (!$this->tryGetBuildStatus($status)) {
return false;
}
return $status === "FAILURE"
|| $status === "ABORTED"
|| $status === "UNSTABLE";
}
/**
* Indicates if we should handle this payload to trigger a notification.
*
* @return bool if false, this payload is to be ignored for notifications
*/
public function shouldNotify () : bool {
return $this->isFailure() || !$this->shouldNotifyOnlyOnFailure();
}
}
diff --git a/app/Analyzers/Phabricator/PhabricatorGroupMapping.php b/app/Analyzers/Phabricator/PhabricatorGroupMapping.php
index a89ff66..c4c90b8 100644
--- a/app/Analyzers/Phabricator/PhabricatorGroupMapping.php
+++ b/app/Analyzers/Phabricator/PhabricatorGroupMapping.php
@@ -1,39 +1,37 @@
<?php
namespace Nasqueron\Notifications\Analyzers\Phabricator;
use Nasqueron\Notifications\Analyzers\ItemGroupMapping;
use Nasqueron\Notifications\Phabricator\PhabricatorStory;
class PhabricatorGroupMapping extends ItemGroupMapping {
///
/// Extra properties
///
/**
* An array of words, each item a string with a word to find in the story.
*
* @var array
*/
public $words = [];
///
/// Helper methods to process words
///
/**
* Determines if the specified story belong to this mapping
- *
- * @return bool
*/
public function doesStoryBelong (PhabricatorStory $story) : bool {
foreach ($this->words as $word) {
if (stripos($story->text, $word) !== false) {
return true;
}
}
return false;
}
}
diff --git a/app/Config/Reporting/ServiceReportEntry.php b/app/Config/Reporting/ServiceReportEntry.php
index 1266c5f..148fe79 100644
--- a/app/Config/Reporting/ServiceReportEntry.php
+++ b/app/Config/Reporting/ServiceReportEntry.php
@@ -1,129 +1,127 @@
<?php
namespace Nasqueron\Notifications\Config\Reporting;
use Nasqueron\Notifications\Config\Services\Service;
use Nasqueron\Notifications\Facades\ProjectsMap;
final class ServiceReportEntry extends BaseReportEntry {
///
/// Private members
///
/**
* @var Service
*/
private $service;
///
/// Public properties
///
/**
* @var string
*/
public $gate;
/**
* @var string
*/
public $door;
/**
* @var string
*/
public $instance;
/**
* @var string
*/
public $status = "";
///
/// Constructor
///
public function __construct (Service $service) {
$this->service = $service;
$this->query();
}
///
/// Report builder
///
/**
* Queries the service to fill public properties.
*/
protected function query () : void {
// Direct properties
$this->gate = $this->service->gate;
$this->door = $this->service->door;
$this->instance = (string)$this->service->instance;
// Properties to query with business logic
$this->status = $this->getServiceStatus();
}
/**
* @return string An issue to fix, or an empty string if all looks good.
*/
protected function getServiceStatus () : string {
if ($this->isPhabricatorServiceWithNotCachedProjectsMap()) {
return "Projects map not cached.";
}
return "";
}
/**
* Determines if the service matches the following issue to report:
* - service is Phabricator
* - instance doesn't have the projects' name/PHID map in cache
- *
- * @return bool
*/
protected function isPhabricatorServiceWithNotCachedProjectsMap () : bool {
if ($this->service->gate !== 'Phabricator') {
return false;
}
$map = ProjectsMap::fetch($this->service->door);
return !$map->isCached();
}
///
/// Format
///
/**
* Gets the entry as an array. Formats empty string.
*
* @return string[]
*/
public function toArray () : array {
return [
$this->gate,
$this->door,
$this->instance,
$this->status,
];
}
/**
* Gets the entry as an array. Formats empty string.
*
* @return string[]
*/
public function toFancyArray () : array {
return [
$this->gate,
$this->door,
self::fancyString($this->instance, 'ø'),
self::fancyString($this->status, '✓'),
];
}
}
diff --git a/app/Config/Services/Services.php b/app/Config/Services/Services.php
index 22b5928..2d2fc10 100644
--- a/app/Config/Services/Services.php
+++ b/app/Config/Services/Services.php
@@ -1,107 +1,105 @@
<?php
namespace Nasqueron\Notifications\Config\Services;
use Illuminate\Support\Facades\Storage;
class Services {
///
/// Properties
///
/**
* @var Service[]
*/
public $services = [];
///
/// Constructors
///
/**
* @param string $file The JSON file to deserialize
* @return Services The deserialized instance
*/
public static function loadFromJson (string $file) : Services {
$data = json_decode(Storage::get($file));
$mapper = new \JsonMapper();
return $mapper->map($data, new self());
}
///
/// Methods to get a list of services
///
/**
* Gets the services found in credentials.json configuration file.
- *
- * @return Service[]
*/
- public function get () {
+ public function get () : array {
return $this->services;
}
/**
* Gets all the services for a specific gate.
*
* @param string $gate The gate (e.g. GitHub)
* @return Service[]
*/
public function getForGate (string $gate) : array {
$services = [];
foreach ($this->services as $service) {
if ($service->gate === $gate) {
$services[] = $service;
}
}
return $services;
}
///
/// Methods to find a service matching criteria
///
/**
* Gets the service for a specific gate and door
*
* @param string $gate The gate (e.g. GitHub)
* @param string $door The door (e.g. Nasqueron)
* @return Service|null The service information is found; otherwise, null.
*/
public function findServiceByDoor (string $gate, string $door) : ?Service {
foreach ($this->services as $service) {
if ($service->gate === $gate && $service->door === $door) {
return $service;
}
}
return null;
}
/**
* Finds a service for a specific gate, property and value
*
* @param string $gate The gate (e.g. Phabricator)
* @param string $property The property to check (e.g. instance)
* @param mixed $value The property value to find
* (e.g. 'http://devcentral.nasqueron.org')
* @return Service|null The service information is found; otherwise, null.
*/
public function findServiceByProperty (
string $gate,
string $property,
mixed $value
) : ?Service {
foreach ($this->services as $service) {
if ($service->gate === $gate && $service->$property === $value) {
return $service;
}
}
return null;
}
}
diff --git a/app/Console/Commands/ConfigShow.php b/app/Console/Commands/ConfigShow.php
index c69ed23..ec53de0 100644
--- a/app/Console/Commands/ConfigShow.php
+++ b/app/Console/Commands/ConfigShow.php
@@ -1,105 +1,105 @@
<?php
namespace Nasqueron\Notifications\Console\Commands;
use Illuminate\Console\Command;
use Nasqueron\Notifications\Config\Reporting\ConfigReport;
class ConfigShow extends Command {
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'config:show';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Show notifications center configuration';
/**
* @var \Nasqueron\Notifications\Config\Reporting\ConfigReport
*/
private $report;
///
/// Prepare information tables
///
/**
* Gets the services (defined in credentials.json) as table rows.
*
- * @return array
+ * @return string[][]
*/
protected function getServicesTableRows () : array {
$rows = [];
foreach ($this->report->services as $service) {
$rows[] = $service->toFancyArray();
}
return $rows;
}
/**
* Gets features as table rows
*
- * @return array
+ * @return string[][]
*/
protected function getFeaturesTableRows () : array {
$rows = [];
foreach ($this->report->features as $feature) {
$rows[] = $feature->toFancyArray();
}
return $rows;
}
///
/// Handle the command
///
/**
* Executes the console command.
*/
public function handle () : void {
$this->prepareReport();
$this->printGates();
$this->printFeatures();
$this->printServices();
}
protected final function prepareReport() : void {
$this->report = new ConfigReport();
}
protected final function printGates () : void {
$this->info("Gates:\n");
foreach ($this->report->gates as $gate) {
$this->line('- ' . $gate);
}
}
protected final function printFeatures () : void {
$this->info("\nFeatures:\n");
$this->table(
['Feature', 'Enabled'],
$this->getFeaturesTableRows()
);
}
protected final function printServices () : void {
$this->info("\nServices declared in credentials:\n");
$this->table(
['Gate', 'Door', 'Instance', 'Status'],
$this->getServicesTableRows()
);
}
}
diff --git a/app/Console/Commands/NotificationsPayload.php b/app/Console/Commands/NotificationsPayload.php
index 77d6f5a..8ce3065 100644
--- a/app/Console/Commands/NotificationsPayload.php
+++ b/app/Console/Commands/NotificationsPayload.php
@@ -1,225 +1,219 @@
<?php
namespace Nasqueron\Notifications\Console\Commands;
use Nasqueron\Notifications\Notifications\Notification;
use Nasqueron\Notifications\Phabricator\PhabricatorStory;
use Illuminate\Console\Command;
use InvalidArgumentException;
use ReflectionClass;
class NotificationsPayload extends Command {
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'notifications:payload {service} {payload} {args*}';
/**
* The console command description.
*
* @var string
*/
protected $description = <<<'TXT'
Gets a notification payload from a service payload
TXT;
/**
* The service to handle a payload for.
*
* @var string
*/
private $service;
/**
* The payload.
*
* @var string
*/
private $payload;
/**
* The parameters to pass to the notifications class constructor.
*
* An array with arguments' names as keys, arguments' values as values.
*
* @var array
*/
private $constructor;
/**
* Executes the console command.
*/
public function handle() : void {
if ($this->parseArguments()) {
$this->printNotification();
}
}
/**
* Parses arguments passed to the command.
*
* @return bool true if arguments looks good; otherwise, false.
*/
private function parseArguments () : bool {
try {
$this->parseService();
$this->parsePayload();
$this->parseConstructorParameters();
} catch (InvalidArgumentException $ex) {
$this->error($ex->getMessage());
return false;
}
return true;
}
/**
* Parses service argument.
*
* Fills it to the service property.
*
* @throws InvalidArgumentException when a notification class can't be
* found for the requested service.
*/
private function parseService () : void {
$this->service = $this->argument('service');
if (!class_exists($this->getNotificationClass())) {
throw new InvalidArgumentException(
"Unknown service: $this->service"
);
}
}
/**
* Parses path to the payload argument.
*
* Fills the content of the file to the payload property.
*
* @throws InvalidArgumentException when payload file is not found.
*/
private function parsePayload () : void {
$payloadFile = $this->argument('payload');
if (!file_exists($payloadFile)) {
throw new InvalidArgumentException("File not found: $payloadFile");
}
$this->payload = file_get_contents($payloadFile);
}
/**
* Parses all the extra arguments and sets the constructor property
* as an array of constructor arguments.
*
* @throws InvalidArgumentException on wrong arguments count.
*/
private function parseConstructorParameters () : void {
$keys = $this->getNotificationConstructorParameters();
$values = $this->argument('args');
$values['payload'] = $this->payload;
$this->constructor = self::argumentsArrayCombine($keys, $values);
$this->constructor['payload'] = $this->formatPayload();
}
/**
* Formats payload to pass to constructor
*
- * @return PhabricatorStory|stdClass A deserialization of the payload
+ * @return PhabricatorStory|\stdClass A deserialization of the payload
*/
- private function formatPayload() {
+ private function formatPayload() : \stdClass|PhabricatorStory {
if ($this->service === "Phabricator") {
$project = $this->constructor['project'];
return PhabricatorStory::loadFromJson($project, $this->payload);
}
return json_decode($this->payload);
}
/**
* Creates an array by using one array for keys and another for its values.
*
* @param array $keys
* @param array $values
* @return array
*
* @throws InvalidArgumentException when keys and values counts don't match
*/
public static function argumentsArrayCombine (
array $keys, array $values
) : array {
$countKeys = count($keys);
$countValues = count($values);
if ($countKeys != $countValues) {
throw new InvalidArgumentException(<<<MSG
Number of arguments mismatch: got $countValues but expected $countKeys.
MSG
);
}
return array_combine($keys, $values);
}
/**
* Initializes a new instance of the relevant notification class,
* with the arguments given in the constructor property.
- *
- * @return \Nasqueron\Notifications\Notifications\Notification
*/
private function getNotification () : Notification {
$class = $this->getNotificationClass();
$args = array_values($this->constructor);
return new $class(...$args);
}
/**
* Gets the notification in JSON format.
- *
- * @return string
*/
private function formatNotification () : string {
return json_encode($this->getNotification(), JSON_PRETTY_PRINT);
}
/**
* Prints the notification for the service, payload and specified arguments.
*/
private function printNotification () : void {
$this->line($this->formatNotification());
}
/**
- * Gets the notification class for the specified service.
- *
- * @return string
+ * Gets the notification class for the specified service
*/
private function getNotificationClass () : string {
$namespace = "Nasqueron\Notifications\Notifications\\";
return $namespace . $this->service . "Notification";
}
/**
* Gets an array with the parameters to pass to the constructor
* of the notification class for the specified service.
*
- * @return array
+ * @return string[]
*/
private function getNotificationConstructorParameters () : array {
$parameters = [];
$class = new ReflectionClass($this->getNotificationClass());
foreach ($class->getConstructor()->getParameters() as $parameter) {
$parameters[] = $parameter->getName();
}
return $parameters;
}
}
diff --git a/app/Contracts/APIClient.php b/app/Contracts/APIClient.php
index 7bfe9d1..5cf3b85 100644
--- a/app/Contracts/APIClient.php
+++ b/app/Contracts/APIClient.php
@@ -1,24 +1,24 @@
<?php
namespace Nasqueron\Notifications\Contracts;
interface APIClient {
/**
* Sets API end point
*
* @param string $url The API end point URL
* @return void
*/
- public function setEndPoint (string $url);
+ public function setEndPoint (string $url) : void;
/**
* Calls an API method
*
* @param string $method The method to call
* @param array $arguments The arguments to use
* @return mixed The API result
*/
- public function call (string $method, array $arguments = []);
+ public function call (string $method, array $arguments = []) : mixed;
}
diff --git a/app/Contracts/APIFactory.php b/app/Contracts/APIFactory.php
index 797a695..879c211 100644
--- a/app/Contracts/APIFactory.php
+++ b/app/Contracts/APIFactory.php
@@ -1,15 +1,14 @@
<?php
namespace Nasqueron\Notifications\Contracts;
interface APIFactory {
/**
* Gets an instance of the API client class
*
* @param string $endPoint The API end point
- * @return APIClient
*/
- public function get (string $endPoint);
+ public function get (string $endPoint) : APIClient;
}
diff --git a/app/Events/DockerHubPayloadEvent.php b/app/Events/DockerHubPayloadEvent.php
index 4372c91..ffff94a 100644
--- a/app/Events/DockerHubPayloadEvent.php
+++ b/app/Events/DockerHubPayloadEvent.php
@@ -1,52 +1,50 @@
<?php
namespace Nasqueron\Notifications\Events;
use Illuminate\Queue\SerializesModels;
class DockerHubPayloadEvent extends Event {
use SerializesModels;
/**
* The gate door which receives the request
* @var string
*/
public $door;
/**
* The event triggering this request
* @var string
*/
public $event;
/**
* The request content, as a structured data
* @var \stdClass
*/
public $payload;
/**
* Gets event according the kind of payload we receive.
- *
- * @return string
*/
public function getEvent () : string {
if (isset($this->payload->repository->repo_url)) {
return "push";
}
return "buildFailure";
}
/**
* Creates a new event instance.
*
* @param string $door
* @param \stdClass $payload
*/
public function __construct(string $door, \stdClass $payload) {
$this->door = $door;
$this->payload = $payload;
$this->event = $this->getEvent();
}
}
diff --git a/app/Events/PhabricatorPayloadEvent.php b/app/Events/PhabricatorPayloadEvent.php
index c08e4ec..3449960 100644
--- a/app/Events/PhabricatorPayloadEvent.php
+++ b/app/Events/PhabricatorPayloadEvent.php
@@ -1,54 +1,52 @@
<?php
namespace Nasqueron\Notifications\Events;
use Nasqueron\Notifications\Phabricator\PhabricatorStory;
use Illuminate\Queue\SerializesModels;
class PhabricatorPayloadEvent extends Event {
use SerializesModels;
/**
* The gate door which receives the request
* @var string
*/
public $door;
/**
* The raw payload
* @var iterable
*/
public $payload;
/**
* The story sent by the request
* @var PhabricatorStory
*/
public $story;
/**
* Gets story from the request
- *
- * @return PhabricatorStory
*/
- protected function getStory () {
+ protected function getStory () : PhabricatorStory {
return PhabricatorStory::loadFromIterable(
$this->door,
$this->payload
);
}
/**
* Creates a new event instance.
*
* @param string $door
* @param iterable $payload
*/
public function __construct(string $door, iterable $payload) {
$this->door = $door;
$this->payload = $payload;
$this->story = $this->getStory();
}
}
diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php
index 2d031b9..a9b46f0 100644
--- a/app/Exceptions/Handler.php
+++ b/app/Exceptions/Handler.php
@@ -1,75 +1,73 @@
<?php
namespace Nasqueron\Notifications\Exceptions;
use Nasqueron\Notifications\Facades\Raven;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Validation\ValidationException;
use Illuminate\Session\TokenMismatchException;
use Illuminate\Support\Facades\Config;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Exception;
class Handler extends ExceptionHandler {
/**
* A list of the exception types that should not be reported.
*
* @var string[]
*/
protected $dontReport = [
AuthorizationException::class,
CommandNotFoundException::class,
HttpException::class,
ModelNotFoundException::class,
TokenMismatchException::class,
ValidationException::class,
];
/**
* Reports or logs an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception|\Throwable $e
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function report(Exception|\Throwable $e) : void {
if (!$this->shouldReport($e)) {
return;
}
if ($this->shouldReportToSentry()) {
$this->reportToSentry($e);
}
$log = $this->container->make(LoggerInterface::class);
$log->error((string)$e);
}
/**
* Determines if the error handler should report to Sentry
- *
- * @return bool
*/
protected function shouldReportToSentry () : bool {
return Raven::isConfigured() && Config::get('app.env') !== 'testing';
}
/**
* Reports the exception to Sentry
*
* @param Exception $e The exception to report
*/
protected function reportToSentry (Exception $e) : void {
Raven::captureException($e);
}
}
diff --git a/app/Facades/Broker.php b/app/Facades/Broker.php
index 9b1f6b7..1dd2c6b 100644
--- a/app/Facades/Broker.php
+++ b/app/Facades/Broker.php
@@ -1,21 +1,19 @@
<?php
namespace Nasqueron\Notifications\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @see \Keruald\Broker\Broker
*/
class Broker extends Facade {
/**
* Gets the registered name of the component.
- *
- * @return string
*/
protected static function getFacadeAccessor() : string {
return 'broker';
}
}
diff --git a/app/Facades/DockerHub.php b/app/Facades/DockerHub.php
index 573905f..2359e67 100644
--- a/app/Facades/DockerHub.php
+++ b/app/Facades/DockerHub.php
@@ -1,21 +1,19 @@
<?php
namespace Nasqueron\Notifications\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @see \Keruald\DockerHub\Build\TriggerBuildFactory
*/
class DockerHub extends Facade {
/**
* Gets the registered name of the component.
- *
- * @return string
*/
protected static function getFacadeAccessor() : string {
return 'dockerhub';
}
}
diff --git a/app/Facades/Mailgun.php b/app/Facades/Mailgun.php
index 5ca14eb..8ea5d37 100644
--- a/app/Facades/Mailgun.php
+++ b/app/Facades/Mailgun.php
@@ -1,21 +1,19 @@
<?php
namespace Nasqueron\Notifications\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @see \Keruald\Mailgun\MailgunMessageFactory
*/
class Mailgun extends Facade {
/**
* Gets the registered name of the component.
- *
- * @return string
*/
protected static function getFacadeAccessor() : string {
return 'mailgun';
}
}
diff --git a/app/Facades/PhabricatorAPI.php b/app/Facades/PhabricatorAPI.php
index b3235a2..69dc18c 100644
--- a/app/Facades/PhabricatorAPI.php
+++ b/app/Facades/PhabricatorAPI.php
@@ -1,21 +1,19 @@
<?php
namespace Nasqueron\Notifications\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @see \Nasqueron\Notifications\Phabricator\PhabricatorAPIFactory
*/
class PhabricatorAPI extends Facade {
/**
* Gets the registered name of the component.
- *
- * @return string
*/
protected static function getFacadeAccessor() : string {
return 'phabricator-api';
}
}
diff --git a/app/Facades/ProjectsMap.php b/app/Facades/ProjectsMap.php
index ccac4dc..c3b6827 100644
--- a/app/Facades/ProjectsMap.php
+++ b/app/Facades/ProjectsMap.php
@@ -1,21 +1,19 @@
<?php
namespace Nasqueron\Notifications\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @see \Nasqueron\Notifications\Phabricator\PhabricatorAPIFactory
*/
class ProjectsMap extends Facade {
/**
* Gets the registered name of the component.
- *
- * @return string
*/
protected static function getFacadeAccessor() : string {
return 'phabricator-projectsmap';
}
}
diff --git a/app/Facades/Raven.php b/app/Facades/Raven.php
index 8473298..05a662c 100644
--- a/app/Facades/Raven.php
+++ b/app/Facades/Raven.php
@@ -1,28 +1,26 @@
<?php
namespace Nasqueron\Notifications\Facades;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Facade;
/**
* @see \Raven_Client
*/
class Raven extends Facade {
/**
* Gets the registered name of the component.
- *
- * @return string
*/
protected static function getFacadeAccessor() : string {
return 'raven';
}
/**
* Determines if a Sentry DSN is provided in the configuration
*/
public static function isConfigured () : bool {
return Config::get('services.sentry.dsn') !== null;
}
}
diff --git a/app/Facades/Report.php b/app/Facades/Report.php
index 6fe1da4..2da78c7 100644
--- a/app/Facades/Report.php
+++ b/app/Facades/Report.php
@@ -1,21 +1,19 @@
<?php
namespace Nasqueron\Notifications\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @see \Keruald\Broker\Broker
*/
class Report extends Facade {
/**
* Gets the registered name of the component.
- *
- * @return string
*/
protected static function getFacadeAccessor() : string {
return 'report';
}
}
diff --git a/app/Facades/Services.php b/app/Facades/Services.php
index a846473..957f085 100644
--- a/app/Facades/Services.php
+++ b/app/Facades/Services.php
@@ -1,21 +1,19 @@
<?php
namespace Nasqueron\Notifications\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @see \Nasqueron\Notifications\Config\Services\Services
*/
class Services extends Facade {
/**
* Gets the registered name of the component.
- *
- * @return string
*/
protected static function getFacadeAccessor() : string {
return 'services';
}
}
diff --git a/app/Http/Controllers/Gate/GateController.php b/app/Http/Controllers/Gate/GateController.php
index a3325ae..803b92f 100644
--- a/app/Http/Controllers/Gate/GateController.php
+++ b/app/Http/Controllers/Gate/GateController.php
@@ -1,120 +1,118 @@
<?php
namespace Nasqueron\Notifications\Http\Controllers\Gate;
use Nasqueron\Notifications\Config\Features;
use Nasqueron\Notifications\Config\Services\Service;
use Nasqueron\Notifications\Facades\Services;
use Nasqueron\Notifications\Facades\Report;
use Nasqueron\Notifications\Http\Controllers\Controller;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Response;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\Response as BaseResponse;
/**
* Represents a controller handling an entry-point for API payloads
*/
class GateController extends Controller {
///
/// Private members
///
/**
* @var string
*/
protected $door;
///
/// Requests
///
/**
* Handles GET requests
*/
public function onGet () : View {
// Virtually all the push APIs will send they payloads
// using a POST request, so we can provide a sensible
// default GET error message.
return view('gate/ispostonly');
}
/**
* Logs the request
*/
protected function logRequest (array $extraContextualData = []) : void {
Log::info('[Gate] New payload.', [
'service' => $this->getServiceName(),
'door' => $this->door,
] + $extraContextualData);
}
///
/// Reports
///
/**
* Initializes the report and registers it
*/
protected function initializeReport () : void {
if (Features::isEnabled('ActionsReport')) {
Report::attachToGate($this->getServiceName(), $this->door);
}
}
/**
* Renders the report
- *
- * @return \Symfony\Component\HttpFoundation\Response
*/
protected function renderReport () : BaseResponse {
if (!Features::isEnabled('ActionsReport')) {
return response("");
}
$report = App::make('report');
$statusCode = $report->containsError() ? 503 : 200;
return Response::json($report)
->setStatusCode($statusCode);
}
///
/// Credentials
///
/**
* Gets service credentials for this gate and door
*/
public function getService () : ?Service {
return Services::findServiceByDoor(
$this->getServiceName(),
$this->door
);
}
/**
* Checks if a registered service exists for this service and door.
*/
protected function doesServiceExist () : bool {
return $this->getService() !== null;
}
/**
* Gets secret for this service and door.
*
* @return string the secret, or if unknown, an empty string
*/
protected function getSecret () : string {
$service= $this->getService();
if ($service !== null) {
return $service->secret;
}
return "";
}
}
diff --git a/app/Jobs/FireJenkinsNotification.php b/app/Jobs/FireJenkinsNotification.php
index e162c35..880d141 100644
--- a/app/Jobs/FireJenkinsNotification.php
+++ b/app/Jobs/FireJenkinsNotification.php
@@ -1,49 +1,47 @@
<?php
namespace Nasqueron\Notifications\Jobs;
use Nasqueron\Notifications\Events\JenkinsPayloadEvent;
use Nasqueron\Notifications\Events\NotificationEvent;
use Nasqueron\Notifications\Notifications\JenkinsNotification;
use Illuminate\Support\Facades\Event;
class FireJenkinsNotification extends Job {
/**
* @var JenkinsPayloadEvent
*/
private $event;
/**
* Initializes a new instance of FireJenkinsNotification
*
* @param JenkinsPayloadEvent $event The event to notify
*/
public function __construct (JenkinsPayloadEvent $event) {
$this->event = $event;
}
///
/// Task
///
/**
* Executes the job.
- *
- * @return void
*/
public function handle() : void {
$notification = $this->createNotification();
if ($notification->shouldNotify()) {
Event::dispatch(new NotificationEvent($notification));
}
}
protected function createNotification() : JenkinsNotification {
return new JenkinsNotification(
$this->event->door, // project
$this->event->payload // raw content
);
}
}
diff --git a/app/Jobs/NotifyNewCommitsToDiffusion.php b/app/Jobs/NotifyNewCommitsToDiffusion.php
index acd151f..887f867 100644
--- a/app/Jobs/NotifyNewCommitsToDiffusion.php
+++ b/app/Jobs/NotifyNewCommitsToDiffusion.php
@@ -1,200 +1,198 @@
<?php
namespace Nasqueron\Notifications\Jobs;
use Nasqueron\Notifications\Actions\ActionError;
use Nasqueron\Notifications\Actions\NotifyNewCommitsAction;
use Nasqueron\Notifications\Events\ReportEvent;
use Nasqueron\Notifications\Facades\PhabricatorAPI;
use Nasqueron\Notifications\Phabricator\PhabricatorAPI as API;
use Nasqueron\Notifications\Phabricator\PhabricatorAPIException;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
use RuntimeException;
/**
* This class allows to notify Phabricator of new commits, so daemons can pull
* these new commits and add them into Diffusion.
*/
class NotifyNewCommitsToDiffusion extends Job {
///
/// Private members
///
/**
* The clone URL of the repository
*
* @var string
*/
private $repository;
/**
* @var \Nasqueron\Notifications\Phabricator\PhabricatorAPI
*/
private $api;
/**
* @var string
*/
private $callSign;
/**
* @var NotifyNewCommitsAction
*/
private $actionToReport;
/**
* @var string
*/
private $sourceProject;
///
/// Constructor
///
/**
* Initializes a new instance of NotifyNewCommitsToDiffusion.
*/
public function __construct ($sourceProject, $repository) {
$this->sourceProject = $sourceProject;
$this->repository = $repository;
}
///
/// Task
///
/**
* Executes the job.
- *
- * @return void
*/
public function handle () : void {
if (!$this->fetchRequirements()) {
return;
}
$this->initializeReport();
$this->notifyPhabricator();
$this->sendReport();
}
/**
* Initializes the actions report.
*/
private function initializeReport () : void {
$this->actionToReport = new NotifyNewCommitsAction($this->callSign);
}
/**
* Notifies Phabricator to pull from the repository.
*/
private function notifyPhabricator () : void {
try {
$this->callDiffusionLookSoon();
} catch (PhabricatorAPIException $ex) {
$actionError = new ActionError($ex);
$this->actionToReport->attachError($actionError);
Log::error($ex);
}
}
/**
* Fires a report event with the actions report.
*/
private function sendReport () : void {
$event = new ReportEvent($this->actionToReport);
Event::dispatch($event);
}
///
/// Helper methods to find correct Phabricator instance and get the API
///
/**
* Gets the relevant Phabricator project for the specified source project.
*
* @return string The Phabricator project name
*/
private function getPhabricatorProject () : string {
return $this->sourceProject;
}
///
/// Helper methods to populate object members
///
/**
* Fetches API and call sign.
*
* @return bool true if all requirement have been fetched
*/
private function fetchRequirements () : bool {
return $this->fetchAPI() && $this->fetchCallSign();
}
/**
* Fetches the Phabricator API to use for the current source project.
*
* @return bool true if an API instance has been fetched
*/
private function fetchAPI () : bool {
$project = $this->getPhabricatorProject();
try {
$this->api = PhabricatorAPI::getForProject($project);
return true;
} catch (RuntimeException $ex) {
return false;
}
}
/**
* Fetches the call sign matching the repository.
*
* @return bool true if a call sign have been found ; otherwise, false.
*/
private function fetchCallSign () : bool {
$this->callSign = $this->getCallSign();
return $this->callSign !== "";
}
///
/// Helper methods to query Phabricator API
///
/**
* Gets the call sign matching the repository URL (e.g. "OPS").
*
* @return string the repository call sign, or "" if not in Phabricator
*/
private function getCallSign () : string {
$reply = $this->api->call(
'repository.query',
[ 'remoteURIs[0]' => $this->repository ]
);
if ($reply === null || !count($reply)) {
return "";
}
return API::getFirstResult($reply)->callsign;
}
/**
* Calls the diffusion.looksoon API method.
*
* @throws PhabricatorAPIException
*/
private function callDiffusionLookSoon () : void {
$this->api->call(
'diffusion.looksoon',
[ 'callsigns[0]' => $this->callSign ]
);
}
}
diff --git a/app/Jobs/SendMessageToBroker.php b/app/Jobs/SendMessageToBroker.php
index 014f17c..c9b7545 100644
--- a/app/Jobs/SendMessageToBroker.php
+++ b/app/Jobs/SendMessageToBroker.php
@@ -1,112 +1,110 @@
<?php
namespace Nasqueron\Notifications\Jobs;
use Nasqueron\Notifications\Actions\ActionError;
use Nasqueron\Notifications\Actions\AMQPAction;
use Nasqueron\Notifications\Events\ReportEvent;
use Nasqueron\Notifications\Facades\Broker;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
class SendMessageToBroker extends Job {
///
/// Private members
///
/**
* The routing key, for topic exchange
*
* @var string
*/
private $routingKey = '';
/**
* The message to send
*
* @var string
*/
private $message = '';
/**
* The target exchange
*
* @var string
*/
private $target = '';
/**
* If not null, an exception thrown during the task
*
* @var \Exception
*/
private $exception;
///
/// Constructor
///
/**
* Creates a new job instance.
*
* @param string $target The queue or exchange to send the message to
* @param string $routingKey The routing key, for topic exchange
* @param string $message The message to send
*
* @return void
*/
public function __construct (
string $target,
string $routingKey,
string $message
) {
$this->target = $target;
$this->routingKey = $routingKey;
$this->message = $message;
}
///
/// Task
///
/**
* Executes the job.
- *
- * @return void
*/
public function handle() : void {
$this->sendMessage();
$this->report();
}
/**
* Sends the message to the broker.
*/
protected function sendMessage () : void {
try {
Broker::setExchangeTarget($this->target, "topic", true)
->routeTo($this->routingKey)
->sendMessage($this->message);
} catch (\Exception $ex) {
$this->exception = $ex;
Log::error($ex);
}
}
/**
* Prepares a report and fires a report event.
*/
protected function report () : void {
$actionToReport = new AMQPAction(
"publish",
$this->target,
$this->routingKey
);
if ($this->exception !== null) {
$actionToReport->attachError(new ActionError($this->exception));
}
Event::dispatch(new ReportEvent($actionToReport));
}
}
diff --git a/app/Jobs/TriggerDockerHubBuild.php b/app/Jobs/TriggerDockerHubBuild.php
index fc1bab7..886702d 100644
--- a/app/Jobs/TriggerDockerHubBuild.php
+++ b/app/Jobs/TriggerDockerHubBuild.php
@@ -1,85 +1,83 @@
<?php
namespace Nasqueron\Notifications\Jobs;
use Nasqueron\Notifications\Actions\ActionError;
use Nasqueron\Notifications\Actions\TriggerDockerHubBuildAction;
use Nasqueron\Notifications\Events\ReportEvent;
use Nasqueron\Notifications\Facades\DockerHub;
use Exception;
use Illuminate\Support\Facades\Event;
/**
* This class allows to trigger a new Docker Hub build.
*/
class TriggerDockerHubBuild extends Job {
///
/// Private members
///
/**
* @var string The Docker image to trigger a build for
*/
private $image;
/**
* @var TriggerDockerHubBuildAction
*/
private $actionToReport;
///
/// Constructor
///
/**
* Initializes a new instance of TriggerDockerHubBuild.
*/
public function __construct ($image) {
$this->image = $image;
}
///
/// Task
///
/**
* Executes the job.
- *
- * @return void
*/
public function handle () : void {
$this->initializeReport();
$this->triggerBuild();
$this->sendReport();
}
/**
* Initializes the actions report.
*/
private function initializeReport () : void {
$this->actionToReport = new TriggerDockerHubBuildAction($this->image);
}
/**
* Triggers a new Docker Hub build.
*/
private function triggerBuild () : void {
try {
DockerHub::build($this->image);
} catch (Exception $ex) {
$actionError = new ActionError($ex);
$this->actionToReport->attachError($actionError);
}
}
/**
* Fires a report event with the actions report.
*/
private function sendReport () : void {
$event = new ReportEvent($this->actionToReport);
Event::dispatch($event);
}
}
diff --git a/app/Notifications/DockerHubNotification.php b/app/Notifications/DockerHubNotification.php
index 960db47..c14eb8e 100644
--- a/app/Notifications/DockerHubNotification.php
+++ b/app/Notifications/DockerHubNotification.php
@@ -1,91 +1,87 @@
<?php
namespace Nasqueron\Notifications\Notifications;
use Nasqueron\Notifications\Analyzers\DockerHub\BaseEvent;
use InvalidArgumentException;
/**
* A Docker Hub notification.
*
* As we always sort them to the 'docker' group, and the registry only fires
* one kind of event, this is pretty straightforward without any need for
* configuration files or analyser class.
*
* HOW TO IMPLEMENT PAYLOADS SORT PER REPOSITORY?
*
* If you want to extend this to sort Docker images through some rules, we
* suggest you add a feature request to Docker to include source repository
* for the image, then call the GitHubPayloadAnalyzer with this repo instead
* of implementing a new one. This will allows to avoid to maintain two sets
* of configuration, one for the GitHub repos, one for the Docker repos.
*
* Even without that, you can probably be safe with a class or a method to map
* GitHub and Docker names, either because they are the same, either because
* there is a prefix: e.g. nasqueron/arcanist and nasqueron/docker-arcanist.
*/
class DockerHubNotification extends Notification {
public function __construct (
string $project,
string $event,
\stdClass $payload
) {
// Straightforward properties
$this->service = "DockerHub";
$this->project = $project;
$this->type = $event;
$this->rawContent = $payload;
$this->group = "docker";
// Properties from the payload
$this->analyzeByEvent();
}
///
/// Analyze by event
///
/**
* Fills properties from event payload.
*/
public function analyzeByEvent () : void {
$analyzer = $this->getAnalyzer();
$this->rawContent = $analyzer->getPayload();
$this->text = $analyzer->getText();
$this->link = $analyzer->getLink();
}
/**
* Gets analyzer class name for the current event.
- *
- * @return string
*/
private function getAnalyzerClassName () : string {
return "Nasqueron\Notifications\Analyzers\DockerHub\\"
. ucfirst($this->type)
. "Event";
}
/**
* Gets analyzer for the current event.
- *
- * @return \Nasqueron\Notifications\Analyzers\DockerHub\BaseEvent
*/
private function getAnalyzer () : BaseEvent {
$class = $this->getAnalyzerClassName();
if (!class_exists($class)) {
throw new InvalidArgumentException(
"Event $this->type doesn't have a matching $class class."
);
}
return new $class($this->rawContent);
}
}
diff --git a/app/Notifications/GitHubNotification.php b/app/Notifications/GitHubNotification.php
index 6ffa0b5..55dfdf7 100644
--- a/app/Notifications/GitHubNotification.php
+++ b/app/Notifications/GitHubNotification.php
@@ -1,73 +1,69 @@
<?php
namespace Nasqueron\Notifications\Notifications;
use Nasqueron\Notifications\Analyzers\GitHub\GitHubPayloadAnalyzer;
class GitHubNotification extends Notification {
/**
* @var GitHubPayloadAnalyzer
*/
private $analyzer = null;
public function __construct (
string $project,
string $event,
\stdClass $payload
) {
// Straightforward properties
$this->service = "GitHub";
$this->project = $project;
$this->type = $event;
$this->rawContent = $payload;
// Analyzes and fills
$this->group = $this->getGroup();
$this->text = $this->getText();
$this->link = $this->getLink();
}
/**
* Gets analyzer
*/
private function getAnalyzer () : GitHubPayloadAnalyzer {
if ($this->analyzer === null) {
$this->analyzer = new GitHubPayloadAnalyzer(
$this->project,
$this->type,
$this->rawContent
);
}
return $this->analyzer;
}
/**
* Gets the target notificatrion group
*
* @return string the target group for the notification
*/
public function getGroup () : string {
return $this->getAnalyzer()->getGroup();
}
/**
* Gets the notification text.
* Intended to convey a short message (thing Twitter or IRC).
- *
- * @return string
*/
public function getText () : string {
return $this->getAnalyzer()->getDescription();
}
/**
* Gets the notification URL. Intended to be a widget or icon link.
- *
- * @return string
*/
public function getLink () : string {
return $this->getAnalyzer()->getLink();
}
}
diff --git a/app/Notifications/JenkinsNotification.php b/app/Notifications/JenkinsNotification.php
index aa01357..7f482d8 100644
--- a/app/Notifications/JenkinsNotification.php
+++ b/app/Notifications/JenkinsNotification.php
@@ -1,112 +1,104 @@
<?php
namespace Nasqueron\Notifications\Notifications;
use Nasqueron\Notifications\Analyzers\Jenkins\JenkinsPayloadAnalyzer;
/**
* A Jenkins notification.
*
* This handles the JSON payloads sent by the following plugin:
* https://wiki.jenkins-ci.org/display/JENKINS/Notification+Plugin
*/
class JenkinsNotification extends Notification {
/**
* @var \Nasqueron\Notifications\Analyzers\Jenkins\JenkinsPayloadAnalyzer
*/
private $analyzer = null;
/**
* Initializes a new instance of the JenkinsNotification class.
*
* @param string $project The project this message is for
* @param mixed $payload The message fired by Jenkins notification plugin
*/
public function __construct (string $project, mixed $payload) {
// Straightforward properties
$this->service = "Jenkins";
$this->project = $project;
$this->rawContent = $payload;
// Properties from the payload
$this->group = $this->getGroup();
$this->text = $this->getText();
$this->link = $payload->build->full_url;
$this->type = $this->getType();
}
/**
* Gets the notification type.
- *
- * @return string
*/
public function getType () : string {
$build = $this->rawContent->build;
$type = strtolower($build->phase);
if (property_exists($build, 'status')) {
$type .= '.';
$type .= $build->status;
}
return strtolower($type);
}
/**
* Gets the notification text. Intended to convey a short message
* (thing Twitter or IRC).
- *
- * @return string
*/
public function getText () : string {
$name = $this->rawContent->name;
$build = $this->rawContent->build;
$phase = strtolower($build->phase);
$text = "Jenkins job $name has been $phase";
if (property_exists($build, 'status')) {
$status = strtolower($build->status);
$text .= ": $status";
}
return $text;
}
/**
* Gets analyzer
- *
- * @return \Nasqueron\Notifications\Analyzers\Jenkins\JenkinsPayloadAnalyzer
*/
private function getAnalyzer () : JenkinsPayloadAnalyzer {
if ($this->analyzer === null) {
$this->analyzer = new JenkinsPayloadAnalyzer(
$this->project,
$this->rawContent
);
}
return $this->analyzer;
}
/**
* Gets the notification group.
- *
- * @return string
*/
public function getGroup () : string {
return $this->getAnalyzer()->getGroup();
}
/**
* Indicates if we should handle this payload to trigger a notification.
*
* @return bool if false, this payload is to be ignored for notifications
*/
public function shouldNotify () : bool {
return $this->getAnalyzer()->shouldNotify();
}
}
diff --git a/app/Notifications/PhabricatorNotification.php b/app/Notifications/PhabricatorNotification.php
index 697a5ce..2649dae 100644
--- a/app/Notifications/PhabricatorNotification.php
+++ b/app/Notifications/PhabricatorNotification.php
@@ -1,67 +1,63 @@
<?php
namespace Nasqueron\Notifications\Notifications;
use Nasqueron\Notifications\Analyzers\Phabricator\PhabricatorPayloadAnalyzer;
use Nasqueron\Notifications\Phabricator\PhabricatorStory;
class PhabricatorNotification extends Notification {
/**
* @var PhabricatorPayloadAnalyzer
*/
private $analyzer = null;
/**
* Initializes a new PhabricatorNotification instance
*
* @param string $project The project for this notification
* @param PhabricatorStory $payload The story to convert into a notification
*/
public function __construct (string $project, PhabricatorStory $payload) {
// Straightforward properties
$this->service = "Phabricator";
$this->project = $project;
$this->rawContent = $payload;
$this->text = $payload->text;
// Analyzes and fills
$this->type = $payload->getObjectType();
$this->group = $this->getGroup();
$this->link = $this->getLink();
}
/**
* Gets analyzer
- *
- * @return \Nasqueron\Notifications\Analyzers\Phabricator\PhabricatorPayloadAnalyzer
*/
private function getAnalyzer () : PhabricatorPayloadAnalyzer {
if ($this->analyzer === null) {
$this->analyzer = new PhabricatorPayloadAnalyzer(
$this->project,
$this->rawContent
);
}
return $this->analyzer;
}
/**
* Gets the target notificatrion group
*
* @return string the target group for the notification
*/
public function getGroup () : string {
return $this->getAnalyzer()->getGroup();
}
/**
* Gets the notification URL. Intended to be a widget or icon link.
- *
- * @return string
*/
public function getLink () : string {
return "";
}
}
diff --git a/app/Phabricator/PhabricatorAPI.php b/app/Phabricator/PhabricatorAPI.php
index 139352b..e48b107 100644
--- a/app/Phabricator/PhabricatorAPI.php
+++ b/app/Phabricator/PhabricatorAPI.php
@@ -1,164 +1,161 @@
<?php
namespace Nasqueron\Notifications\Phabricator;
use Nasqueron\Notifications\Contracts\APIClient;
use Nasqueron\Notifications\Facades\Services;
class PhabricatorAPI implements APIClient {
///
/// Private members
///
/**
* The Phabricator main URL
*
* @var string
*/
private $endPoint;
/**
* The token generated at /settings/panel/apitokens/ to query the API
*
* @var string
*/
private $apiToken;
///
/// Constructors
///
/**
* Initializes a new instance of the Phabricator API class
*
* @param string $endPoint The Phabricator main URL, without trailing slash
* @param string $apiToken The token generated at /settings/panel/apitokens/
*/
public function __construct (string $endPoint, string $apiToken) {
$this->endPoint = $endPoint;
$this->apiToken = $apiToken;
}
/**
* @throws \RuntimeException when the service isn't in credentials.json
*/
public static function forInstance ($instance) : PhabricatorAPI {
$service = Services::findServiceByProperty(
'Phabricator',
'instance',
$instance
);
if ($service === null) {
throw new \RuntimeException(
"No credentials for Phabricator instance $instance."
);
}
return new self($service->instance, $service->secret);
}
/**
* @throws \RuntimeException when the service isn't in credentials.json
*/
- public static function forProject ($project) {
+ public static function forProject ($project) : PhabricatorAPI {
$service = Services::findServiceByDoor('Phabricator', $project);
if ($service === null) {
throw new \RuntimeException(
"No credentials for Phabricator project $project."
);
}
return new self($service->instance, $service->secret);
}
///
/// APIClient implementation
///
/**
* Sets API end point
*
* @param string $url The API end point URL
*/
- public function setEndPoint (string $url) {
+public function setEndPoint (string $url) : void {
$this->endPoint = $url;
}
/**
* Calls a Conduit API method
*
* @param string $method The method to call (e.g. repository.create)
* @param array $arguments The arguments to use
*
* @return mixed The API result
*/
- public function call (string $method, array $arguments = []) {
+ public function call (string $method, array $arguments = []) : mixed {
$url = $this->endPoint . '/api/' . $method;
$arguments['api.token'] = $this->apiToken;
$reply = json_decode(static::post($url, $arguments));
if ($reply->error_code !== null) {
throw new PhabricatorAPIException(
$reply->error_code,
$reply->error_info
);
}
return $reply->result;
}
///
/// Helper methods
///
/**
* Gets the first result of an API reply.
- *
- * @param iterable $reply
- * @return mixed
*/
- public static function getFirstResult (iterable $reply) {
+ public static function getFirstResult (iterable $reply) : mixed {
if (is_object($reply) && property_exists($reply, 'data')) {
$reply = $reply->data;
}
foreach ($reply as $value) {
return $value;
}
}
///
/// CURL session
///
- protected static function getPostFields ($arguments) {
+ protected static function getPostFields ($arguments) : string {
$items = [];
foreach ($arguments as $key => $value) {
$items[] = urlencode($key) . '=' . urlencode($value);
}
return implode('&', $items);
}
- protected static function post ($url, $arguments) {
+ protected static function post ($url, $arguments) : bool|string {
$options = [
CURLOPT_URL => $url,
CURLOPT_HEADER => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => 1,
CURLOPT_POSTFIELDS => static::getPostFields($arguments),
];
$ch = curl_init();
curl_setopt_array($ch, $options);
$result = curl_exec($ch);
curl_close($ch);
if ($result === false) {
throw new \RuntimeException(
"Can't reach Phabricator API endpoint: $url"
);
}
return $result;
}
}
diff --git a/app/Phabricator/PhabricatorAPIFactory.php b/app/Phabricator/PhabricatorAPIFactory.php
index 8127f21..3fc284e 100644
--- a/app/Phabricator/PhabricatorAPIFactory.php
+++ b/app/Phabricator/PhabricatorAPIFactory.php
@@ -1,28 +1,28 @@
<?php
namespace Nasqueron\Notifications\Phabricator;
use Nasqueron\Notifications\Contracts\APIFactory;
class PhabricatorAPIFactory implements APIFactory {
/**
* Gets an instance of the Phabricator API client class.
*
* @param string $instance The Phabricator instance
* @return \Nasqueron\Notifications\Phabricator\PhabricatorAPI
*/
- public function get (string $instance) {
+ public function get (string $instance) : PhabricatorAPI {
return PhabricatorAPI::forInstance($instance);
}
/**
* Gets an instance of the Phabricator API client class for a project.
*
* @param string $project The Phabricator project name
* @return PhabricatorAPI
*/
- public function getForProject (string $project) {
+ public function getForProject (string $project) : PhabricatorAPI {
return PhabricatorAPI::forProject($project);
}
}
diff --git a/app/Phabricator/PhabricatorStory.php b/app/Phabricator/PhabricatorStory.php
index 2bf1837..ce0540f 100644
--- a/app/Phabricator/PhabricatorStory.php
+++ b/app/Phabricator/PhabricatorStory.php
@@ -1,331 +1,331 @@
<?php
namespace Nasqueron\Notifications\Phabricator;
use InvalidArgumentException;
class PhabricatorStory {
///
/// Properties
///
/**
* The Phabricator instance name
*
* @var string
*/
public $instanceName;
/**
* The unique identifier Phabricator assigns to each story
*
* @var int
*/
public $id;
/**
* Type of story (e.g. PhabricatorApplicationTransactionFeedStory)
*
* @var string
*/
public $type;
/**
* @var array|null
*/
public $data;
/**
* The person logged to Phabricator and triggering the event
*
* @var string
*/
public $authorPHID;
/**
* A short English textual description of the event
*
* @var string
*/
public $text;
/**
* The unixtime the event occured
*
* @var int
*/
public $epoch;
/**
* The projects attached to this story.
*
* When there is no project, [].
* When not yet queried, null.
*
* @var string[]|null
*/
private $projects = null;
///
/// Constructors
///
/**
* Initializes a new instance of the Phabricator story class
*
* @param string $instanceName The Phabricator instance name
*/
public function __construct (string $instanceName) {
$this->instanceName = $instanceName;
}
/**
* Initializes a new instance of PhabricatorStory from an iterable.
*
* This is intended to parse the feed.hooks payloads.
*
* @param string $instanceName The Phabricator instance name
* @param iterable $payload The data submitted by Phabricator
* @return PhabricatorStory
*/
public static function loadFromIterable (
string $instanceName, iterable $payload
- ) {
+ ) : PhabricatorStory {
$instance = new self($instanceName);
foreach ($payload as $key => $value) {
$property = self::mapPhabricatorFeedKey($key);
$instance->$property = $value;
}
return $instance;
}
public static function loadFromJson (
$instanceName,
$payload
- ) {
+ ) : PhabricatorStory {
$array = json_decode($payload, true);
if (!is_array($array)) {
throw new InvalidArgumentException(<<<MSG
Payload should be deserializable as an array.
MSG
);
}
return self::loadFromIterable($instanceName, $array);
}
///
/// Helper methods
///
private function hasVoidObjectType () : bool {
return $this->data === null || !isset($this->data['objectPHID']);
}
/**
* Gets object type (e.g. TASK for PHID-TASK-l34fw5wievp6n6rnvpuk)
*
* @return string The object type, as a 4 letters string (e.g. 'TASK')
*/
- public function getObjectType () {
+ public function getObjectType () : string {
if ($this->hasVoidObjectType()) {
return 'VOID';
}
return substr($this->data['objectPHID'], 5, 4);
}
/**
* Gets the identifier of the projets related to this task
*
* return string[] The list of project PHIDs
*/
- public function getProjectsPHIDs () {
+ public function getProjectsPHIDs () : array {
if (!array_key_exists('objectPHID', $this->data)) {
return [];
}
$objectPHID = $this->data['objectPHID'];
$objectType = $this->getObjectType();
switch ($objectType) {
case 'DREV':
return $this->getItemProjectsPHIDs(
'repository.query',
$this->getRepositoryPHID('differential.query')
);
case 'TASK':
return $this->getItemProjectsPHIDs(
'maniphest.query',
$objectPHID
);
case 'CMIT':
return $this->getItemProjectsPHIDs(
'repository.query',
$this->getRepositoryPHID('diffusion.querycommits')
);
case 'PSTE':
return $this->getItemProjectsPHIDsThroughApplicationSearch(
'paste.search',
$objectPHID
);
default:
return [];
}
}
/**
* Gets the PHID of a repository
*
* @param string $method The API method to call (e.g. differential.query)
* @return string The repository PHID or "" if not found
*/
- public function getRepositoryPHID (string $method) {
+ public function getRepositoryPHID (string $method) : string {
$objectPHID = $this->data['objectPHID'];
$api = PhabricatorAPI::forProject($this->instanceName);
$reply = $api->call(
$method,
[ 'phids[0]' => $objectPHID ]
);
if ($reply === []) {
return "";
}
$apiResult = PhabricatorAPI::getFirstResult($reply);
if ($apiResult === null) {
// Repository information can't be fetched (T1136).
// This occurs when the bot account used to fetch information
// doesn't have access to the repository, for example if it's
// in a private space or restricted to a group it doesn't belong to.
return "";
}
return $apiResult->repositoryPHID;
}
/**
* Gets the projects for a specific item
*
* @param string $method The API method to call (e.g. differential.query)
* @param string $objectPHID The object PHID to pass as method parameter
* @return string[] The list of project PHIDs
*/
- public function getItemProjectsPHIDs (string $method, string $objectPHID) {
+ public function getItemProjectsPHIDs (string $method, string $objectPHID) : array {
if (!$objectPHID) {
return [];
}
$api = PhabricatorAPI::forProject($this->instanceName);
$reply = $api->call(
$method,
[ 'phids[0]' => $objectPHID ]
);
if ($reply === []) {
return [];
}
return PhabricatorAPI::getFirstResult($reply)->projectPHIDs;
}
/**
* Gets the project for a specific item, using the new ApplicationSearch.
*
* This is a transitional method: when every Phabricator will have been
* migrated from info (generation 1) or query (generation 2) to search
* (generation 3), we'll rename it to getItemProjectsPHIDs and overwrite it.
*/
protected function getItemProjectsPHIDsThroughApplicationSearch (
$method,
$objectPHID
- ) {
+ ) : array {
if (!$objectPHID) {
return [];
}
$api = PhabricatorAPI::forProject($this->instanceName);
$reply = $api->call(
$method,
[
'constraints[phids][0]' => $objectPHID,
'attachments[projects]' => 1
]
);
$apiResult = PhabricatorAPI::getFirstResult($reply);
if ($apiResult === null) {
// Object information (e.g. a paste) can't be fetched (T1138).
// This occurs when the bot account used to fetch information
// doesn't have access to the object, for example if it's
// in a private space or restricted to a group it doesn't belong to.
return [];
}
return $apiResult->attachments->projects->projectPHIDs;
}
/**
* Gets the list of the projects associated to the story
*
* @return string[] The list of project PHIDs
*/
- public function getProjects () {
+ public function getProjects () : array {
if ($this->projects === null) {
$this->attachProjects();
}
return $this->projects;
}
/**
* Queries the list of the projects associated to the story
* and attached it to the projects property.
*/
- public function attachProjects () {
+ public function attachProjects () : void {
$this->projects = [];
$PHIDs = $this->getProjectsPHIDs();
if (count($PHIDs) == 0) {
// No project is attached to the story's object
return;
}
$map = ProjectsMap::load($this->instanceName);
foreach ($PHIDs as $PHID) {
$this->projects[] = $map->getProjectName($PHID);
}
}
///
/// Static helper methods
///
/**
* Maps a field of the API reply to a property of the PhabricatorStory class
*
* @param string $key The field of the API reply
* @return string The property's name
*/
- public static function mapPhabricatorFeedKey (string $key) {
+ public static function mapPhabricatorFeedKey (string $key) : string {
if ($key == "storyID") {
return "id";
}
if (str_starts_with($key, "story") && strlen($key) > 5) {
return lcfirst(substr($key, 5));
}
return $key;
}
}
diff --git a/app/Phabricator/ProjectsMap.php b/app/Phabricator/ProjectsMap.php
index 2af59d0..81acede 100644
--- a/app/Phabricator/ProjectsMap.php
+++ b/app/Phabricator/ProjectsMap.php
@@ -1,285 +1,277 @@
<?php
namespace Nasqueron\Notifications\Phabricator;
use Nasqueron\Notifications\Contracts\APIClient as APIClient;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
class ProjectsMap implements \IteratorAggregate, \ArrayAccess {
///
/// Private properties and constants
///
/**
* The maximum number of projects to fetch
*/
const LIMIT = 1000;
/**
* The projects as an array with phid as keys, project names as $value
*
* @var string[]
*/
private $map = [];
/**
* The Phabricator instance name for this projects map
*
* @var string
*/
private $instanceName;
/**
*
* @var \Nasqueron\Notifications\Contracts\APIClient
*/
private $apiClient;
/**
* The source of the map
*
* @var string
*/
private $source = 'unloaded';
///
/// Constructor
///
/**
* Initializes a new instance of ProjectsMap.
*
* @param string $instanceName The Phabricator instance name
*/
public function __construct (string $instanceName) {
$this->instanceName = $instanceName;
}
///
/// IteratorAggregate interface implementation
///
/**
* Gets iterator.
- *
- * @return \Traversable
*/
- public function getIterator () {
+ public function getIterator () : \Traversable {
return new \ArrayIterator($this->map);
}
///
/// ArrayAccess interface implementation
///
/**
* Determines whether an offset exists.
*
* @param mixed $offset The offset
* @return bool
*/
- public function offsetExists (mixed $offset) {
+ public function offsetExists (mixed $offset) : bool {
return array_key_exists($offset, $this->map);
}
/**
* Gets the value at the specified offset.
*
* @param mixed $offset The offset.
* @return mixed The value
*/
- public function offsetGet (mixed $offset) {
+ public function offsetGet (mixed $offset) : mixed {
return $this->map[$offset];
}
/**
* Assigns a value to the specified offset.
*
* @param mixed $offset The offset
* @param mixed $value The value to assign
*/
public function offsetSet (mixed $offset, mixed $value) {
$this->map[$offset] = $value;
}
/**
* Unsets a value at the specified offset.
*
* @param mixed $offset The offset where to remove the value
*/
public function offsetUnset (mixed $offset) {
unset($this->map[$offset]);
}
///
/// Static constructors
///
/**
* Gets a new ProjectsMap instance from cache or API when not cached.
*
* @param string $phabricatorInstanceName The Phabricator instance name
* @return ProjectsMap
*/
- public static function load (string $phabricatorInstanceName) {
+ public static function load (string $phabricatorInstanceName) : ProjectsMap {
$instance = new self($phabricatorInstanceName);
if ($instance->isCached()) {
$instance->loadFromCache();
} else {
$instance->fetchFromAPI();
}
return $instance;
}
/**
* Gets a new ProjectsMap instance and queries Phabricator API to fill it.
*/
public static function fetch (
string $phabricatorInstanceName,
?APIClient $apiClient = null
- ) {
+ ) : ProjectsMap {
$instance = new self($phabricatorInstanceName);
$instance->setAPIClient($apiClient);
$instance->fetchFromAPI();
return $instance;
}
///
/// API
///
- /**
- * @return \Nasqueron\Notifications\Contracts\APIClient
- */
- public function getAPIClient () {
+ public function getAPIClient () : APIClient {
if ($this->apiClient === null) {
$factory = App::make('phabricator-api');
$this->apiClient = $factory->getForProject($this->instanceName);
}
return $this->apiClient;
}
- /**
- * @param \Nasqueron\Notifications\Contracts\APIClient|null $apiClient
- */
- public function setAPIClient (?APIClient $apiClient = null) {
+ public function setAPIClient (?APIClient $apiClient = null) : void {
$this->apiClient = $apiClient;
}
/**
* Fetches the projects' map from the Phabricator API.
*
* @throws \Exception when API reply is empty or invalid.
*/
private function fetchFromAPI () {
$reply = $this->getAPIClient()->call(
'project.query',
[ 'limit' => self::LIMIT ]
);
if (!$reply) {
throw new \Exception(<<<MSG
Empty reply calling project.query at $this->instanceName Conduit API.
MSG
);
}
if (!property_exists($reply, 'data')) {
throw new \Exception(<<<MSG
Invalid reply calling project.query at $this->instanceName Conduit API.
MSG
);
}
foreach ($reply->data as $phid => $projectInfo) {
$this->offsetSet($phid, $projectInfo->name);
}
$this->source = 'api';
}
///
/// Cache
///
/**
* Gets cache key.
*
* @return string The cache key for the current projects map
*/
- private function getCacheKey () {
+ private function getCacheKey () : string {
return class_basename(get_class($this))
. '-'
. md5($this->instanceName);
}
/**
* Determines if the instance is cached
*
* @return bool true if cached; otherwise, false.
*/
- public function isCached () {
+ public function isCached () : bool {
return Cache::has($this->getCacheKey());
}
/**
* Saves data to cache
*/
public function saveToCache () {
Cache::forever($this->getCacheKey(), $this->map);
}
/**
* Loads data from cache
*
* Populates 'map' and 'source' properties
*/
public function loadFromCache () {
$cachedMap = Cache::get($this->getCacheKey());
if ($cachedMap !== null) {
$this->map = $cachedMap;
$this->source = 'cache';
}
}
///
/// Output
///
/**
* Gets project name, refreshing the cache if needed.
*
* @param string $projectPHID the PHID of the project to query the name
* @return string The name of the poject, or an empty string if not found
*/
- public function getProjectName (string $projectPHID) {
+ public function getProjectName (string $projectPHID) : string {
if ($this->offsetExists($projectPHID)) {
return $this->offsetGet($projectPHID);
}
if ($this->source !== 'api') {
$this->fetchFromAPI();
return $this->getProjectName($projectPHID);
}
return "";
}
/**
* Returns the projects map as an array.
*
- * @return array An array, each row containing ['PHID', 'project name']
+ * @return array[] An array, each row containing ['PHID', 'project name']
*/
- public function toArray () {
+ public function toArray () : array {
$array = [];
foreach ($this->map as $phid => $projectName) {
$array[] = [$phid, $projectName];
}
return $array;
}
}
diff --git a/app/Phabricator/ProjectsMapFactory.php b/app/Phabricator/ProjectsMapFactory.php
index e19a9f6..75c2c8a 100644
--- a/app/Phabricator/ProjectsMapFactory.php
+++ b/app/Phabricator/ProjectsMapFactory.php
@@ -1,27 +1,27 @@
<?php
namespace Nasqueron\Notifications\Phabricator;
class ProjectsMapFactory {
/**
* Loads projects map from cache or fetches it from API if not cached.
*
* @param string $instanceName The Phabricator instance name
* @return ProjectsMap
*/
- public function load (string $instanceName) {
+ public function load (string $instanceName) : ProjectsMap {
return ProjectsMap::load($instanceName);
}
/**
* Fetches projects map from API.
*
* @param string $instanceName The Phabricator instance name
* @return ProjectsMap
*/
- public function fetch (string $instanceName) {
+ public function fetch (string $instanceName) : ProjectsMap {
return ProjectsMap::fetch($instanceName);
}
}
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index 4f9bb51..24bd0d1 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -1,26 +1,22 @@
<?php
namespace Nasqueron\Notifications\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider {
/**
* Bootstrap any application services.
- *
- * @return void
*/
- public function boot() {
+ public function boot() : void {
//
}
/**
* Register any application services.
- *
- * @return void
*/
- public function register() {
+ public function register() : void {
//
}
}
diff --git a/app/Providers/BrokerServiceProvider.php b/app/Providers/BrokerServiceProvider.php
index 6c719a6..c38e65c 100644
--- a/app/Providers/BrokerServiceProvider.php
+++ b/app/Providers/BrokerServiceProvider.php
@@ -1,34 +1,30 @@
<?php
namespace Nasqueron\Notifications\Providers;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;
use Keruald\Broker\BrokerFactory;
class BrokerServiceProvider extends ServiceProvider {
/**
* Bootstraps the application services.
- *
- * @return void
*/
- public function boot() {
+ public function boot() : void {
}
/**
* Registers the application services.
- *
- * @return void
*/
- public function register() {
+ public function register() : void {
$this->app->singleton('broker', function (Application $app) {
$config = $app->make('config');
$driver = $config->get('broker.driver');
$params = $config->get('broker.connections.' . $driver);
return BrokerFactory::make($params);
});
}
}
diff --git a/app/Providers/DockerHubServiceProvider.php b/app/Providers/DockerHubServiceProvider.php
index 7cb52f4..b404134 100644
--- a/app/Providers/DockerHubServiceProvider.php
+++ b/app/Providers/DockerHubServiceProvider.php
@@ -1,50 +1,46 @@
<?php
namespace Nasqueron\Notifications\Providers;
use Keruald\DockerHub\Build\TriggerBuildFactory;
use GuzzleHttp\Client;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;
class DockerHubServiceProvider extends ServiceProvider {
/**
* Bootstraps the application services.
- *
- * @return void
*/
- public function boot() {
+ public function boot() : void {
}
/**
* Gets the tokens to trigger build for the Docker Hub images.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return array
*/
- public static function getTokens (Application $app) {
+ public static function getTokens (Application $app) : array {
$file = $app->make('config')->get('services.dockerhub.tokens');
$fs = $app->make('filesystem')->disk('local');
if ($fs->exists($file)) {
$content = $fs->get($file);
return json_decode($content, true);
}
return [];
}
/**
* Registers the application services.
- *
- * @return void
*/
- public function register() {
+ public function register() : void {
$this->app->singleton('dockerhub', function (Application $app) {
$tokens = DockerHubServiceProvider::getTokens($app);
return new TriggerBuildFactory(new Client, $tokens);
});
}
}
diff --git a/app/Providers/MailgunServiceProvider.php b/app/Providers/MailgunServiceProvider.php
index f275ea1..887ca52 100644
--- a/app/Providers/MailgunServiceProvider.php
+++ b/app/Providers/MailgunServiceProvider.php
@@ -1,33 +1,29 @@
<?php
namespace Nasqueron\Notifications\Providers;
use Keruald\Mailgun\MailgunMessageFactory;
use GuzzleHttp\Client;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;
class MailgunServiceProvider extends ServiceProvider {
/**
* Bootstraps the application services.
- *
- * @return void
*/
- public function boot() {
+ public function boot() : void {
}
/**
* Registers the application services.
- *
- * @return void
*/
- public function register() {
+ public function register() : void {
$this->app->singleton('mailgun', function (Application $app) {
$config = $app->make('config');
$key = $config->get('services.mailgun.secret');
return new MailgunMessageFactory(new Client, $key);
});
}
}
diff --git a/app/Providers/PhabricatorAPIServiceProvider.php b/app/Providers/PhabricatorAPIServiceProvider.php
index 13678ea..16cb87e 100644
--- a/app/Providers/PhabricatorAPIServiceProvider.php
+++ b/app/Providers/PhabricatorAPIServiceProvider.php
@@ -1,29 +1,25 @@
<?php
namespace Nasqueron\Notifications\Providers;
use Illuminate\Support\ServiceProvider;
use Nasqueron\Notifications\Phabricator\PhabricatorAPIFactory;
class PhabricatorAPIServiceProvider extends ServiceProvider {
/**
* Bootstraps the application services.
- *
- * @return void
*/
- public function boot() {
+ public function boot() : void {
}
/**
* Registers the application services.
- *
- * @return void
*/
- public function register() {
+ public function register() : void {
$this->app->singleton('phabricator-api', static function () {
return new PhabricatorAPIFactory;
});
}
}
diff --git a/app/Providers/PhabricatorProjectsMapServiceProvider.php b/app/Providers/PhabricatorProjectsMapServiceProvider.php
index 9610d00..785fc67 100644
--- a/app/Providers/PhabricatorProjectsMapServiceProvider.php
+++ b/app/Providers/PhabricatorProjectsMapServiceProvider.php
@@ -1,29 +1,25 @@
<?php
namespace Nasqueron\Notifications\Providers;
use Illuminate\Support\ServiceProvider;
use Nasqueron\Notifications\Phabricator\ProjectsMapFactory;
class PhabricatorProjectsMapServiceProvider extends ServiceProvider {
/**
* Bootstraps the application services.
- *
- * @return void
*/
- public function boot() {
+ public function boot() : void {
}
/**
* Registers the application services.
- *
- * @return void
*/
- public function register() {
+ public function register() : void {
$this->app->singleton('phabricator-projectsmap', static function () {
return new ProjectsMapFactory;
});
}
}
diff --git a/app/Providers/ReportServiceProvider.php b/app/Providers/ReportServiceProvider.php
index 47d8a48..4c2ad93 100644
--- a/app/Providers/ReportServiceProvider.php
+++ b/app/Providers/ReportServiceProvider.php
@@ -1,40 +1,38 @@
<?php
namespace Nasqueron\Notifications\Providers;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;
use Nasqueron\Notifications\Actions\ActionsReport;
use Nasqueron\Notifications\Events\ReportEvent;
class ReportServiceProvider extends ServiceProvider {
/**
* Registers the application services.
- *
- * @return void
*/
- public function register() {
+ public function register() : void {
$this->app->singleton('report', function (Application $app) {
$report = new ActionsReport();
static::listenToActionsForReport(
$report,
$app->make('events')
);
return $report;
});
}
public static function listenToActionsForReport (
ActionsReport $report,
Dispatcher $events
- ) {
+ ) : void {
$events->listen(
'Nasqueron\Notifications\Events\ReportEvent',
static function (ReportEvent $event) use ($report) {
$report->addAction($event->action);
}
);
}
}
diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php
index 04507b6..ebff488 100644
--- a/app/Providers/RouteServiceProvider.php
+++ b/app/Providers/RouteServiceProvider.php
@@ -1,41 +1,39 @@
<?php
namespace Nasqueron\Notifications\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Routing\Router;
class RouteServiceProvider extends ServiceProvider {
/**
* This namespace is applied to the controller routes in your routes file.
*
* In addition, it is set as the URL generator's root namespace.
*
* @var string
*/
protected $namespace = 'Nasqueron\Notifications\Http\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
- *
- * @return void
*/
- public function boot() {
+ public function boot() : void {
//
parent::boot();
}
/**
* Define the routes for the application.
*
* @param \Illuminate\Routing\Router $router
* @return void
*/
- public function map(Router $router) {
+ public function map(Router $router) : void {
$router->group(['namespace' => $this->namespace], static function ($router) {
require app_path('Http/routes.php');
});
}
}
diff --git a/app/Providers/SentryServiceProvider.php b/app/Providers/SentryServiceProvider.php
index cd7d2e2..a67ef28 100644
--- a/app/Providers/SentryServiceProvider.php
+++ b/app/Providers/SentryServiceProvider.php
@@ -1,30 +1,26 @@
<?php
namespace Nasqueron\Notifications\Providers;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;
class SentryServiceProvider extends ServiceProvider {
/**
* Bootstraps the application services.
- *
- * @return void
*/
- public function boot() {
+ public function boot() : void {
}
/**
* Registers the application services.
- *
- * @return void
*/
- public function register() {
+ public function register() : void {
$this->app->singleton('raven', function (Application $app) {
$config = $app->make('config');
$dsn = $config->get('services.sentry.dsn');
return new \Raven_Client($dsn);
});
}
}
diff --git a/app/Providers/ServicesServiceProvider.php b/app/Providers/ServicesServiceProvider.php
index 00c911d..85c8e38 100644
--- a/app/Providers/ServicesServiceProvider.php
+++ b/app/Providers/ServicesServiceProvider.php
@@ -1,26 +1,24 @@
<?php
namespace Nasqueron\Notifications\Providers;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;
use Nasqueron\Notifications\Config\Services\Services;
class ServicesServiceProvider extends ServiceProvider {
/**
* Registers the application services.
- *
- * @return void
*/
- public function register() {
+ public function register() : void {
$this->app->singleton('services', function (Application $app) {
$path = config('services.gate.credentials');
if (strlen($path) > 0 && $app->make('filesystem')->has($path)) {
return Services::loadFromJson($path);
}
return new Services;
});
}
}
diff --git a/tests/Phabricator/ProjectsMapTest.php b/tests/Phabricator/ProjectsMapTest.php
index 6cdb170..972f63f 100644
--- a/tests/Phabricator/ProjectsMapTest.php
+++ b/tests/Phabricator/ProjectsMapTest.php
@@ -1,198 +1,198 @@
<?php
namespace Nasqueron\Notifications\Tests\Phabricator;
use Nasqueron\Notifications\Contracts\APIClient;
use Nasqueron\Notifications\Phabricator\ProjectsMap;
use Nasqueron\Notifications\Tests\TestCase;
use ErrorException;
use Exception;
class ProjectsMapTest extends TestCase {
/**
* @var \Nasqueron\Notifications\Phabricator\ProjectsMap
*/
private $map;
public function setUp (): void {
parent::setUp();
//
// We mock the API, so an imaginary instance of Phabricator
// will return 3 results: Accounts, Agora & architecture.
//
// Agora has the key "PHID-PROJ-cztcgpvqr6smnnekotq7".
//
$this->mockPhabricatorAPIForProjectsMap();
$this->map = ProjectsMap::fetch("http://phabricator.acme.tld");
}
public function testIteratorIsTraversable () {
$this->assertInstanceOf(
"Traversable",
$this->map->getIterator()
);
}
///
/// Tests for ArrayAccess
///
public function testOffsetExistsWhenItDoes () {
$this->assertTrue(
$this->map->offsetExists("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
public function testOffsetExistsWhenItDoesNot () {
$this->assertFalse(
$this->map->offsetExists("non-existing-key")
);
}
public function testOffsetGetWhenItDoesExist () {
$this->assertSame(
"Agora",
$this->map->offsetGet("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
public function testOffsetGetWhenItDoesNotExist () {
$this->expectException(ErrorException::class);
$this->map->offsetGet("non-existing-key");
}
/**
* @covers Nasqueron\Notifications\Phabricator\ProjectsMap::offsetSet
*/
public function testOffsetSet () {
$this->map->offsetSet("newkey", "quux");
$this->assertSame("quux", $this->map->offsetGet("newkey"));
}
/**
* @covers Nasqueron\Notifications\Phabricator\ProjectsMap::offsetUnset
*/
public function testOffsetUnset () {
unset($this->map["PHID-PROJ-cztcgpvqr6smnnekotq7"]);
$this->assertFalse(
$this->map->offsetExists("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
///
/// Tests for cache
///
public function testCache () {
$this->assertFalse($this->map->isCached());
$this->map->saveToCache();
$this->assertTrue($this->map->isCached());
}
public function testLoadFromCache () {
$this->map->saveToCache();
$map = new ProjectsMap("http://phabricator.acme.tld");
$map->loadFromCache();
$this->assertTrue(
$map->offsetExists("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
public function testLoadWhenInCache () {
$this->map->saveToCache();
$map = ProjectsMap::load("http://phabricator.acme.tld");
$this->assertTrue(
$map->offsetExists("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
///
/// Tests for helper methods
///
public function testGetProjectName () {
$this->assertSame(
"Agora",
$this->map->getProjectName("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
public function testGetProjectNameForNewInstance () {
$map = new ProjectsMap("http://phabricator.acme.tld");
$this->assertSame(
"Agora",
$map->getProjectName("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
public function testGetProjectNameWhenItDoesNotExist () {
$this->assertSame(
"",
$this->map->getProjectName("non-existing-key")
);
}
public function testToArrayProducesArray () {
$array = $this->map->toArray();
$this->assertTrue(
is_array($array),
"Test if toArray return an array"
);
}
public function testThatArrayCount () {
$array = $this->map->toArray();
$this->assertSame(3, count($array));
}
public function testThatArrayContainsExpectedData () {
$this->assertSame(
[
["PHID-PROJ-6dg6ogx5pjmk24ur4tp4", "Accounts"],
["PHID-PROJ-cztcgpvqr6smnnekotq7", "Agora"],
["PHID-PROJ-3iew3cqf3htpazfyzb5a", "architecture"]
],
$this->map->toArray()
);
}
///
/// Tests API
///
private function mockPhabricatorAPIWithReply ($reply) : APIClient {
return (new class($reply) implements APIClient {
private $reply;
public function __construct ($reply) {
$this->reply = $reply;
}
public function setEndPoint ($url) : void { }
- public function call ($method, $arguments = []) {
+ public function call ($method, $arguments = []) : mixed {
return $this->reply;
}
});
}
public function testFetchFromAPIWithoutReply () {
$this->expectException(Exception::class);
$mock = $this->mockPhabricatorAPIWithReply(false);
ProjectsMap::fetch("http://phabricator.acme.tld", $mock);
}
public function testFetchFromAPIInvalidReply () {
$this->expectException(Exception::class);
$mock = $this->mockPhabricatorAPIWithReply(new \stdClass);
ProjectsMap::fetch("http://phabricator.acme.tld", $mock);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Feb 28, 20:36 (21 h, 33 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2447800
Default Alt Text
(138 KB)
Attached To
Mode
rNOTIF Notifications center
Attached
Detach File
Event Timeline
Log In to Comment