Page MenuHomeDevCentral

No OneTemporary

diff --git a/app/Analyzers/DockerHub/BaseEvent.php b/app/Analyzers/DockerHub/BaseEvent.php
index 95ae983..106d021 100644
--- a/app/Analyzers/DockerHub/BaseEvent.php
+++ b/app/Analyzers/DockerHub/BaseEvent.php
@@ -1,48 +1,48 @@
<?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 ($payload) {
+ public function __construct (\stdClass $payload) {
$this->payload = $payload;
}
///
/// Public methods
///
/**
* Gets notification payload.
*
* This method allows analyzer to edit the payload.
*/
public function getPayload () {
return $this->payload;
}
/**
* Gets notification text for this event.
*
* @return string
*/
abstract public function getText();
/**
* Gets notification link related to this event.
*
* @return string
*/
abstract public function getLink();
}
diff --git a/app/Analyzers/DockerHub/BuildFailureEvent.php b/app/Analyzers/DockerHub/BuildFailureEvent.php
index acd5c60..35ec1c8 100644
--- a/app/Analyzers/DockerHub/BuildFailureEvent.php
+++ b/app/Analyzers/DockerHub/BuildFailureEvent.php
@@ -1,67 +1,67 @@
<?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 ($payload) {
+ public function __construct (\stdClass $payload) {
parent::__construct($payload);
$this->payload = $this->getMailGunPayload();
}
/**
* Gets a MailGun message.
*
* @return \stdClass
*/
private function getMailGunPayload () {
return Mailgun::fetchMessageFromPayload($this->payload);
}
/**
* @return string
*/
private function getMailBody () {
$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) {
preg_match($regex, $this->getMailBody(), $matches);
return $matches[1];
}
/**
* Gets text from payload.
*
* @return string
*/
public function getText() {
$repo = $this->extractFromBody("@\"(.*?\/.*?)\"@");
return "Image build by Docker Hub registry failure for $repo";
}
/**
* Gets link from payload.
*
* @return string
*/
public function getLink() {
return $this->extractFromBody("@(https\:\/\/hub.docker.com\/r.*)@");
}
}
diff --git a/app/Analyzers/GitHub/Events/RepositoryEvent.php b/app/Analyzers/GitHub/Events/RepositoryEvent.php
index 1e51521..ffa9335 100644
--- a/app/Analyzers/GitHub/Events/RepositoryEvent.php
+++ b/app/Analyzers/GitHub/Events/RepositoryEvent.php
@@ -1,66 +1,66 @@
<?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 ($action) {
+ protected static function isValidAction (string $action) {
$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/WithCommit.php b/app/Analyzers/GitHub/Events/WithCommit.php
index 8d8463c..87fc4aa 100644
--- a/app/Analyzers/GitHub/Events/WithCommit.php
+++ b/app/Analyzers/GitHub/Events/WithCommit.php
@@ -1,64 +1,63 @@
<?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 () {
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 ($message) {
+ public static function getCommitTitle (string $message) {
// 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 () {
$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/Config/Services/Services.php b/app/Config/Services/Services.php
index 1e06852..22b5928 100644
--- a/app/Config/Services/Services.php
+++ b/app/Config/Services/Services.php
@@ -1,107 +1,107 @@
<?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 () {
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,
- $value
+ mixed $value
) : ?Service {
foreach ($this->services as $service) {
if ($service->gate === $gate && $service->$property === $value) {
return $service;
}
}
return null;
}
}
diff --git a/app/Events/DockerHubPayloadEvent.php b/app/Events/DockerHubPayloadEvent.php
index e5a2581..4372c91 100644
--- a/app/Events/DockerHubPayloadEvent.php
+++ b/app/Events/DockerHubPayloadEvent.php
@@ -1,52 +1,52 @@
<?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 string $door
* @param \stdClass $payload
*/
- public function __construct($door, $payload) {
+ public function __construct(string $door, \stdClass $payload) {
$this->door = $door;
$this->payload = $payload;
$this->event = $this->getEvent();
}
}
diff --git a/app/Events/GitHubPayloadEvent.php b/app/Events/GitHubPayloadEvent.php
index f45faac..ea47b75 100644
--- a/app/Events/GitHubPayloadEvent.php
+++ b/app/Events/GitHubPayloadEvent.php
@@ -1,40 +1,40 @@
<?php
namespace Nasqueron\Notifications\Events;
use Illuminate\Queue\SerializesModels;
class GitHubPayloadEvent extends Event {
use SerializesModels;
/**
* The gate door which receives the request
* @var string
*/
public $door;
/**
* The GitHub event triggering this request
* @var string
*/
public $event;
/**
* The request content, as a structured data
* @var \stdClass
*/
public $payload;
/**
* Creates a new event instance.
*
- * @param string $door
- * @param string $event
+ * @param string $door
+ * @param string $event
* @param \stdClass $payload
*/
- public function __construct($door, $event, $payload) {
+ public function __construct(string $door, string $event, \stdClass $payload) {
$this->door = $door;
$this->event = $event;
$this->payload = $payload;
}
}
diff --git a/app/Events/JenkinsPayloadEvent.php b/app/Events/JenkinsPayloadEvent.php
index 01cae98..14f4070 100644
--- a/app/Events/JenkinsPayloadEvent.php
+++ b/app/Events/JenkinsPayloadEvent.php
@@ -1,32 +1,32 @@
<?php
namespace Nasqueron\Notifications\Events;
use Illuminate\Queue\SerializesModels;
class JenkinsPayloadEvent extends Event {
use SerializesModels;
/**
* The gate door which receives the request
* @var string
*/
public $door;
/**
* The request content, as a structured data
* @var \stdClass
*/
public $payload;
/**
* Creates a new event instance.
*
- * @param string $door
+ * @param string $door
* @param \stdClass $payload
*/
- public function __construct($door, $payload) {
+ public function __construct(string $door, \stdClass $payload) {
$this->door = $door;
$this->payload = $payload;
}
}
diff --git a/app/Listeners/LastPayloadSaver.php b/app/Listeners/LastPayloadSaver.php
index 6ec4da1..5a2bffe 100644
--- a/app/Listeners/LastPayloadSaver.php
+++ b/app/Listeners/LastPayloadSaver.php
@@ -1,29 +1,29 @@
<?php
namespace Nasqueron\Notifications\Listeners;
use Nasqueron\Notifications\Events\Event;
class LastPayloadSaver {
///
/// Events handling
///
/**
* Handles payload events
*/
public function handle (Event $event) : void {
self::savePayload($event->payload);
}
/**
* Saves payload to log file
*
* @param mixed $payload The payload to save
*/
- public static function savePayload ($payload) : void {
+ public static function savePayload (mixed $payload) : void {
$filename = storage_path('logs/payload.json');
$content = json_encode($payload);
file_put_contents($filename, $content);
}
}
diff --git a/app/Notifications/JenkinsNotification.php b/app/Notifications/JenkinsNotification.php
index 1ce9d9e..aa01357 100644
--- a/app/Notifications/JenkinsNotification.php
+++ b/app/Notifications/JenkinsNotification.php
@@ -1,112 +1,112 @@
<?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
+ * @param mixed $payload The message fired by Jenkins notification plugin
*/
- public function __construct ($project, $payload) {
+ 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/Phabricator/PhabricatorAPI.php b/app/Phabricator/PhabricatorAPI.php
index 49270ed..139352b 100644
--- a/app/Phabricator/PhabricatorAPI.php
+++ b/app/Phabricator/PhabricatorAPI.php
@@ -1,163 +1,164 @@
<?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 ($endPoint, $apiToken) {
+ 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) {
$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 ($url) {
+ public function setEndPoint (string $url) {
$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
+ * @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 ($method, $arguments = []) {
+ public function call (string $method, array $arguments = []) {
$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 ($reply) {
+ public static function getFirstResult (iterable $reply) {
if (is_object($reply) && property_exists($reply, 'data')) {
$reply = $reply->data;
}
foreach ($reply as $value) {
return $value;
}
}
///
/// CURL session
///
protected static function getPostFields ($arguments) {
$items = [];
foreach ($arguments as $key => $value) {
$items[] = urlencode($key) . '=' . urlencode($value);
}
return implode('&', $items);
}
protected static function post ($url, $arguments) {
$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 193d06d..8127f21 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 ($instance) {
+ public function get (string $instance) {
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 ($project) {
+ public function getForProject (string $project) {
return PhabricatorAPI::forProject($project);
}
}
diff --git a/app/Phabricator/PhabricatorStory.php b/app/Phabricator/PhabricatorStory.php
index 33822bb..2bf1837 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 ($instanceName) {
+ 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
) {
$instance = new self($instanceName);
foreach ($payload as $key => $value) {
$property = self::mapPhabricatorFeedKey($key);
$instance->$property = $value;
}
return $instance;
}
public static function loadFromJson (
$instanceName,
$payload
) {
$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 () {
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 () {
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 ($method) {
+ public function getRepositoryPHID (string $method) {
$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 $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 ($method, $objectPHID) {
+ public function getItemProjectsPHIDs (string $method, string $objectPHID) {
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
) {
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 () {
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 () {
$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 ($key) {
+ public static function mapPhabricatorFeedKey (string $key) {
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 0596b70..2af59d0 100644
--- a/app/Phabricator/ProjectsMap.php
+++ b/app/Phabricator/ProjectsMap.php
@@ -1,285 +1,285 @@
<?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 ($instanceName) {
+ public function __construct (string $instanceName) {
$this->instanceName = $instanceName;
}
///
/// IteratorAggregate interface implementation
///
/**
* Gets iterator.
*
* @return \Traversable
*/
public function getIterator () {
return new \ArrayIterator($this->map);
}
///
/// ArrayAccess interface implementation
///
/**
* Determines whether an offset exists.
*
* @param mixed $offset The offset
* @return bool
*/
- public function offsetExists ($offset) {
+ public function offsetExists (mixed $offset) {
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 ($offset) {
+ public function offsetGet (mixed $offset) {
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 ($offset, $value) {
+ 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 ($offset) {
+ 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 ($phabricatorInstanceName) {
+ public static function load (string $phabricatorInstanceName) {
$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
) {
$instance = new self($phabricatorInstanceName);
$instance->setAPIClient($apiClient);
$instance->fetchFromAPI();
return $instance;
}
///
/// API
///
/**
* @return \Nasqueron\Notifications\Contracts\APIClient
*/
public function getAPIClient () {
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) {
$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 () {
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 () {
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 ($projectPHID) {
+ public function getProjectName (string $projectPHID) {
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']
*/
public function toArray () {
$array = [];
foreach ($this->map as $phid => $projectName) {
$array[] = [$phid, $projectName];
}
return $array;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 24, 22:22 (8 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2259032
Default Alt Text
(40 KB)

Event Timeline