From 7cf54da7dc697e7a26aa425b1a93b25c39c34f57 Mon Sep 17 00:00:00 2001 From: Divyajose Date: Tue, 18 Oct 2022 14:03:01 +0530 Subject: [PATCH 1/4] Team member sync --- .../apigee_edge_teams.links.task.yml | 5 + .../apigee_edge_teams.routing.yml | 24 ++ .../apigee_edge_teams.services.yml | 4 + modules/apigee_edge_teams/composer.json | 9 +- modules/apigee_edge_teams/drush.services.yml | 6 + modules/apigee_edge_teams/src/CliService.php | 73 ++++++ .../src/CliServiceInterface.php | 39 ++++ .../src/Commands/ApigeeEdgeCommands.php | 61 +++++ .../Controller/TeamMemberSyncController.php | 209 ++++++++++++++++++ .../src/Form/TeamMemberSyncForm.php | 155 +++++++++++++ .../src/Job/TeamMemberCreateUpdate.php | 56 +++++ .../src/Job/TeamMemberSync.php | 79 +++++++ .../src/Job/TeamMemberUpdate.php | 36 +++ 13 files changed, 755 insertions(+), 1 deletion(-) create mode 100644 modules/apigee_edge_teams/drush.services.yml create mode 100644 modules/apigee_edge_teams/src/CliService.php create mode 100644 modules/apigee_edge_teams/src/CliServiceInterface.php create mode 100644 modules/apigee_edge_teams/src/Commands/ApigeeEdgeCommands.php create mode 100644 modules/apigee_edge_teams/src/Controller/TeamMemberSyncController.php create mode 100644 modules/apigee_edge_teams/src/Form/TeamMemberSyncForm.php create mode 100644 modules/apigee_edge_teams/src/Job/TeamMemberCreateUpdate.php create mode 100644 modules/apigee_edge_teams/src/Job/TeamMemberSync.php create mode 100644 modules/apigee_edge_teams/src/Job/TeamMemberUpdate.php diff --git a/modules/apigee_edge_teams/apigee_edge_teams.links.task.yml b/modules/apigee_edge_teams/apigee_edge_teams.links.task.yml index f06a1608f..72c8815ad 100644 --- a/modules/apigee_edge_teams/apigee_edge_teams.links.task.yml +++ b/modules/apigee_edge_teams/apigee_edge_teams.links.task.yml @@ -106,3 +106,8 @@ apigee_edge_teams.team_app.analytics: title: 'Analytics' base_route: entity.team_app.canonical weight: -1 + +apigee_edge_teams.settings.team_member.sync: + route_name: apigee_edge_teams.settings.team_member.sync + title: 'Sync' + base_route: apigee_edge_teams.settings.team diff --git a/modules/apigee_edge_teams/apigee_edge_teams.routing.yml b/modules/apigee_edge_teams/apigee_edge_teams.routing.yml index 26ac0555c..09bc81879 100644 --- a/modules/apigee_edge_teams/apigee_edge_teams.routing.yml +++ b/modules/apigee_edge_teams/apigee_edge_teams.routing.yml @@ -71,3 +71,27 @@ apigee_edge_teams.settings.team_app.cache: _title: 'Caching' requirements: _permission: 'administer team' + +apigee_edge_teams.settings.team_member.sync: + path: '/admin/config/apigee-edge/app-settings/team-settings/sync' + defaults: + _form: '\Drupal\apigee_edge_teams\Form\TeamMemberSyncForm' + _title: 'Team Memeber Synchronization' + requirements: + _permission: 'administer team' + +apigee_edge_teams.team_member.run: + path: '/admin/config/apigee-edge/app-settings/team-settings/sync/run' + defaults: + _controller: '\Drupal\apigee_edge_teams\Controller\TeamMemberSyncController::run' + requirements: + _permission: 'administer team' + _csrf_token: 'TRUE' + +apigee_edge_teams.team_member.schedule: + path: '/admin/config/apigee-edge/app-settings/team-settings/sync/schedule' + defaults: + _controller: '\Drupal\apigee_edge_teams\Controller\TeamMemberSyncController::schedule' + requirements: + _permission: 'administer team' + _csrf_token: 'TRUE' diff --git a/modules/apigee_edge_teams/apigee_edge_teams.services.yml b/modules/apigee_edge_teams/apigee_edge_teams.services.yml index 6b304a35c..5246435ec 100644 --- a/modules/apigee_edge_teams/apigee_edge_teams.services.yml +++ b/modules/apigee_edge_teams/apigee_edge_teams.services.yml @@ -130,3 +130,7 @@ services: arguments: ['@entity_type.manager', '@logger.channel.apigee_edge_teams'] tags: - { name: paramconverter } + + apigee_edge_teams.cli: + class: Drupal\apigee_edge_teams\CliService + arguments: ['@apigee_edge.apigee_edge_mgmt_cli_service'] diff --git a/modules/apigee_edge_teams/composer.json b/modules/apigee_edge_teams/composer.json index 50d6e9dbf..ca2e104f4 100644 --- a/modules/apigee_edge_teams/composer.json +++ b/modules/apigee_edge_teams/composer.json @@ -11,5 +11,12 @@ "sort-packages": true }, "minimum-stability": "dev", - "prefer-stable": true + "prefer-stable": true, + "extra": { + "drush": { + "services": { + "drush.services.yml": "^9" + } + } + } } diff --git a/modules/apigee_edge_teams/drush.services.yml b/modules/apigee_edge_teams/drush.services.yml new file mode 100644 index 000000000..60f1a3f73 --- /dev/null +++ b/modules/apigee_edge_teams/drush.services.yml @@ -0,0 +1,6 @@ +services: + apigee_edge_teams.commands: + class: \Drupal\apigee_edge_teams\Commands\ApigeeEdgeCommands + arguments: ['@apigee_edge_teams.cli', '@apigee_edge.apigee_edge_mgmt_cli_service'] + tags: + - { name: drush.command } diff --git a/modules/apigee_edge_teams/src/CliService.php b/modules/apigee_edge_teams/src/CliService.php new file mode 100644 index 000000000..3f5a8d5cb --- /dev/null +++ b/modules/apigee_edge_teams/src/CliService.php @@ -0,0 +1,73 @@ +apigeeEdgeManagementCliService = $apigeeEdgeManagementCliService; + } + + /** + * {@inheritdoc} + */ + public function sync(StyleInterface $io, callable $t) { + $io->title($t('Team Member synchronization')); + $batch = TeamMemberSyncController::getBatch(); + $last_message = ''; + + foreach ($batch['operations'] as $operation) { + $context = [ + 'finished' => 0, + ]; + + while ($context['finished'] < 1) { + call_user_func_array($operation[0], array_merge($operation[1], [&$context])); + if (isset($context['message']) && $context['message'] !== $last_message) { + $io->text($t($context['message'])); + } + $last_message = $context['message']; + + gc_collect_cycles(); + } + } + } + +} diff --git a/modules/apigee_edge_teams/src/CliServiceInterface.php b/modules/apigee_edge_teams/src/CliServiceInterface.php new file mode 100644 index 000000000..9f4c4d0c2 --- /dev/null +++ b/modules/apigee_edge_teams/src/CliServiceInterface.php @@ -0,0 +1,39 @@ +cliService = $cli_service; + } + + /** + * Team Member synchronization. + * + * @command apigee-edge-teams:sync + * + * @usage drush apigee-edge-teams:sync + * Starts the team member synchronization. + */ + public function sync() { + $this->cliService->sync($this->io(), 'dt'); + } + +} diff --git a/modules/apigee_edge_teams/src/Controller/TeamMemberSyncController.php b/modules/apigee_edge_teams/src/Controller/TeamMemberSyncController.php new file mode 100644 index 000000000..948d8222b --- /dev/null +++ b/modules/apigee_edge_teams/src/Controller/TeamMemberSyncController.php @@ -0,0 +1,209 @@ +executor = $executor; + $this->messenger = $messenger; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('apigee_edge.job_executor'), + $container->get('messenger') + ); + } + + /** + * Generates a job tag. + * + * @param string $type + * Tag type. + * + * @return string + * Job tag. + */ + protected static function generateTag(string $type): string { + return "team_member_sync_{$type}_" . \Drupal::service('password_generator')->generate(); + } + + /** + * Returns the team member sync filter. + * + * @return null|string + * Filter condition or null if not set. + */ + protected static function getFilter(): ?string { + return ((string) \Drupal::config('apigee_edge.sync')->get('filter')) ?: NULL; + } + + /** + * Handler for 'apigee_edge_teams.team_member.schedule'. + * + * Runs a team member sync in the background. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The HTTP request. + * + * @return \Symfony\Component\HttpFoundation\RedirectResponse + * HTTP response doing a redirect. + */ + public function schedule(Request $request): RedirectResponse { + $destination = $request->query->get('destination'); + + $job = new TeamMemberSync(static::getFilter()); + $job->setTag($this->generateTag('background')); + apigee_edge_get_executor()->cast($job); + + $this->messenger()->addStatus($this->t('Team Member synchronization is scheduled.')); + + return new RedirectResponse($destination); + } + + /** + * Handler for 'apigee_edge_teams.team_member.run'. + * + * Starts the team member sync batch process. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The HTTP request. + * + * @return \Symfony\Component\HttpFoundation\RedirectResponse + * HTTP response doing a redirect. + */ + public function run(Request $request): RedirectResponse { + $destination = $request->query->get('destination'); + $batch = static::getBatch(); + batch_set($batch); + return batch_process($destination); + } + + /** + * Gets the batch array. + * + * @return array + * The batch array. + */ + public static function getBatch(): array { + $tag = static::generateTag('batch'); + + return [ + 'title' => t('Synchronizing Team Member'), + 'operations' => [ + [[static::class, 'batchGenerateJobs'], [$tag]], + [[static::class, 'batchExecuteJobs'], [$tag]], + ], + 'finished' => [static::class, 'batchFinished'], + ]; + } + + /** + * The first batch operation. + * + * This generates the team member sync jobs for the second operation. + * + * @param string $tag + * Job tag. + * @param array $context + * Batch context. + */ + public static function batchGenerateJobs(string $tag, array &$context) { + $job = new TeamMemberSync(static::getFilter()); + $job->setTag($tag); + apigee_edge_get_executor()->call($job); + + $context['message'] = (string) $job; + $context['finished'] = 1.0; + } + + /** + * The second batch operation. + * + * @param string $tag + * Job tag. + * @param array $context + * Batch context. + */ + public static function batchExecuteJobs(string $tag, array &$context) { + if (!isset($context['sandbox'])) { + $context['sandbox'] = []; + } + + $executor = apigee_edge_get_executor(); + $job = $executor->select($tag); + + if ($job === NULL) { + $context['finished'] = 1.0; + return; + } + + $executor->call($job); + + $context['message'] = (string) $job; + $context['finished'] = $executor->countJobs($tag, [Job::FAILED, Job::FINISHED]) / $executor->countJobs($tag); + } + + /** + * Batch finish callback. + */ + public static function batchFinished() { + \Drupal::messenger()->addStatus(t('Team members are synced in Drupal')); + } + +} diff --git a/modules/apigee_edge_teams/src/Form/TeamMemberSyncForm.php b/modules/apigee_edge_teams/src/Form/TeamMemberSyncForm.php new file mode 100644 index 000000000..da13940f8 --- /dev/null +++ b/modules/apigee_edge_teams/src/Form/TeamMemberSyncForm.php @@ -0,0 +1,155 @@ +sdkConnector = $sdk_connector; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('apigee_edge.sdk_connector') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'apigee_edge_team_member_sync_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + try { + $this->sdkConnector->testConnection(); + } + catch (\Exception $exception) { + $this->messenger()->addError($this->t('Cannot connect to Apigee Edge server. Please ensure that Apigee Edge connection settings are correct.', [ + ':link' => Url::fromRoute('apigee_edge.settings')->toString(), + ])); + return $form; + } + + $form['#attached']['library'][] = 'apigee_edge/apigee_edge.admin'; + + $form['sync'] = [ + '#type' => 'details', + '#title' => $this->t('Synchronize team members'), + '#open' => TRUE, + ]; + + $form['sync']['description'] = [ + '#type' => 'container', + 'p1' => [ + '#type' => 'html_tag', + '#tag' => 'p', + '#value' => $this->t('Team member synchronization will:'), + ], + 'list' => [ + '#theme' => 'item_list', + '#items' => [ + $this->t('Store the team members in Drupal'), + ], + ], + 'p2' => [ + '#type' => 'html_tag', + '#tag' => 'p', + '#value' => $this->t('The "Run team member sync" button will sync the team members, displaying a progress bar on the screen while running. The "Background team member sync" button will run the team member sync process in batches each time cron runs and may take multiple cron runs to complete.', [':cron_url' => Url::fromRoute('system.cron_settings')->toString()]), + ], + ]; + + $form['sync']['sync_submit'] = [ + '#title' => $this->t('Run team member sync'), + '#type' => 'link', + '#url' => $this->buildUrl('apigee_edge_teams.team_member.run'), + '#attributes' => [ + 'class' => [ + 'button', + 'button--primary', + ], + ], + ]; + $form['sync']['background_team_member_sync_submit'] = [ + '#title' => $this->t('Background team member sync'), + '#type' => 'link', + '#url' => $this->buildUrl('apigee_edge_teams.team_member.schedule'), + '#attributes' => [ + 'class' => [ + 'button', + ], + ], + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + } + + /** + * Build URL for team member sync processes, using CSRF protection. + * + * @param string $route_name + * The name of the route. + * + * @return \Drupal\Core\Url + * The URL to redirect to. + */ + protected function buildUrl(string $route_name): Url { + $url = Url::fromRoute($route_name); + $token = \Drupal::csrfToken()->get($url->getInternalPath()); + $url->setOptions(['query' => ['destination' => 'admin/config/apigee-edge/app-settings/team-settings/sync', 'token' => $token]]); + return $url; + } + +} diff --git a/modules/apigee_edge_teams/src/Job/TeamMemberCreateUpdate.php b/modules/apigee_edge_teams/src/Job/TeamMemberCreateUpdate.php new file mode 100644 index 000000000..29d33331a --- /dev/null +++ b/modules/apigee_edge_teams/src/Job/TeamMemberCreateUpdate.php @@ -0,0 +1,56 @@ +team_ids = $team_ids; + } + + /** + * {@inheritdoc} + */ + protected function executeRequest() { + $member_controller = \Drupal::service('apigee_edge_teams.team_membership_manager'); + $team_members = $member_controller->getMembers($this->team_ids); + } + +} diff --git a/modules/apigee_edge_teams/src/Job/TeamMemberSync.php b/modules/apigee_edge_teams/src/Job/TeamMemberSync.php new file mode 100644 index 000000000..24bab27f2 --- /dev/null +++ b/modules/apigee_edge_teams/src/Job/TeamMemberSync.php @@ -0,0 +1,79 @@ +filter = $filter; + } + + /** + * Executes the request itself. + */ + protected function executeRequest(){} + + /** + * {@inheritdoc} + */ + public function execute(): bool { + parent::execute(); + + $team_ids = array_keys(\Drupal::entityTypeManager()->getStorage('team')->loadMultiple()); + + foreach ($team_ids as $team_name) { + $update_team_member_job = new TeamMemberUpdate($team_name); + $update_team_member_job->setTag($this->getTag()); + $this->scheduleJob($update_team_member_job); + } + + return FALSE; + } + + /** + * {@inheritdoc} + */ + public function __toString(): string { + return t('Synchronizing Team Members in Drupal.')->render(); + } + +} diff --git a/modules/apigee_edge_teams/src/Job/TeamMemberUpdate.php b/modules/apigee_edge_teams/src/Job/TeamMemberUpdate.php new file mode 100644 index 000000000..c7547924d --- /dev/null +++ b/modules/apigee_edge_teams/src/Job/TeamMemberUpdate.php @@ -0,0 +1,36 @@ + $this->team_ids, + ])->render(); + } + +} From 4e36a8fe7b516a02c3c13f81f40a11099fc884a1 Mon Sep 17 00:00:00 2001 From: Divyajose <75604843+divya-intelli@users.noreply.github.com> Date: Thu, 20 Oct 2022 13:46:54 +0530 Subject: [PATCH 2/4] Typo --- modules/apigee_edge_teams/apigee_edge_teams.routing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/apigee_edge_teams/apigee_edge_teams.routing.yml b/modules/apigee_edge_teams/apigee_edge_teams.routing.yml index 09bc81879..ecfa0df33 100644 --- a/modules/apigee_edge_teams/apigee_edge_teams.routing.yml +++ b/modules/apigee_edge_teams/apigee_edge_teams.routing.yml @@ -76,7 +76,7 @@ apigee_edge_teams.settings.team_member.sync: path: '/admin/config/apigee-edge/app-settings/team-settings/sync' defaults: _form: '\Drupal\apigee_edge_teams\Form\TeamMemberSyncForm' - _title: 'Team Memeber Synchronization' + _title: 'Team Member Synchronization' requirements: _permission: 'administer team' From 5033a1aae44069c1f7a0b78e1f14e17f20a9b313 Mon Sep 17 00:00:00 2001 From: Divyajose Date: Tue, 1 Nov 2022 16:13:37 +0530 Subject: [PATCH 3/4] fix to show more than 100 teams --- modules/apigee_edge_teams/src/Entity/TeamAccessHandler.php | 2 +- modules/apigee_edge_teams/src/TeamPermissionHandler.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/apigee_edge_teams/src/Entity/TeamAccessHandler.php b/modules/apigee_edge_teams/src/Entity/TeamAccessHandler.php index 82028d3b7..f51335a78 100644 --- a/modules/apigee_edge_teams/src/Entity/TeamAccessHandler.php +++ b/modules/apigee_edge_teams/src/Entity/TeamAccessHandler.php @@ -108,7 +108,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter // Check if current developer is a member of the team and has the permision // to view more than 100 teams. // Should not run for developer with less than 100 teams. - if ($account->hasPermission('view extensive team list') && (count($developer_team_ids) > 100)) { + if ($account->hasPermission('view extensive team list') && (count($developer_team_ids) >= 100)) { $team_members = $this->teamMembershipManager->getMembers($entity->id()); if (in_array($account->getEmail(), $team_members)) { $developer_team_access = TRUE; diff --git a/modules/apigee_edge_teams/src/TeamPermissionHandler.php b/modules/apigee_edge_teams/src/TeamPermissionHandler.php index 08dbcd574..f963fe0c9 100644 --- a/modules/apigee_edge_teams/src/TeamPermissionHandler.php +++ b/modules/apigee_edge_teams/src/TeamPermissionHandler.php @@ -164,7 +164,7 @@ public function getDeveloperPermissionsByTeam(TeamInterface $team, AccountInterf else { // Check if current developer is a member of the team and has the permision // to view more than 100 teams. - if ($account->hasPermission('view extensive team list') && (count($developer_team_ids) > 100)) { + if ($account->hasPermission('view extensive team list') && (count($developer_team_ids) >= 100)) { $team_members = $this->teamMembershipManager->getMembers($team->id()); if (in_array($account->getEmail(), $team_members)) { $developer_team_access = TRUE; From 38e1acdfc6dda3e6d9aea2b1a4db00c4612707e9 Mon Sep 17 00:00:00 2001 From: Divyajose Date: Wed, 16 Nov 2022 18:31:31 +0530 Subject: [PATCH 4/4] text change --- modules/apigee_edge_teams/src/Form/TeamMemberSyncForm.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/apigee_edge_teams/src/Form/TeamMemberSyncForm.php b/modules/apigee_edge_teams/src/Form/TeamMemberSyncForm.php index da13940f8..33e99cb65 100644 --- a/modules/apigee_edge_teams/src/Form/TeamMemberSyncForm.php +++ b/modules/apigee_edge_teams/src/Form/TeamMemberSyncForm.php @@ -95,7 +95,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { 'list' => [ '#theme' => 'item_list', '#items' => [ - $this->t('Store the team members in Drupal'), + $this->t('Caches team members in Drupal'), ], ], 'p2' => [ @@ -103,6 +103,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#tag' => 'p', '#value' => $this->t('The "Run team member sync" button will sync the team members, displaying a progress bar on the screen while running. The "Background team member sync" button will run the team member sync process in batches each time cron runs and may take multiple cron runs to complete.', [':cron_url' => Url::fromRoute('system.cron_settings')->toString()]), ], + 'p3' => [ + '#type' => 'html_tag', + '#tag' => 'p', + '#value' => $this->t('By running the sync, team member detail is stored in members cache table and will have expiry that is set in team caching. To show more than 100 teams for a member enable permission "View extensive teams list". ', [':team_caching' => Url::fromRoute('apigee_edge_teams.settings.team.cache')->toString()]), + ], ]; $form['sync']['sync_submit'] = [