Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F27327198
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
12 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/app/Phabricator/ProjectsMap.php b/app/Phabricator/ProjectsMap.php
index 62ba716..876053f 100644
--- a/app/Phabricator/ProjectsMap.php
+++ b/app/Phabricator/ProjectsMap.php
@@ -1,273 +1,275 @@
<?php
namespace Nasqueron\Notifications\Phabricator;
use Nasqueron\Notifications\Phabricator\PhabricatorAPIClient as ApiClient;
use App;
use 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 for this projects map
*
* @var string
*/
private $instance;
/**
*
* @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 $instance The Phabricator root URL without trailing slash
*/
public function __construct ($instance) {
$this->instance = $instance;
}
///
/// 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
*/
public function offsetExists ($offset) {
return array_key_exists($offset, $this->map);
}
/**
* Gets an offset
*
* @param mixed $offset The offset
* @return mixed The value
*/
public function offsetGet ($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) {
$this->map[$offset] = $value;
}
/**
* Unset an offset
*
* @param mixed $offset The offset
*/
public function offsetUnset ($offset) {
unset($this->map[$offset]);
}
///
/// Static constructors
///
/**
* Gets a new ProjectsMap instance from cache or API when not cached
*
* @param string $phabricatorURL The Phabricator URL (e.g. http://secure.phabricator.com)
* @return ProjectsMap
*/
public static function load ($phabricatorURL) {
$instance = new self($phabricatorURL);
if ($instance->isCached()) {
$instance->loadFromCache();
} else {
$instance->fetchFromAPI();
}
return $instance;
}
/**
* Gets a new ProjectsMap instance and queries Phabricator API to fill it
*
* @param string $phabricatorURL The Phabricator URL (e.g. http://secure.phabricator.com)
+ * @param Nasqueron\Notifications\Contracts\APIClient $apiClient The Phabricator API client
* @return ProjectsMap
*/
- public static function fetch ($phabricatorURL) {
+ public static function fetch ($phabricatorURL, APIClient $apiClient = null) {
$instance = new self($phabricatorURL);
+ $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->get($this->instance);
}
return $this->apiClient;
}
/**
* @param Nasqueron\Notifications\Contracts\APIClient $apiClient
*/
- public function setAPIClient (APIClient $apiClient) {
+ public function setAPIClient (APIClient $apiClient = null) {
$this->apiClient = $apiClient;
}
/**
* Fetches the projects' map from the Phabricator API
*/
private function fetchFromAPI () {
$reply = $this->getAPIClient()->call(
'project.query',
[ 'limit' => self::LIMIT ]
);
if (!$reply) {
throw new \Exception("Empty reply calling project.query at $this->instance API.");
}
if (!property_exists($reply, 'data')) {
throw new \Exception("Invalid reply calling project.query at $this->instance API.");
}
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->instance);
}
/**
* 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
*/
public function getProjectName ($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, each row ['PHID', 'project name']
*
* @return array
*/
public function toArray () {
$array = [];
foreach ($this->map as $phid => $projectName) {
$array[] = [$phid, $projectName];
}
return $array;
}
}
diff --git a/tests/Phabricator/ProjectsMapTest.php b/tests/Phabricator/ProjectsMapTest.php
index 7bbe281..dd2e5b2 100644
--- a/tests/Phabricator/ProjectsMapTest.php
+++ b/tests/Phabricator/ProjectsMapTest.php
@@ -1,164 +1,192 @@
<?php
namespace Nasqueron\Notifications\Tests\Phabricator;
use Nasqueron\Notifications\Phabricator\ProjectsMap;
use Nasqueron\Notifications\Tests\TestCase;
+use Mockery;
+
class ProjectsMapTest extends TestCase {
/**
* @var Nasqueron\Notifications\Phabricator\ProjectsMap
*/
private $map;
public function setUp () {
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")
);
}
/**
* @expectedException ErrorException
*/
public function testOffsetGetWhenItDoesNotExist () {
$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) {
+ return Mockery::mock('Nasqueron\Notifications\Contract\APIClient')
+ ->shouldReceive('call')
+ ->andReturn($reply);
+ }
+
+ /**
+ * @expectedException Exception
+ */
+ public function testFetchFromAPIWithoutReply () {
+ $mock = $this->mockPhabricatorAPIWithReply(false);
+ ProjectsMap::fetch("http://phabricator.acme.tld", $mock);
+ }
+
+ /**
+ * @expectedException Exception
+ */
+ public function testFetchFromAPIInvalidReply () {
+ $mock = $this->mockPhabricatorAPIWithReply(new \stdClass);
+ ProjectsMap::fetch("http://phabricator.acme.tld", $mock);
+ }
+
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, May 5, 11:49 (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3684461
Default Alt Text
(12 KB)
Attached To
Mode
rNOTIF Notifications center
Attached
Detach File
Event Timeline
Log In to Comment