diff --git a/src/Endpoint/Abstracts/AbstractEndpoint.php b/src/Endpoint/Abstracts/AbstractEndpoint.php index 9e38e7e..2b6afc9 100644 --- a/src/Endpoint/Abstracts/AbstractEndpoint.php +++ b/src/Endpoint/Abstracts/AbstractEndpoint.php @@ -251,7 +251,7 @@ public function execute(array $options = []): static * @param null $data - short form data for Endpoint, which is configure by configureData method * @return $this */ - public function asyncExecute(array $options = []): EndpointInterface + public function asyncExecute(array $options = []): static { $request = $this->buildRequest(); $this->promise = $this->getHttpClient()->sendAsync($request, $options); diff --git a/src/Endpoint/Abstracts/AbstractModelEndpoint.php b/src/Endpoint/Abstracts/AbstractModelEndpoint.php index 4dae1d0..36f87f5 100644 --- a/src/Endpoint/Abstracts/AbstractModelEndpoint.php +++ b/src/Endpoint/Abstracts/AbstractModelEndpoint.php @@ -2,6 +2,7 @@ namespace MRussell\REST\Endpoint\Abstracts; +use GuzzleHttp\Psr7\Request; use MRussell\REST\Endpoint\Interfaces\EndpointInterface; use MRussell\REST\Exception\Endpoint\InvalidRequest; use GuzzleHttp\Psr7\Response; @@ -126,7 +127,7 @@ public function reset(): static return $this->clear(); } - public function execute(array $options = []): static + protected function setDefaultAction(): void { if (empty($this->_action)) { if (!empty($this->getId())) { @@ -135,7 +136,12 @@ public function execute(array $options = []): static $this->setCurrentAction(self::MODEL_ACTION_CREATE); } } - return parent::execute($options); + } + + public function buildRequest(): Request + { + $this->setDefaultAction(); + return parent::buildRequest(); } /** diff --git a/src/Endpoint/Provider/AbstractEndpointProvider.php b/src/Endpoint/Provider/AbstractEndpointProvider.php index c9ff056..d49be93 100644 --- a/src/Endpoint/Provider/AbstractEndpointProvider.php +++ b/src/Endpoint/Provider/AbstractEndpointProvider.php @@ -22,13 +22,14 @@ abstract class AbstractEndpointProvider implements EndpointProviderInterface * @inheritdoc * @throws InvalidRegistration */ - public function registerEndpoint($name, $className, array $properties = []): static + public function registerEndpoint(string $name, string $className, array $properties = []): static { try { $implements = class_implements($className); if (is_array($implements) && isset($implements[EndpointInterface::class])) { if (isset($properties[self::ENDPOINT_VERSIONS])) { $versions = $properties[self::ENDPOINT_VERSIONS]; + unset($properties[self::ENDPOINT_VERSIONS]); } $this->addEndpointRegistry($name, [ @@ -48,7 +49,10 @@ public function registerEndpoint($name, $className, array $properties = []): sta protected function addEndpointRegistry(string $name, array $properties): void { - if (isset($properties[self::ENDPOINT_NAME])) { + if (!isset($properties[self::ENDPOINT_CLASS])) { + throw new InvalidRegistration([$name]); + } + if (!isset($properties[self::ENDPOINT_NAME])) { $properties[self::ENDPOINT_NAME] = $name; } @@ -111,22 +115,28 @@ protected function buildEndpoint(string $name, string $version = null): Endpoint protected function isInVersionRange(string $version, array $ranges): bool { - $is = false; - foreach ($ranges as $range) { + $is = true; + foreach ($ranges as $compare => $range) { + if (is_numeric($compare)) { + $compare = "=="; + } $internalComp = true; if (is_array($range)) { - foreach ($range as $compare => $v) { - if (!version_compare($version, $v, $compare)) { + foreach ($range as $c => $v) { + if (is_array($v)) { + continue; + } + if (!$this->isInVersionRange($version, [$c => $v])) { $internalComp = false; break; } } } else { - $internalComp = $range == $version; + $internalComp = version_compare($version, $range, $compare); } - if ($internalComp) { - $is = true; + if (!$internalComp) { + $is = false; break; } } diff --git a/tests/Endpoint/AbstractEndpointProviderTest.php b/tests/Endpoint/AbstractEndpointProviderTest.php index 8b68b12..2caf658 100644 --- a/tests/Endpoint/AbstractEndpointProviderTest.php +++ b/tests/Endpoint/AbstractEndpointProviderTest.php @@ -14,7 +14,7 @@ /** * Class AbstractEndpointProviderTest * @package MRussell\REST\Tests\Endpoint - * @coversDefaultClass MRussell\REST\Endpoint\Provider\AbstractEndpointProvider + * @coversDefaultClass \MRussell\REST\Endpoint\Provider\AbstractEndpointProvider * @group AbstractEndpointProviderTest */ class AbstractEndpointProviderTest extends TestCase @@ -40,7 +40,8 @@ protected function tearDown(): void } /** - * @covers MRussell\REST\Endpoint\Provider\DefaultEndpointProvider::__construct + * @covers \MRussell\REST\Endpoint\Provider\DefaultEndpointProvider::__construct + * @covers \MRussell\REST\Endpoint\Provider\DefaultEndpointsTrait::registerDefaultEndpoints * @covers ::registerEndpoint */ public function testConstructor(): void @@ -61,13 +62,35 @@ public function testConstructor(): void /** * @covers ::registerEndpoint + * @covers ::addEndpointRegistry * @return EndpointProviderInterface */ public function testRegisterEndpoint(): EndpointProvider { $Provider = new EndpointProvider(); $this->assertEquals($Provider, $Provider->registerEndpoint('auth', AuthEndpoint::class)); - $this->assertEquals($Provider, $Provider->registerEndpoint('foo', Endpoint::class, ['url' => 'foo', 'httpMethod' => "GET"])); + $this->assertEquals($Provider, $Provider->registerEndpoint('foo', Endpoint::class, ['url' => 'foobar', 'httpMethod' => "GET"])); + $this->assertEquals($Provider, $Provider->registerEndpoint('versioned', Endpoint::class, + ['url' => 'v2/test', 'httpMethod' => "GET", 'versions' => [ '>=' => '2.0']])); + $Class = new \ReflectionClass(EndpointProvider::class); + $addEndpointRegistry = $Class->getMethod('addEndpointRegistry'); + $addEndpointRegistry->setAccessible(true); + + $addEndpointRegistry->invoke($Provider, 'bulk', ['url' => 'bulk', 'httpMethod' => "POST", 'class' => Endpoint::class]); + + $property = $Class->getProperty('registry'); + $property->setAccessible(true); + $register = $property->getValue($Provider); + $this->assertNotEmpty($register); + $this->assertEquals( 'auth', $register['auth']['name']); + $this->assertTrue(isset($register['auth']['versions'])); + $this->assertTrue(isset($register['auth']['properties'])); + $this->assertEquals( 'foo', $register['foo']['name']); + $this->assertTrue(isset($register['auth']['class'])); + $this->assertEquals( 'foobar', $register['foo']['properties']['url']); + $this->assertFalse(isset($register['versioned']['properties']['versions'])); + $this->assertEquals('bulk', $register['bulk']['name']); + return $Provider; } @@ -83,23 +106,42 @@ public function testInvalidRegistration(EndpointProviderInterface $Provider): vo $Provider->registerEndpoint("baz", "baz"); } + /** + * @depends testRegisterEndpoint + * @covers ::addEndpointRegistry + * @throws InvalidRegistration + */ + public function testInvalidRegistrationViaExtension(EndpointProviderInterface $Provider): void + { + $this->expectException(InvalidRegistration::class); + $this->expectExceptionMessage("Endpoint Object [bulk] must extend MRussell\REST\Endpoint\Interfaces\EndpointInterface"); + $Class = new \ReflectionClass(EndpointProvider::class); + $addEndpointRegistry = $Class->getMethod('addEndpointRegistry'); + $addEndpointRegistry->setAccessible(true); + + $addEndpointRegistry->invoke($Provider, 'bulk', ['url' => 'bulk', 'httpMethod' => "POST"]); + } + /** * @depends testRegisterEndpoint * @covers ::hasEndpoint * @covers ::getEndpoint * @covers ::buildEndpoint + * @covers ::getEndpointDefinition */ public function testGetEndpoint(EndpointProviderInterface $Provider): void { $this->assertEquals(false, $Provider->hasEndpoint('test')); $this->assertEquals(true, $Provider->hasEndpoint('foo')); $this->assertEquals(true, $Provider->hasEndpoint('auth')); + $this->assertEquals(true, $Provider->hasEndpoint('versioned')); + $this->assertEquals(false, $Provider->hasEndpoint('versioned', '1.0')); $Auth = new AuthEndpoint(); $this->assertEquals($Auth, $Provider->getEndpoint('auth')); $FooEP = $Provider->getEndpoint('foo'); $this->assertNotEmpty($FooEP); - $this->assertEquals('foo', $FooEP->getEndPointUrl()); - $this->assertEquals(['url' => 'foo', 'httpMethod' => "GET", 'auth' => 1], $FooEP->getProperties()); + $this->assertEquals('foobar', $FooEP->getEndPointUrl()); + $this->assertEquals(['url' => 'foobar', 'httpMethod' => "GET", 'auth' => 1], $FooEP->getProperties()); } /** @@ -113,4 +155,22 @@ public function testUnknownEndpoint(EndpointProviderInterface $Provider): void $this->expectExceptionMessage("An Unknown Endpoint [test] was requested."); $Provider->getEndpoint('test'); } + + /** + * @covrs ::isInVersionRange + */ + public function testIsInVersionRange(): void + { + $Provider = new EndpointProvider(); + $reflection = new \ReflectionClass(EndpointProvider::class); + $isInVersionRange = $reflection->getMethod('isInVersionRange'); + $isInVersionRange->setAccessible(true); + + $this->assertTrue($isInVersionRange->invoke($Provider,'1.0',['1.0'])); + $this->assertTrue($isInVersionRange->invoke($Provider,'1.1',[ '>=' => '1.0' ])); + $this->assertTrue($isInVersionRange->invoke($Provider,'1.9.1',[ ['>=' => '1.0'], ["<" => "2.0"] ])); + $this->assertFalse($isInVersionRange->invoke($Provider,'2.1',[ ['>=' => '1.0'], ["<" => "2.0"] ])); + $this->assertFalse($isInVersionRange->invoke($Provider,'2.0.1',[ '>=' => '1.0', "<" => "2.0" ])); + $this->assertTrue($isInVersionRange->invoke($Provider,'1.9',[ ['>=' => ['1.0','1.1']], "<" => "2.0" ])); + } } diff --git a/tests/Endpoint/AbstractModelEndpointTest.php b/tests/Endpoint/AbstractModelEndpointTest.php index fcdfe4d..42dba50 100644 --- a/tests/Endpoint/AbstractModelEndpointTest.php +++ b/tests/Endpoint/AbstractModelEndpointTest.php @@ -75,14 +75,43 @@ public function testCall(): void $Class = new \ReflectionClass($Model); $actions = $Class->getProperty('_actions'); $actions->setAccessible(true); - $this->assertEquals(['foo' => "GET", 'create' => "POST", 'retrieve' => "GET", 'update' => "PUT", 'delete' => "DELETE"], $actions->getValue($Model)); + $this->assertEquals(['foo' => "HEAD", 'create' => "POST", 'retrieve' => "GET", 'update' => "PUT", 'delete' => "DELETE"], $actions->getValue($Model)); $this->client->mockResponses->append(new Response(200)); $Model->setClient($this->client); $this->assertEquals($Model, $Model->foo()); $props = $Model->getProperties(); - $this->assertEquals("GET", $props['httpMethod']); + $this->assertEquals("HEAD", $props['httpMethod']); + } + + /** + * @covers ::setDefaultAction + * @covers ::buildRequest + */ + public function testSetDefaultAction(): void + { + $Model = new ModelEndpoint(); + $Class = new \ReflectionClass($Model); + $action = $Class->getProperty('_action'); + $action->setAccessible(true); + $this->assertEmpty($action->getValue($Model)); + + $this->client->mockResponses->append(new Response(200)); + $Model->setClient($this->client); + + $this->assertEquals($Model, $Model->execute()); + $this->assertEquals('create', $Model->getCurrentAction()); + $this->assertEquals("POST", $this->client->mockResponses->getLastRequest()->getMethod()); + + $this->client->mockResponses->append(new Response(200)); + + $Model = new ModelEndpoint(); + $Model->setClient($this->client); + $Model['id'] = '12345'; + $this->assertEquals($Model, $Model->execute()); + $this->assertEquals('retrieve', $Model->getCurrentAction()); + $this->assertEquals("GET", $this->client->mockResponses->getLastRequest()->getMethod()); } /** diff --git a/tests/Stubs/Endpoint/ModelEndpointWithActions.php b/tests/Stubs/Endpoint/ModelEndpointWithActions.php index a64ecba..6dcf37c 100644 --- a/tests/Stubs/Endpoint/ModelEndpointWithActions.php +++ b/tests/Stubs/Endpoint/ModelEndpointWithActions.php @@ -9,5 +9,5 @@ class ModelEndpointWithActions extends ModelEndpoint self::PROPERTY_RESPONSE_PROP => 'account', ]; - protected array $_actions = ['foo' => "GET"]; + protected array $_actions = ['foo' => "HEAD"]; }