diff --git a/CHANGELOG.md b/CHANGELOG.md index 735bc175..245caff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,11 @@ # CHANGELOG +## [3.0.4](https://github.com/apigee/apigee-client-php/milestone/28?closed=1) +* [#343] Fix Invalid payload issue while editing/updating AppGroup/Teams in Apigee X. +* [#342] Fix for test failing for symfony 6.4. +* [#340] Fix for attribute values lost in PUT call while creating AppGroup. +* [#337] Fix for Team app credentials listings sorted in ascending order. +* [#334] Fix for \Apigee\Edge\HttpClient\Plugin\Authentication\GceServiceAccount::isAvailable() throws exception when called on non-GCE context. + ## [3.0.3](https://github.com/apigee/apigee-client-php/milestone/26?closed=1) * [#323] Add union type to suppress deprecation warning. * [#325] Fix error when AppGroup list is empty or do not returns appgroups array. diff --git a/composer.json b/composer.json index f58114e7..32c9220f 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ "phpunit/phpunit": "^9.6", "sebastian/comparator": "^4.0.5", "symfony/cache": "^6.3", - "vimeo/psalm": "^5.12" + "vimeo/psalm": "^5.20" }, "autoload": { "psr-4": { diff --git a/psalm.xml.dist b/psalm.xml.dist index b0582145..065e811d 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -52,5 +52,8 @@ + + + diff --git a/src/Api/ApigeeX/Controller/AppGroupMembersController.php b/src/Api/ApigeeX/Controller/AppGroupMembersController.php index a4fe6b4b..411eaa3d 100644 --- a/src/Api/ApigeeX/Controller/AppGroupMembersController.php +++ b/src/Api/ApigeeX/Controller/AppGroupMembersController.php @@ -20,6 +20,7 @@ use Apigee\Edge\Api\ApigeeX\Serializer\AppGroupMembershipSerializer; use Apigee\Edge\Api\ApigeeX\Structure\AppGroupMembership; +use Apigee\Edge\Api\Management\Serializer\AttributesPropertyAwareEntitySerializer; use Apigee\Edge\ClientInterface; use Apigee\Edge\Controller\AbstractController; use Apigee\Edge\Controller\OrganizationAwareControllerTrait; @@ -47,7 +48,7 @@ class AppGroupMembersController extends AbstractController implements AppGroupMe * * @param string $appGroup * @param string $organization - * @param \Apigee\Edge\ClientInterface $client + * @param ClientInterface $client */ public function __construct(string $appGroup, string $organization, ClientInterface $client) { @@ -73,8 +74,10 @@ public function getMembers(): AppGroupMembership public function setMembers(AppGroupMembership $members): AppGroupMembership { $members = $this->serializer->normalize($members); - $apigeeReservedMembers = new AttributesProperty(); + // We don't have a separate API to get appgroup attributes, + // that is why we are calling getAppGroupAttributes() method. + $apigeeReservedMembers = $this->getAppGroupAttributes(); // Adding the new members into the attribute. $apigeeReservedMembers->add('__apigee_reserved__developer_details', json_encode($members)); $response = $this->client->put( @@ -101,6 +104,23 @@ public function removeMember(string $email): void $this->client->delete($this->getBaseEndpointUri()->withPath("{$this->getBaseEndpointUri()->getPath()}/{$encoded}")); } + /** + * Helper function for getting all attributes in AppGroup. + * + * @return AttributesProperty + */ + public function getAppGroupAttributes(): AttributesProperty + { + $appGroup = $this->responseToArray($this->client->get($this->getBaseEndpointUri())); + $serializer = new AttributesPropertyAwareEntitySerializer(); + $appGroupAttributes = $serializer->denormalize( + $appGroup['attributes'], + AttributesProperty::class + ); + + return $appGroupAttributes; + } + /** * {@inheritdoc} */ diff --git a/src/Api/ApigeeX/Controller/AppGroupMembersControllerInterface.php b/src/Api/ApigeeX/Controller/AppGroupMembersControllerInterface.php index 9f5f67b1..a321d92e 100644 --- a/src/Api/ApigeeX/Controller/AppGroupMembersControllerInterface.php +++ b/src/Api/ApigeeX/Controller/AppGroupMembersControllerInterface.php @@ -28,7 +28,7 @@ interface AppGroupMembersControllerInterface extends AppGroupAwareControllerInte /** * List all developers associated with a appgroup. * - * @return \Apigee\Edge\Api\ApigeeX\Structure\AppGroupMembership + * @return AppGroupMembership * Array of developers with their optional roles in the appgroup. */ public function getMembers(): AppGroupMembership; @@ -39,10 +39,10 @@ public function getMembers(): AppGroupMembership; * WARNING! If you pass en empty membership object you remove all developers * from the appgroup. * - * @param \Apigee\Edge\Api\ApigeeX\Structure\AppGroupMembership $members + * @param AppGroupMembership $members * Membership object with the changes to be applied. * - * @return \Apigee\Edge\Api\ApigeeX\Structure\AppGroupMembership + * @return AppGroupMembership * Membership object with the applied changes, it does not contain all * members. Use getMembers() to retrieve them. */ diff --git a/src/Api/ApigeeX/Entity/AppGroup.php b/src/Api/ApigeeX/Entity/AppGroup.php index da1a3706..0c8dead0 100644 --- a/src/Api/ApigeeX/Entity/AppGroup.php +++ b/src/Api/ApigeeX/Entity/AppGroup.php @@ -18,18 +18,24 @@ namespace Apigee\Edge\Api\ApigeeX\Entity; -use Apigee\Edge\Api\Management\Entity\AppOwner; +use Apigee\Edge\Entity\CommonEntityPropertiesAwareTrait; +use Apigee\Edge\Entity\Entity; +use Apigee\Edge\Entity\Property\AttributesPropertyAwareTrait; use Apigee\Edge\Entity\Property\DisplayNamePropertyAwareTrait; use Apigee\Edge\Entity\Property\NamePropertyAwareTrait; +use Apigee\Edge\Entity\Property\StatusPropertyAwareTrait; use Apigee\Edge\Structure\AttributesProperty; /** * Describes an AppGroup entity. */ -class AppGroup extends AppOwner implements AppGroupInterface +class AppGroup extends Entity implements AppGroupInterface { use DisplayNamePropertyAwareTrait; use NamePropertyAwareTrait; + use AttributesPropertyAwareTrait; + use CommonEntityPropertiesAwareTrait; + use StatusPropertyAwareTrait; /** @var string|null */ protected $channelUri; diff --git a/src/Api/ApigeeX/Entity/AppGroupInterface.php b/src/Api/ApigeeX/Entity/AppGroupInterface.php index d422ba79..ecd3959e 100644 --- a/src/Api/ApigeeX/Entity/AppGroupInterface.php +++ b/src/Api/ApigeeX/Entity/AppGroupInterface.php @@ -18,16 +18,20 @@ namespace Apigee\Edge\Api\ApigeeX\Entity; -use Apigee\Edge\Api\Management\Entity\AppOwnerInterface; +use Apigee\Edge\Entity\CommonEntityPropertiesInterface; +use Apigee\Edge\Entity\Property\AttributesPropertyInterface; use Apigee\Edge\Entity\Property\DisplayNamePropertyInterface; use Apigee\Edge\Entity\Property\NamePropertyInterface; +use Apigee\Edge\Entity\Property\StatusPropertyInterface; /** * Interface AppGroupInterface. */ -interface AppGroupInterface extends AppOwnerInterface, +interface AppGroupInterface extends AttributesPropertyInterface, DisplayNamePropertyInterface, - NamePropertyInterface + NamePropertyInterface, + StatusPropertyInterface, + CommonEntityPropertiesInterface { /** * @param string $channelUri diff --git a/src/ClientInterface.php b/src/ClientInterface.php index c42e55cb..5a11a875 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -63,12 +63,12 @@ interface ClientInterface extends HttpClient */ public const APIGEE_ON_GCP_ENDPOINT = 'https://apigee.googleapis.com/v1'; - public const VERSION = '3.0.3'; + public const VERSION = '3.0.4'; /** * Allows access to the last request, response and exception. * - * @return \Apigee\Edge\HttpClient\Utility\JournalInterface + * @return JournalInterface */ public function getJournal(): JournalInterface; @@ -108,10 +108,10 @@ public function getEndpoint(): string; * @param \Psr\Http\Message\UriInterface|string $uri * @param array $headers * - * @throws \Apigee\Edge\Exception\ApiException + * @throws Exception\ApiException * @throws \Http\Client\Exception * - * @return \Psr\Http\Message\ResponseInterface + * @return ResponseInterface */ public function get($uri, array $headers = []): ResponseInterface; @@ -121,10 +121,10 @@ public function get($uri, array $headers = []): ResponseInterface; * @param \Psr\Http\Message\UriInterface|string $uri * @param array $headers * - * @throws \Apigee\Edge\Exception\ApiException + * @throws Exception\ApiException * @throws \Http\Client\Exception * - * @return \Psr\Http\Message\ResponseInterface + * @return ResponseInterface */ public function head($uri, array $headers = []): ResponseInterface; @@ -135,10 +135,10 @@ public function head($uri, array $headers = []): ResponseInterface; * @param \Psr\Http\Message\StreamInterface|resource|string|null $body * @param array $headers * - * @throws \Apigee\Edge\Exception\ApiException + * @throws Exception\ApiException * @throws \Http\Client\Exception * - * @return \Psr\Http\Message\ResponseInterface + * @return ResponseInterface */ public function post($uri, $body = null, array $headers = []): ResponseInterface; @@ -149,10 +149,10 @@ public function post($uri, $body = null, array $headers = []): ResponseInterface * @param \Psr\Http\Message\StreamInterface|resource|string|null $body * @param array $headers * - * @throws \Apigee\Edge\Exception\ApiException + * @throws Exception\ApiException * @throws \Http\Client\Exception * - * @return \Psr\Http\Message\ResponseInterface + * @return ResponseInterface */ public function put($uri, $body = null, array $headers = []): ResponseInterface; @@ -163,10 +163,10 @@ public function put($uri, $body = null, array $headers = []): ResponseInterface; * @param \Psr\Http\Message\StreamInterface|resource|string|null $body * @param array $headers * - * @throws \Apigee\Edge\Exception\ApiException + * @throws Exception\ApiException * @throws \Http\Client\Exception * - * @return \Psr\Http\Message\ResponseInterface + * @return ResponseInterface */ public function delete($uri, $body = null, array $headers = []): ResponseInterface; } diff --git a/tests/Api/ApigeeX/Controller/AppCredentialControllerTestBase.php b/tests/Api/ApigeeX/Controller/AppCredentialControllerTestBase.php index 07f0212a..ca61bab3 100644 --- a/tests/Api/ApigeeX/Controller/AppCredentialControllerTestBase.php +++ b/tests/Api/ApigeeX/Controller/AppCredentialControllerTestBase.php @@ -18,10 +18,10 @@ namespace Apigee\Edge\Tests\Api\ApigeeX\Controller; +use Apigee\Edge\Api\ApigeeX\Entity\AppGroup; use Apigee\Edge\Api\Management\Controller\AppCredentialControllerInterface; use Apigee\Edge\Api\Management\Entity\AppCredentialInterface; use Apigee\Edge\Api\Management\Entity\AppInterface; -use Apigee\Edge\Api\Management\Entity\AppOwnerInterface; use Apigee\Edge\Structure\CredentialProductInterface; use Apigee\Edge\Tests\Api\Management\Entity\ApiProductTestEntityProviderTrait; use Apigee\Edge\Tests\Test\Controller\DefaultAPIClientAwareTrait; @@ -43,13 +43,13 @@ abstract class AppCredentialControllerTestBase extends EntityControllerTestBase // The order of these trait matters. Check @depends in test methods. use AttributesAwareEntityControllerTestTrait; - /** @var \Apigee\Edge\Api\ApigeeX\Entity\ApiProductInterface */ + /** @var ApiProductInterface */ protected static $testApiProduct; - /** @var \Apigee\Edge\Api\Management\Entity\AppOwnerInterface */ + /** @var AppOwnerInterface */ protected static $testAppOwner; - /** @var \Apigee\Edge\Api\Management\Entity\AppInterface */ + /** @var AppInterface */ protected static $testApp; /** @@ -66,12 +66,12 @@ public static function setUpBeforeClass(): void public function testCreatedAppHasAnEmptyCredential(): void { - /** @var \Apigee\Edge\Api\Management\Entity\DeveloperAppInterface $entity */ - /** @var \Apigee\Edge\Api\Management\Controller\AppByOwnerControllerInterface $controller */ + /** @var DeveloperAppInterface $entity */ + /** @var AppByOwnerControllerInterface $controller */ $entity = static::appByOwnerController()->load(static::$testApp->id()); $credentials = $entity->getCredentials(); $this->assertCount(1, $credentials); - /** @var \Apigee\Edge\Api\Management\Entity\AppCredentialInterface $credential */ + /** @var AppCredentialInterface $credential */ $credential = reset($credentials); $this->assertCount(0, $credential->getApiProducts()); $this->assertNotEmpty($credential->getConsumerKey()); @@ -82,7 +82,7 @@ public function testCreatedAppHasAnEmptyCredential(): void /** * @depends testCreatedAppHasAnEmptyCredential * - * @return \Apigee\Edge\Api\Management\Entity\AppCredentialInterface + * @return AppCredentialInterface */ public function testCreate(): AppCredentialInterface { @@ -102,13 +102,13 @@ public function testCreate(): AppCredentialInterface /** * @depends testCreate * - * @param \Apigee\Edge\Api\Management\Entity\AppCredentialInterface $created + * @param AppCredentialInterface $created * * @return string */ public function testLoad(AppCredentialInterface $created) { - /** @var \Apigee\Edge\Api\Management\Entity\AppCredentialInterface $loaded */ + /** @var AppCredentialInterface $loaded */ $loaded = static::entityController()->load($created->id()); $this->assertCount(count($loaded->getApiProducts()), $created->getApiProducts()); $this->assertEquals($created->getConsumerKey(), $loaded->getConsumerKey()); @@ -126,7 +126,7 @@ public function testLoad(AppCredentialInterface $created) */ public function testAddProducts(string $entityId): void { - /** @var \Apigee\Edge\Api\Management\Controller\AppCredentialControllerInterface $controller */ + /** @var AppCredentialControllerInterface $controller */ $controller = $this->entityController(); $credential = $controller->addProducts($entityId, [static::$testApiProduct->id()]); $productNames = $this->getCredentialProducts($credential); @@ -140,9 +140,9 @@ public function testAddProducts(string $entityId): void */ public function testOverrideScopes(string $entityId): void { - /** @var \Apigee\Edge\Api\Management\Controller\AppCredentialControllerInterface $controller */ + /** @var AppCredentialControllerInterface $controller */ $controller = $this->entityController(); - /** @var \Apigee\Edge\Api\Management\Entity\AppCredentialInterface $credential */ + /** @var AppCredentialInterface $credential */ $credential = $controller->load($entityId); $this->assertEmpty($credential->getScopes()); $credential = $controller->overrideScopes($entityId, ['scope 1']); @@ -165,9 +165,9 @@ public function testOverrideScopes(string $entityId): void public function testStatusChange(string $entityId): void { static::markOnlineTestSkipped(__FUNCTION__); - /** @var \Apigee\Edge\Api\Management\Controller\AppCredentialControllerInterface $controller */ + /** @var AppCredentialControllerInterface $controller */ $controller = static::entityController(); - /* @var \Apigee\Edge\Api\Management\Entity\AppCredentialInterface $credential */ + /* @var AppCredentialInterface $credential */ $controller->setStatus($entityId, AppCredentialControllerInterface::STATUS_REVOKE); $credential = $controller->load($entityId); $this->assertEquals($credential->getStatus(), AppCredentialInterface::STATUS_REVOKED); @@ -186,16 +186,16 @@ public function testStatusChange(string $entityId): void public function testApiProductStatusChange(string $entityId): void { static::markOnlineTestSkipped(__FUNCTION__); - /** @var \Apigee\Edge\Api\Management\Controller\AppCredentialControllerInterface $controller */ + /** @var AppCredentialControllerInterface $controller */ $controller = static::entityController(); - /* @var \Apigee\Edge\Api\Management\Entity\AppCredentialInterface $credential */ + /* @var AppCredentialInterface $credential */ $controller->setApiProductStatus( $entityId, static::$testApiProduct->id(), AppCredentialControllerInterface::STATUS_REVOKE ); $credential = $controller->load($entityId); - /** @var \Apigee\Edge\Structure\CredentialProduct $product */ + /** @var CredentialProduct $product */ foreach ($credential->getApiProducts() as $product) { if ($product->getApiproduct() === static::$testApiProduct->id()) { $this->assertEquals($product->getStatus(), CredentialProductInterface::STATUS_REVOKED); @@ -224,11 +224,11 @@ public function testApiProductStatusChange(string $entityId): void public function testGenerate(): string { static::markOnlineTestSkipped(__FUNCTION__); - /** @var \Apigee\Edge\Api\Management\Controller\AppCredentialControllerInterface $controller */ + /** @var AppCredentialControllerInterface $controller */ $controller = $this->entityController(); - /** @var \Apigee\Edge\Api\Management\Entity\AppInterface $app */ + /** @var AppInterface $app */ $app = static::appByOwnerController()->load(static::$testApp->id()); - /** @var \Apigee\Edge\Api\Management\Entity\AppCredentialInterface $credential */ + /** @var AppCredentialInterface $credential */ $credential = $controller->generate( [static::$testApiProduct->id()], $app->getAttributes(), @@ -243,7 +243,7 @@ public function testGenerate(): string // Thanks for the offline tests, we can not expect a concrete value // here. $this->assertNotEquals('-1', $credential->getExpiresAt()); - /** @var \Apigee\Edge\Api\Management\Entity\AppInterface $updatedApp */ + /** @var AppInterface $updatedApp */ $updatedApp = static::appByOwnerController()->load(static::$testApp->id()); // Credential generation should not deleted any previously existing app // credentials. @@ -260,13 +260,13 @@ public function testGenerate(): string public function testDeleteApiProduct(string $entityId): void { static::markOnlineTestSkipped(__FUNCTION__); - /** @var \Apigee\Edge\Api\Management\Controller\AppCredentialControllerInterface $controller */ + /** @var AppCredentialControllerInterface $controller */ $controller = static::entityController(); - /** @var \Apigee\Edge\Api\Management\Entity\AppCredentialInterface $credential */ + /** @var AppCredentialInterface $credential */ $credential = $controller->load($entityId); $productNames = $this->getCredentialProducts($credential); $this->assertContains(static::$testApiProduct->id(), $productNames); - /* @var \Apigee\Edge\Api\Management\Entity\AppCredentialInterface $credential */ + /* @var AppCredentialInterface $credential */ $controller->deleteApiProduct( $entityId, static::$testApiProduct->id() @@ -281,15 +281,15 @@ public function testDeleteApiProduct(string $entityId): void */ public function testAddAttributesToEntity(): string { - /** @var \Apigee\Edge\Api\Management\Entity\AppCredentialInterface $credential */ + /** @var AppCredentialInterface $credential */ $credentials = static::$testApp->getCredentials(); $credential = reset($credentials); - /** @var \Apigee\Edge\Structure\AttributesProperty $attributes */ + /** @var AttributesProperty $attributes */ $attributes = $credential->getAttributes(); $originalAttributes = $attributes->values(); $attributes->add('name1', 'value1'); $attributes->add('name2', 'value2'); - /** @var \Apigee\Edge\Structure\AttributesProperty $attributesProperty */ + /** @var AttributesProperty $attributesProperty */ $attributesProperty = static::entityController()->updateAttributes($credential->id(), $attributes); /** @var array $newAttributes */ $newAttributes = $attributesProperty->values(); @@ -329,19 +329,19 @@ public function testDelete(): void $this->assertFalse($found, 'Credential credential has not been deleted.'); } - abstract protected static function setupTestApp(AppOwnerInterface $appOwner): AppInterface; + abstract protected static function setupTestApp(AppGroup $appOwner): AppInterface; - abstract protected static function setupTestAppOwner(): AppOwnerInterface; + abstract protected static function setupTestAppOwner(): AppGroup; /** - * @return \Apigee\Edge\Tests\Test\Controller\EntityControllerTesterInterface|\Apigee\Edge\Api\Management\Controller\AppByOwnerControllerInterface + * @return EntityControllerTesterInterface|AppByOwnerControllerInterface */ abstract protected static function appByOwnerController(): EntityControllerTesterInterface; private function getCredentialProducts(AppCredentialInterface $credential) { return array_map(function ($product) { - /* @var \Apigee\Edge\Structure\CredentialProduct $product */ + /* @var CredentialProduct $product */ return $product->getApiproduct(); }, $credential->getApiProducts()); } diff --git a/tests/Api/ApigeeX/Controller/AppGroupAppCredentialControllerTest.php b/tests/Api/ApigeeX/Controller/AppGroupAppCredentialControllerTest.php index 85562193..38cc3ac3 100644 --- a/tests/Api/ApigeeX/Controller/AppGroupAppCredentialControllerTest.php +++ b/tests/Api/ApigeeX/Controller/AppGroupAppCredentialControllerTest.php @@ -19,9 +19,9 @@ namespace Apigee\Edge\Tests\Api\ApigeeX\Controller; use Apigee\Edge\Api\ApigeeX\Controller\AppGroupAppCredentialController; +use Apigee\Edge\Api\ApigeeX\Entity\AppGroup; use Apigee\Edge\Api\ApigeeX\Entity\AppGroupInterface; use Apigee\Edge\Api\Management\Entity\AppInterface; -use Apigee\Edge\Api\Management\Entity\AppOwnerInterface; use Apigee\Edge\ClientInterface; use Apigee\Edge\Entity\EntityInterface; use Apigee\Edge\Tests\Api\ApigeeX\Entity\AppGroupAppTestEntityProviderTrait; @@ -47,14 +47,14 @@ class AppGroupAppCredentialControllerTest extends AppCredentialControllerTestBas /** * {@inheritdoc} */ - protected static function entityController(ClientInterface $client = null): EntityControllerTesterInterface + protected static function entityController(?ClientInterface $client = null): EntityControllerTesterInterface { $client = $client ?? static::defaultAPIClient(); return new EntityControllerTester(new AppGroupAppCredentialController(static::defaultTestOrganization($client), static::$testAppOwner->id(), static::$testApp->id(), $client)); } - protected static function setupTestApp(AppOwnerInterface $appOwner): AppInterface + protected static function setupTestApp(AppGroup $appOwner): AppInterface { $app = static::getNewAppGroupApp(); static::appGroupAppController()->create($app); @@ -62,7 +62,7 @@ protected static function setupTestApp(AppOwnerInterface $appOwner): AppInterfac return $app; } - protected static function setupTestAppOwner(): AppOwnerInterface + protected static function setupTestAppOwner(): AppGroup { $appGroup = static::getNewAppGroup(); static::appGroupController()->create($appGroup); diff --git a/tests/offline-test-data/v1/organizations/phpunit/appgroups/POST.json b/tests/offline-test-data/v1/organizations/phpunit/appgroups/POST.json index c01a3e58..253d2117 100644 --- a/tests/offline-test-data/v1/organizations/phpunit/appgroups/POST.json +++ b/tests/offline-test-data/v1/organizations/phpunit/appgroups/POST.json @@ -1,5 +1,6 @@ { - "apps": [], + "channelUri": "http:\/\/example.com", + "channelId": "devportal", "name": "phpunit", "displayName": "A PHPUnit appgroup", "status": "active", diff --git a/tests/offline-test-data/v1/organizations/phpunit/appgroups/phpunit/PUT.json b/tests/offline-test-data/v1/organizations/phpunit/appgroups/phpunit/PUT.json index b35b8b85..c3dc9c68 100644 --- a/tests/offline-test-data/v1/organizations/phpunit/appgroups/phpunit/PUT.json +++ b/tests/offline-test-data/v1/organizations/phpunit/appgroups/phpunit/PUT.json @@ -1,5 +1,6 @@ { - "apps": [], + "channelUri": "http:\/\/example.com", + "channelId": "devportal", "name": "phpunit", "displayName": "(Edited) A PHPUnit appgroup", "status": "active",