From a8b428eb2501d8f28bf055682efc669adcdd5bd1 Mon Sep 17 00:00:00 2001 From: Shishir Suvarna Date: Mon, 12 Feb 2024 11:34:17 +0530 Subject: [PATCH 1/4] Fix- Team 1000+ listing, AppGroup edit error and custom attribute update issues --- .../Controller/AppGroupMembersController.php | 24 +++++++++- .../AppGroupMembersControllerInterface.php | 6 +-- .../Controller/PaginationHelperTrait.php | 45 ++++++++++--------- src/Api/ApigeeX/Entity/AppGroup.php | 10 ++++- src/Api/ApigeeX/Entity/AppGroupInterface.php | 10 +++-- 5 files changed, 63 insertions(+), 32 deletions(-) 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/Controller/PaginationHelperTrait.php b/src/Api/ApigeeX/Controller/PaginationHelperTrait.php index 5aaa6eff..ed7db5ae 100644 --- a/src/Api/ApigeeX/Controller/PaginationHelperTrait.php +++ b/src/Api/ApigeeX/Controller/PaginationHelperTrait.php @@ -112,8 +112,14 @@ protected function listEntities(PagerInterface $pager = null, array $query_param return $this->responseArrayToArrayOfEntities($responseArray, $key_provider); } else { + // Default page size set to 1000, because the AppGroupApps endpoint + // does not return nextPageToken unless a pageSize is specified + // in the request parameters. + $pageSize = 1000; // Pass an empty pager to load all entities. - $responseArray = $this->getResultsInRange($this->createPager(), $query_params); + $responseArray = $this->getResultsInRange($this->createPager($pageSize), $query_params); + // Check flag 'nextPageToken' to get next items from the list. + $nextPageToken = array_key_exists('nextPageToken', $responseArray) ? $responseArray['nextPageToken'] : false; // Ignore entity type key from response, ex.: developer, apiproduct, // etc. $responseArray = reset($responseArray); @@ -122,30 +128,25 @@ protected function listEntities(PagerInterface $pager = null, array $query_param return []; } $entities = $this->responseArrayToArrayOfEntities($responseArray, $key_provider); - $lastEntity = end($entities); - $lastId = $lastEntity->{$key_provider}(); - do { - $tmp = $this->getResultsInRange($this->createPager(0, $lastId), $query_params); - // Ignore entity type key from response, ex.: developer, - // apiproduct, etc. - $tmp = reset($tmp); - // Remove the first item from the list because it is the same - // as the last item of $entities at this moment. - // Apigee X response always starts with the requested entity - // (pageToken). - array_shift($tmp); - $tmpEntities = $this->responseArrayToArrayOfEntities($tmp, $key_provider); - - if (count($tmpEntities) > 0) { + if ($nextPageToken) { + do { + $tmp = $this->getResultsInRange($this->createPager($pageSize, $nextPageToken), $query_params); + // Check the flag 'nextPageToken' to get next items from the list. + $nextPageToken = array_key_exists('nextPageToken', $tmp) ? $tmp['nextPageToken'] : false; + // Ignore entity type key from response, ex.: developer, + // apiproduct, etc. + $tmp = reset($tmp); + // Remove the first item from the list because it is the same + // as the last item of $entities at this moment. + // Apigee X response always starts with the requested entity + // (pageToken). + array_shift($tmp); + $tmpEntities = $this->responseArrayToArrayOfEntities($tmp, $key_provider); // The returned entity array is keyed by entity id which // is unique so we can do this. $entities += $tmpEntities; - $lastEntity = end($tmpEntities); - $lastId = $lastEntity->{$key_provider}(); - } else { - $lastId = false; - } - } while ($lastId); + } while ($nextPageToken); + } return $entities; } 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 From 4a39adcea150dda41206f222e5d8ebea59d940e1 Mon Sep 17 00:00:00 2001 From: Shishir Suvarna Date: Mon, 12 Feb 2024 11:40:26 +0530 Subject: [PATCH 2/4] Added Appgroup GET list mock files --- .../phpunit/appgroups/GET_pageSize=1000.json | 69 ++++++ .../phpunit/apps/GET_pageSize=1000.json | 206 ++++++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 tests/offline-test-data/v1/organizations/phpunit/appgroups/GET_pageSize=1000.json create mode 100644 tests/offline-test-data/v1/organizations/phpunit/appgroups/phpunit/apps/GET_pageSize=1000.json diff --git a/tests/offline-test-data/v1/organizations/phpunit/appgroups/GET_pageSize=1000.json b/tests/offline-test-data/v1/organizations/phpunit/appgroups/GET_pageSize=1000.json new file mode 100644 index 00000000..cab2cbeb --- /dev/null +++ b/tests/offline-test-data/v1/organizations/phpunit/appgroups/GET_pageSize=1000.json @@ -0,0 +1,69 @@ +{ + "appGroups": [ + { + "name": "phpunit", + "displayName": "A PHPUnit appgroup", + "status": "active", + "attributes": [ + { + "name": "foo", + "value": "bar" + } + ], + "createdAt": 1691588699000, + "lastModifiedAt": 1691588699617 + }, + { + "name": "1phpunit", + "displayName": "A PHPUnit appgroup", + "status": "active", + "attributes": [ + { + "name": "foo", + "value": "bar" + } + ], + "createdAt": 1691588699000, + "lastModifiedAt": 1691588699617 + }, + { + "name": "2phpunit", + "displayName": "A PHPUnit appgroup", + "status": "active", + "attributes": [ + { + "name": "foo", + "value": "bar" + } + ], + "createdAt": 1691588699000, + "lastModifiedAt": 1691588699617 + }, + { + "name": "3phpunit", + "displayName": "A PHPUnit appgroup", + "status": "active", + "attributes": [ + { + "name": "foo", + "value": "bar" + } + ], + "createdAt": 1691588699000, + "lastModifiedAt": 1691588699617 + }, + { + "name": "4phpunit", + "displayName": "A PHPUnit appgroup", + "status": "active", + "attributes": [ + { + "name": "foo", + "value": "bar" + } + ], + "createdAt": 1691588699000, + "lastModifiedAt": 1691588699617 + } + ] +} \ No newline at end of file diff --git a/tests/offline-test-data/v1/organizations/phpunit/appgroups/phpunit/apps/GET_pageSize=1000.json b/tests/offline-test-data/v1/organizations/phpunit/appgroups/phpunit/apps/GET_pageSize=1000.json new file mode 100644 index 00000000..fae49617 --- /dev/null +++ b/tests/offline-test-data/v1/organizations/phpunit/appgroups/phpunit/apps/GET_pageSize=1000.json @@ -0,0 +1,206 @@ +{ + "app": [ + { + "appFamily": "default", + "appId": "a47891c5-ca20-4f2f-975e-0e7588ec81f0", + "attributes": [ + { + "name": "DisplayName", + "value": "PHP Unit: Test app" + }, + { + "name": "Notes", + "value": "This is a test app created by PHP Unit." + }, + { + "name": "foo", + "value": "foo" + }, + { + "name": "bar", + "value": "baz" + } + ], + "callbackUrl": "http://foo.example.com", + "createdAt": 648345600000, + "createdBy": "phpunit@example.com", + "credentials": [ + { + "apiProducts": [], + "attributes": [], + "consumerKey": "Zww6GKaGRxQFGkfE36vgSN0eoac1Ymk3", + "consumerSecret": "LtdYkcmgDYV7kzxz", + "issuedAt": 648345600000, + "scopes": [], + "status": "approved" + } + ], + "appGroup": "phpunit", + "lastModifiedAt": 648345600000, + "lastModifiedBy": "phpunit@example.com", + "name": "phpunit_test_app", + "scopes": [], + "status": "approved" + }, + { + "appFamily": "default", + "appId": "b3e71cbb-7eb5-1111-b384-ac82bca1d9dc", + "attributes": [ + { + "name": "DisplayName", + "value": "PHP Unit: Test app" + }, + { + "name": "Notes", + "value": "This is a test app created by PHP Unit." + }, + { + "name": "foo", + "value": "bar" + } + ], + "callbackUrl": "http://example.com", + "createdAt": 648345600000, + "createdBy": "phpunit@example.com", + "credentials": [ + { + "apiProducts": [], + "attributes": [], + "consumerKey": "Zww6GKaGRxQFGkfE36vgSN0eoac1Ymk3", + "consumerSecret": "LtdYkcmgDYV7kzxz", + "issuedAt": 648345600000, + "scopes": [], + "status": "approved" + } + ], + "appGroup": "phpunit", + "lastModifiedAt": 648345600000, + "lastModifiedBy": "phpunit@example.com", + "name": "1phpunit_test_app", + "scopes": [], + "status": "approved" + }, + { + "appFamily": "default", + "appId": "b3e71cbb-7eb5-2222-b384-ac82bca1d9dc", + "attributes": [ + { + "name": "DisplayName", + "value": "PHP Unit: Test app" + }, + { + "name": "Notes", + "value": "This is a test app created by PHP Unit." + }, + { + "name": "foo", + "value": "foo" + }, + { + "name": "bar", + "value": "baz" + } + ], + "callbackUrl": "http://example.com", + "createdAt": 648345600000, + "createdBy": "phpunit@example.com", + "credentials": [ + { + "apiProducts": [], + "attributes": [], + "consumerKey": "wAXAIiOr2oJOVGqFltnm3Jwr2LE0GEuY", + "consumerSecret": "S8YjnsjmdBqDAegR", + "issuedAt": 648345600000, + "scopes": [], + "status": "approved" + } + ], + "appGroup": "phpunit", + "lastModifiedAt": 648345600000, + "lastModifiedBy": "phpunit@example.com", + "name": "2phpunit_test_app", + "scopes": [], + "status": "approved" + }, + { + "appFamily": "default", + "appId": "b3e71cbb-7eb5-3333-b384-ac82bca1d9dc", + "attributes": [ + { + "name": "DisplayName", + "value": "PHP Unit: Test app" + }, + { + "name": "Notes", + "value": "This is a test app created by PHP Unit." + }, + { + "name": "foo", + "value": "bar" + } + ], + "callbackUrl": "http://example.com", + "createdAt": 648345600000, + "createdBy": "phpunit@example.com", + "credentials": [ + { + "apiProducts": [], + "attributes": [], + "consumerKey": "wAXAIiOr2oJOVGqFltnm3Jwr2LE0GEuY", + "consumerSecret": "S8YjnsjmdBqDAegR", + "issuedAt": 648345600000, + "scopes": [], + "status": "approved" + } + ], + "appGroup": "phpunit", + "lastModifiedAt": 648345600000, + "lastModifiedBy": "phpunit@example.com", + "name": "3phpunit_test_app", + "scopes": [], + "status": "approved" + }, + { + "appFamily": "default", + "appId": "b3e71cbb-7eb5-4444-b384-ac82bca1d9dc", + "attributes": [ + { + "name": "DisplayName", + "value": "PHP Unit: Test app" + }, + { + "name": "Notes", + "value": "This is a test app created by PHP Unit." + }, + { + "name": "foo", + "value": "foo" + }, + { + "name": "bar", + "value": "baz" + } + ], + "callbackUrl": "http://example.com", + "createdAt": 648345600000, + "createdBy": "phpunit@example.com", + "credentials": [ + { + "apiProducts": [], + "attributes": [], + "consumerKey": "wAXAIiOr2oJOVGqFltnm3Jwr2LE0GEuY", + "consumerSecret": "S8YjnsjmdBqDAegR", + "issuedAt": 648345600000, + "scopes": [], + "status": "approved" + } + ], + "appGroup": "phpunit", + "lastModifiedAt": 648345600000, + "lastModifiedBy": "phpunit@example.com", + "name": "4phpunit_test_app", + "scopes": [], + "status": "approved" + } + ] +} \ No newline at end of file From d5925dce96d5bf51b6cdf84c127679247b6fed3c Mon Sep 17 00:00:00 2001 From: Shishir Suvarna Date: Mon, 12 Feb 2024 11:56:23 +0530 Subject: [PATCH 3/4] Updated test case --- composer.json | 3 ++- .../ApigeeX/Controller/AppCredentialControllerTestBase.php | 6 +++--- .../Controller/AppGroupAppCredentialControllerTest.php | 6 +++--- .../v1/organizations/phpunit/appgroups/POST.json | 3 ++- .../v1/organizations/phpunit/appgroups/phpunit/PUT.json | 5 +++-- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index 7f0add89..f4ef884f 100644 --- a/composer.json +++ b/composer.json @@ -73,7 +73,8 @@ "*": "dist" }, "allow-plugins": { - "php-http/discovery": true + "php-http/discovery": true, + "cweagans/composer-patches": true } }, "minimum-stability": "dev", diff --git a/tests/Api/ApigeeX/Controller/AppCredentialControllerTestBase.php b/tests/Api/ApigeeX/Controller/AppCredentialControllerTestBase.php index 5644ddc7..ee4fe3b3 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; @@ -330,9 +330,9 @@ 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 diff --git a/tests/Api/ApigeeX/Controller/AppGroupAppCredentialControllerTest.php b/tests/Api/ApigeeX/Controller/AppGroupAppCredentialControllerTest.php index 85562193..875b47e3 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; @@ -54,7 +54,7 @@ protected static function entityController(ClientInterface $client = null): Enti 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..fb9d96d3 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", @@ -21,4 +22,4 @@ "createdBy": "phpunit@example.com", "lastModifiedAt": 1691588699000, "lastModifiedBy": "phpunit@example.com" -} +} \ No newline at end of file From 304e3fc4c9ce02193517198e28f28f4d2a540296 Mon Sep 17 00:00:00 2001 From: Shishir Suvarna Date: Mon, 12 Feb 2024 14:10:09 +0530 Subject: [PATCH 4/4] Removing cweagans/composer-patches as its not needed --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index f4ef884f..7f0add89 100644 --- a/composer.json +++ b/composer.json @@ -73,8 +73,7 @@ "*": "dist" }, "allow-plugins": { - "php-http/discovery": true, - "cweagans/composer-patches": true + "php-http/discovery": true } }, "minimum-stability": "dev",