Skip to content

Commit

Permalink
Merging AppGroup changes into 2.x branch (#869)
Browse files Browse the repository at this point in the history
* Checking if the organisation is ApigeeX for AppGroup (#803)

* Fix organisation exception (#806)

* Changes for Appgroup listing/view/edit/delete page (#805)

* Changes for Appgroup listing/view/edit/delete page

* Update composer.json

* Update composer.json

* AppGroup-App implementation for list, view, edit, delete apps (#813)

* Merge 2.x into appgroup (#829)

* Warrant that queues executed by cron (#808)

* Sort API Product listings on App (#812)

* Update endpoints to use constants from ClientInterface (#810)

* Sort API products on app forms by API product display name (#815)

* Sort API Products (#818)

* Sort API Products
TypeError: array_map(): Argument #2 ($array) must be of type array

* Removed test case

* App creation fails without API Products (#809)

* App creation fails without API Products caused by features.keymanagement.disable.unbounded.permissions flag
* Update minimum version of apigee-client-php

* Make sure that cron runner starts executing the job (#814)

* Checking if the organisation is ApigeeX for AppGroup (#803)

* Fix organisation exception (#806)

* Changes for Appgroup listing/view/edit/delete page (#805)

* Changes for Appgroup listing/view/edit/delete page

* Update composer.json

* Update composer.json

* AppGroup-App implementation for list, view, edit, delete apps (#813)

---------

Co-authored-by: Dezső BICZÓ <[email protected]>
Co-authored-by: Shishir <[email protected]>
Co-authored-by: phdhiren <[email protected]>
Co-authored-by: Divyajose <[email protected]>

* Support for Add AppGroup/team and update team App credentials with ApiProduct (#824)

* Support for AppGroup membership (#825)

* The Apigee Edge and X Teams module requirements (#826)

* Checking ApigeeX org from OrganizationController and removed from SDKController (#832)

* If team not available on Apigee avoid null value exception (#833)

* Added admin config setting for Team prefix and channelID (#839)

* Implemented AppGroup membership feature for ApigeeX (#843)

* Merge 2.x into appgroup branch (#859)

* Warrant that queues executed by cron (#808)

* Sort API Product listings on App (#812)

* Update endpoints to use constants from ClientInterface (#810)

* Sort API products on app forms by API product display name (#815)

* Sort API Products (#818)

* Sort API Products
TypeError: array_map(): Argument #2 ($array) must be of type array

* Removed test case

* App creation fails without API Products (#809)

* App creation fails without API Products caused by features.keymanagement.disable.unbounded.permissions flag
* Update minimum version of apigee-client-php

* Make sure that cron runner starts executing the job (#814)

* Update the storage defination of App entity (#831)

* Fix issue which removes users from all team (#840)

* Drop PHP 7 support again (#816)

* Revert "Support for PHP 7.4 (#800)"

This reverts commit b71f8a9.

* Clearly specify the currently supported PHP versions

* Update outdated info.yml

* Added accessCheck on entity queries (#842)

* added accessCheck on entity queries

Co-authored-by: Vladimir Roudakov <[email protected]>

* added comment

---------

Co-authored-by: Vladimir Roudakov <[email protected]>

* Fix for jQuery.once() is deprecated in Drupal 9.3 (#851)

Co-authored-by: Vladimir Roudakov <[email protected]>

* fix for phpcs failing in github action (#858)

* PHPCS FIX

---------
Co-authored-by: Shishir <[email protected]>
Co-authored-by: Dezső BICZÓ <[email protected]>
Co-authored-by: Gitesh Koli <[email protected]>
Co-authored-by: Divyajose <[email protected]>
Co-authored-by: Vladimir Roudakov <[email protected]>

* Implemented AppGroup Team Membership Sync (#853)

* Get AppGroup team members from DB (#864)

* Get AppGroup team members from DB and fix recurring teams api call

* Declaring abstract method

* Fix action module getTeam method argument

* Fix for team sync issue when DB is empty

* Added team member sync note for ApigeeX (#867)

---------

Co-authored-by: Shishir <[email protected]>
Co-authored-by: Divyajose <[email protected]>
Co-authored-by: Gitesh Koli <[email protected]>
Co-authored-by: Dezső BICZÓ <[email protected]>
Co-authored-by: Vladimir Roudakov <[email protected]>
  • Loading branch information
5 people committed Jun 23, 2023
1 parent 386c83f commit ea6a45f
Show file tree
Hide file tree
Showing 52 changed files with 1,628 additions and 104 deletions.
11 changes: 9 additions & 2 deletions modules/apigee_edge_actions/src/TeamMembershipManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,15 @@ public function removeMembers(string $team, array $developers): void {
/**
* {@inheritdoc}
*/
public function getTeams(string $developer): array {
return $this->inner->getTeams($developer);
public function getTeams(string $developer, string $team = NULL): array {
return $this->inner->getTeams($developer, $team);
}

/**
* {@inheritdoc}
*/
public function syncAppGroupMembers(string $team): array {
return $this->inner->syncAppGroupMembers($team);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ protected function setUp(): void {
parent::setUp();

$this->installEntitySchema('team_member_role');

$this->addOrganizationMatchedResponse();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ protected function setUp(): void {
parent::setUp();

$this->installEntitySchema('team_member_role');

$this->addOrganizationMatchedResponse();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ public function testEvent() {
]);
$config_entity->save();

// Add matched organization response so it returns the org whenever called.
$this->addOrganizationMatchedResponse();

// Insert and update entity.
/** @var \Drupal\apigee_edge\Entity\DeveloperAppInterface $entity */
$entity = $this->createDeveloperApp();
Expand Down
8 changes: 4 additions & 4 deletions modules/apigee_edge_teams/apigee_edge_teams.install
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use Drupal\user\RoleInterface;
use Drupal\views\Views;

/**
* Install, update and uninstall functions for Apigee Edge Teams.
* Install, update and uninstall functions for Apigee Edge and ApigeeX Teams.
*/

/**
Expand All @@ -41,13 +41,13 @@ function apigee_edge_teams_requirements($phase) {
$org_controller = \Drupal::service('apigee_edge.controller.organization');
/* @var \Apigee\Edge\Api\Management\Entity\Organization $organization */
$organization = $org_controller->load($sdk_connector->getOrganization());
if ($organization && !OrganizationFeatures::isCompaniesFeatureAvailable($organization)) {
if ($organization && !OrganizationFeatures::isCompaniesFeatureAvailable($organization) && OrganizationFeatures::isMonetizationEnabled($organization)) {
$url = [
':url' => 'https://cloud.google.com/apigee/docs/api-platform/get-started/compare-apigee-products#unsupported-apis',
];
$message = ($phase == 'runtime') ?
t("The Apigee Edge Teams module functionality is not available for your org and should be uninstalled, because <a href=':url' target='_blank'>Edge company APIs are not supported in Apigee X and Apigee Hybrid orgs</a>.", $url) :
t("The Apigee Edge Teams module functionality is not available for your org because <a href=':url' target='_blank'>Edge company APIs are not supported in Apigee X and Apigee Hybrid orgs</a>.", $url);
t("The Teams module functionality is not available for monetization enabled org on Apigee X / Hybrid and should be uninstalled, because <a href=':url' target='_blank'>AppGroup APIs are not supported in Apigee X / Hybrid orgs with monetization enabled</a>.", $url) :
t("The Teams module functionality is not available for monetization enabled org on Apigee X / Hybrid because <a href=':url' target='_blank'>AppGroup APIs are not supported in Apigee X / Hybrid orgs with monetization enabled</a>.", $url);
$requirements['apigee_edge_teams_not_supported'] = [
'title' => t('Apigee Edge Teams'),
'description' => $message,
Expand Down
2 changes: 1 addition & 1 deletion modules/apigee_edge_teams/apigee_edge_teams.links.task.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
apigee_edge_teams.settings.team:
route_name: apigee_edge_teams.settings.team
title: 'Alias'
title: 'Settings'
base_route: apigee_edge_teams.settings.team
weight: -10

Expand Down
16 changes: 14 additions & 2 deletions modules/apigee_edge_teams/apigee_edge_teams.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ services:
class: Drupal\apigee_edge_teams\CompanyMembershipObjectCache
arguments: ['@cache_factory', '@apigee_edge.cache.memory_cache_factory', '@config.factory', '@datetime.time']

apigee_edge_teams.cache.appgroup_membership_object:
class: Drupal\apigee_edge_teams\AppGroupMembershipObjectCache
arguments: ['@cache_factory', '@apigee_edge.cache.memory_cache_factory', '@config.factory', '@datetime.time']

apigee_edge.controller.cache.appgroup_teams:
class: Drupal\apigee_edge_teams\AppGroupCache
arguments: ['@apigee_edge.cache.memory_cache_factory']

apigee_edge_teams.controller.team:
class: Drupal\apigee_edge_teams\Entity\Controller\TeamController
arguments: ['@apigee_edge.sdk_connector', '@apigee_edge.controller.organization', '@apigee_edge_teams.controller.cache.team', '@apigee_edge_teams.controller.cache.team_ids', '@apigee_edge.entity.controller.cache.app_cache_by_owner_factory', '@apigee_edge.entity.controller.cache.app_name_cache_by_owner_factory', '@apigee_edge_teams.cache.company_membership_object', '@apigee_edge.controller.cache.developer_companies']
Expand All @@ -40,6 +48,10 @@ services:
class: Drupal\apigee_edge_teams\CompanyMembersControllerFactory
arguments: ['@apigee_edge.sdk_connector', '@apigee_edge_teams.cache.company_membership_object']

apigee_edge_teams.appgroup_members_controller_factory:
class: Drupal\apigee_edge_teams\AppGroupMembersControllerFactory
arguments: ['@apigee_edge.sdk_connector', '@apigee_edge_teams.cache.appgroup_membership_object']

apigee_edge_teams.controller.cache.team:
class: Drupal\apigee_edge\Entity\Controller\Cache\EntityCache
arguments: ['@apigee_edge_teams.cache.memory_cache_factory', '@apigee_edge_teams.controller.cache.team_ids', team]
Expand All @@ -49,7 +61,7 @@ services:

apigee_edge_teams.team_membership_manager:
class: Drupal\apigee_edge_teams\TeamMembershipManager
arguments: [ '@entity_type.manager', '@apigee_edge_teams.company_members_controller_factory', '@apigee_edge.controller.developer', '@apigee_edge.controller.cache.developer_companies', '@cache_tags.invalidator', '@logger.channel.apigee_edge_teams']
arguments: [ '@entity_type.manager', '@apigee_edge_teams.company_members_controller_factory','@apigee_edge_teams.appgroup_members_controller_factory', '@apigee_edge.controller.developer', '@apigee_edge.controller.cache.developer_companies', '@cache_tags.invalidator', '@logger.channel.apigee_edge_teams', '@apigee_edge.controller.organization']

apigee_edge_teams.team_permissions:
class: Drupal\apigee_edge_teams\TeamPermissionHandler
Expand Down Expand Up @@ -85,7 +97,7 @@ services:

apigee_edge_teams.event_subscriber.team_invitation_event_subscriber:
class: Drupal\apigee_edge_teams\EventSubscriber\TeamInvitationSubscriber
arguments: ['@logger.channel.apigee_edge_teams', '@entity_type.manager', '@apigee_edge_teams.team_membership_manager', '@apigee_edge_teams.team_invitation_notifier.email']
arguments: ['@logger.channel.apigee_edge_teams', '@entity_type.manager', '@apigee_edge_teams.team_membership_manager', '@apigee_edge_teams.team_invitation_notifier.email', '@apigee_edge.controller.organization']
tags:
- { name: event_subscriber }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
langcode: en
team_prefix: ''
channelid: ''
entity_label_singular: ''
entity_label_plural: ''
cache_expiration: 900
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ apigee_edge_teams.team_settings:
type: config_object
label: 'Team settings'
mapping:
team_prefix:
type: label
label: 'Prefix to add for the Team name'
channelid:
type: label
label: 'ChannelId settings'
entity_label_singular:
type: label
label: 'How to refer to a Team on the UI (singular)'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ public function access(RouteMatchInterface $route_match, AccountInterface $accou
// If the developer parameter is available in the route make sure it is
// member of the team.
if ($developer !== NULL) {
if (!in_array($team->id(), $this->teamMembershipManager->getTeams($developer->getEmail()))) {
// Argument #2 in getTeams() is required for checking the AppGroup members and not required for Edge.
if (!in_array($team->id(), $this->teamMembershipManager->getTeams($developer->getEmail(), $team->id()))) {
return AccessResultForbidden::forbidden("The {$developer->getEmail()} developer is not member of the {$team->id()} team.");
}
}
Expand Down
209 changes: 209 additions & 0 deletions modules/apigee_edge_teams/src/AppGroupMembersController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
<?php

/**
* Copyright 2023 Google Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/

namespace Drupal\apigee_edge_teams;

use Apigee\Edge\Api\ApigeeX\Controller\AppGroupMembersController as ApigeeXAppGroupMembersController;
use Apigee\Edge\Api\ApigeeX\Structure\AppGroupMembership;
use Drupal\apigee_edge\Entity\Controller\OrganizationController;
use Drupal\apigee_edge\SDKConnectorInterface;
use Drupal\apigee_edge_teams\Entity\Form\TeamForm;
use Drupal\apigee_edge_teams\Entity\TeamInvitationInterface;
use Drupal\apigee_edge_teams\Entity\TeamMemberRoleInterface;
use Drupal\Component\Serialization\Json;

/**
* Definition of the AppGroup members controller service.
*
* @internal You should use the team membership manager service instead of this.
*/
final class AppGroupMembersController implements AppGroupMembersControllerInterface {

/**
* Name of a appgroup.
*
* @var string
*/
private $appGroup;

/**
* The SDK connector service.
*
* @var \Drupal\apigee_edge\SDKConnectorInterface
*/
private $connector;

/**
* The appgroup membership object cache.
*
* @var \Drupal\apigee_edge_teams\AppGroupMembershipObjectCacheInterface
*/
private $appGroupMembershipObjectCache;

/**
* AppGroupMembersController constructor.
*
* @param string $appGroup
* The name of the appgroup.
* @param \Drupal\apigee_edge\SDKConnectorInterface $connector
* The SDK connector service.
* @param \Drupal\apigee_edge_teams\AppGroupMembershipObjectCacheInterface $appgroup_membership_object_cache
* The appgroup membership object cache.
*/
public function __construct(string $appGroup, SDKConnectorInterface $connector, AppGroupMembershipObjectCacheInterface $appgroup_membership_object_cache) {
$this->appGroup = $appGroup;
$this->connector = $connector;
$this->appGroupMembershipObjectCache = $appgroup_membership_object_cache;
}

/**
* Returns the decorated appgroup members controller from the SDK.
*
* @return \Apigee\Edge\Api\ApigeeX\Controller\AppGroupMembersController
* The initialized appgroup members controller.
*/
private function decorated(): ApigeeXAppGroupMembersController {
return new ApigeeXAppGroupMembersController($this->appGroup, $this->connector->getOrganization(), $this->connector->getClient());
}

/**
* {@inheritdoc}
*/
public function getAppGroup(): string {
return $this->decorated()->getAppGroup();
}

/**
* Syncs the appgroup team members email and roles in database.
*
* @return \Apigee\Edge\Api\ApigeeX\Structure\AppGroupMembership
* Array of developers with their optional roles in the appgroup.
*/
public function syncAppGroupMembers(): AppGroupMembership {
$membership = $this->decorated()->getMembers();

$developer_exist = TRUE;
foreach ($membership->getMembers() as $developer_id => $roles) {
$roles = is_array($roles) ? $roles : [];

/** @var \Drupal\user\Entity\User $account */
$account = user_load_by_mail($developer_id);

if (!$account) {
$developer_exist = FALSE;
/** @var \Drupal\apigee_edge_teams\Entity\Storage\TeamInvitationStorageInterface $teamInvitationStorage */
$teamInvitationStorage = \Drupal::entityTypeManager()->getStorage('team_invitation');

$pending_invitations = array_filter($teamInvitationStorage->loadByRecipient($developer_id, $this->appGroup), function (TeamInvitationInterface $team_invitation) {
return $team_invitation->isPending();
});

// Checking if this developer has already pending invite to prevent multiple invites.
if (count($pending_invitations) === 0) {
$teamInvitationStorage->create([
'team' => ['target_id' => $this->appGroup],
'team_roles' => array_values(array_map(function (string $role) {
return ['target_id' => $role];
}, $roles)),
'recipient' => $developer_id,
])->save();
}
}
else {
/** @var \Drupal\apigee_edge_teams\Entity\TeamInterface|null $team_entity */
$team_entity = \Drupal::entityTypeManager()->getStorage('team')->load($this->appGroup);
/** @var \Drupal\apigee_edge_teams\Entity\Storage\TeamMemberRoleStorageInterface $team_member_role_storage */
$team_member_role_storage = \Drupal::entityTypeManager()->getStorage('team_member_role');
$team_member_role_storage->addTeamRoles($account, $team_entity, $roles);
}
}
// Caching the membership only if all the developer exists in Drupal
// to avoid team member without access.
if ($developer_exist) {
$this->appGroupMembershipObjectCache->saveMembership($this->appGroup, $membership);
}

return $membership;
}

/**
* {@inheritdoc}
*/
public function getMembers(): AppGroupMembership {
$membership = $this->appGroupMembershipObjectCache->getMembership($this->appGroup);
if ($membership === NULL) {
/** @var \Drupal\apigee_edge_teams\Entity\TeamInterface|null $team_entity */
$team_entity = \Drupal::entityTypeManager()->getStorage('team')->load($this->appGroup);
// Load team_member_role object.
$team_member_role_storage = \Drupal::entityTypeManager()->getStorage('team_member_role');
$members = array_reduce($team_member_role_storage->loadByTeam($team_entity), function ($carry, TeamMemberRoleInterface $developer_role) {
$carry[$developer_role->getDeveloper()->getEmail()] = $developer_role->getDeveloper()->getEmail();
return $carry;
},
[]);
$membership = new AppGroupMembership($members);
$this->appGroupMembershipObjectCache->saveMembership($team_entity->id(), $membership);
}

return $membership;
}

/**
* {@inheritdoc}
*/
public function setMembers(AppGroupMembership $members): AppGroupMembership {
// Get the existing members and roles from ApigeeX.
$apigeeReservedMembers = $this->decorated()->getMembers();
// Getting all developers including the newly added one.
$developers = array_merge($apigeeReservedMembers->getMembers(), $members->getMembers());

foreach ($developers as $developer => $roles) {
$members->setMember($developer, $roles);
}
// Storing membership info on appgroups __apigee_reserved__developer_details attribute for ApigeeX.
$result = $this->decorated()->setMembers($members);

// Returned membership does not contain all actual members of the appGroup,
// so it is easier to remove the membership object from the cache and
// enforce reload in getMembers().
$this->appGroupMembershipObjectCache->removeMembership($this->appGroup);
return $result;
}

/**
* {@inheritdoc}
*/
public function removeMember(string $email): void {
// Get the existing members and roles from ApigeeX.
$apigeeReservedMembers = $this->decorated()->getMembers();

foreach ($apigeeReservedMembers->getMembers() as $developer => $roles) {
if ($developer === $email) {
$apigeeReservedMembers->removeMember($developer);
}
}
// Storing membership info on appgroups __apigee_reserved__developer_details attribute for ApigeeX.
// Removing and updating the member on appgroups __apigee_reserved__developer_details attribute for ApigeeX.
$result = $this->decorated()->setMembers($apigeeReservedMembers);

$this->appGroupMembershipObjectCache->removeMembership($this->appGroup);
}

}
Loading

0 comments on commit ea6a45f

Please sign in to comment.