Skip to content

Commit f62b71f

Browse files
[#408] Fix team lists cache errors. (#419)
* [#408] Fix team lists cache errors. * [#408] Fix team lists cache errors - updated cache tags. * [#408] Fix team lists cache errors - updates and test. * [#408] Fix team lists cache errors - updates and test. * [#408] Fix team lists cache errors - fixing phpcs. * [#408] Fix tests. * [#408] Fix tests.
1 parent 6e17213 commit f62b71f

File tree

8 files changed

+344
-17
lines changed

8 files changed

+344
-17
lines changed

modules/apigee_edge_actions/tests/src/Kernel/ApigeeEdgeActionsRulesKernelTestBase.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ protected function setUp() {
8686
'first_name' => $this->getRandomGenerator()->word(16),
8787
'last_name' => $this->getRandomGenerator()->word(16),
8888
]);
89-
$this->account->save();
9089
$this->queueDeveloperResponse($this->account, Response::HTTP_CREATED);
90+
$this->account->save();
9191
}
9292

9393
/**

modules/apigee_edge_actions/tests/src/Kernel/Plugin/RulesEvent/EdgeEntityAddMemberEventTest.php

+1-5
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,7 @@ public function testEvent() {
8282
$team = $this->createTeam();
8383

8484
// Add team member.
85-
$this->queueCompanyResponse($team->decorated());
86-
$this->queueDeveloperResponse($this->account);
87-
$this->container->get('apigee_edge_teams.team_membership_manager')->addMembers($team->id(), [
88-
$this->account->getEmail(),
89-
]);
85+
$this->addUserToTeam($team, $this->account);
9086

9187
$this->assertLogsContains("Event apigee_edge_actions_entity_add_member:team was dispatched.");
9288
$this->assertLogsContains("Member {$this->account->first_name->value} was added to team {$team->getDisplayName()}.");

modules/apigee_edge_actions/tests/src/Kernel/Plugin/RulesEvent/EdgeEntityRemoveMemberEventTest.php

+4-6
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,13 @@ public function testEvent() {
8080
// Create a new team.
8181
$team = $this->createTeam();
8282

83-
// Add team member.
84-
$this->queueCompanyResponse($team->decorated());
85-
$this->queueDeveloperResponse($this->account);
8683
$team_membership_manager = $this->container->get('apigee_edge_teams.team_membership_manager');
87-
$team_membership_manager->addMembers($team->id(), [
88-
$this->account->getEmail(),
89-
]);
84+
85+
// Add team member.
86+
$this->addUserToTeam($team, $this->account);
9087

9188
// Remove team member.
89+
$this->stack->queueMockResponse('no_content');
9290
$team_membership_manager->removeMembers($team->id(), [
9391
$this->account->getEmail(),
9492
]);

modules/apigee_edge_teams/src/Entity/ListBuilder/TeamListBuilder.php

+32
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Drupal\apigee_edge\Element\StatusPropertyElement;
2424
use Drupal\apigee_edge\Entity\ListBuilder\EdgeEntityListBuilder;
2525
use Drupal\apigee_edge_teams\Entity\TeamInterface;
26+
use Drupal\Core\Cache\Cache;
2627
use Drupal\Core\Entity\EntityInterface;
2728
use Drupal\Core\Url;
2829

@@ -110,4 +111,35 @@ public function buildRow(EntityInterface $entity) {
110111
return $row + parent::buildRow($entity);
111112
}
112113

114+
/**
115+
* {@inheritdoc}
116+
*/
117+
public function render() {
118+
$build = parent::render();
119+
$account = $this->entityTypeManager->getStorage('user')->load(\Drupal::currentUser()->id());
120+
121+
$build = empty($build['table']) ? $build : $build['table'];
122+
123+
$build['#cache']['keys'][] = 'team_list_per_user';
124+
125+
// Team lists vary for each user and their permissions.
126+
// Note: Even though cache contexts will be optimized to only include the
127+
// 'user' cache context, the element should be invalidated correctly when
128+
// permissions change because the 'user.permissions' cache context defined
129+
// cache tags for permission changes, which should have bubbled up for the
130+
// element when it was optimized away.
131+
// @see \Drupal\KernelTests\Core\Cache\CacheContextOptimizationTest
132+
$build['#cache']['contexts'][] = 'user';
133+
$build['#cache']['contexts'][] = 'user.permissions';
134+
135+
$build['#cache']['tags'] = Cache::mergeTags($build['#cache']['tags'], $account->getCacheTags());
136+
137+
// Use cache expiration defined in configuration.
138+
$build['#cache']['max-age'] = $this->configFactory
139+
->get('apigee_edge_teams.team_settings')
140+
->get('cache_expiration');
141+
142+
return $build;
143+
}
144+
113145
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
<?php
2+
3+
/**
4+
* Copyright 2020 Google Inc.
5+
*
6+
* This program is free software; you can redistribute it and/or modify it under
7+
* the terms of the GNU General Public License version 2 as published by the
8+
* Free Software Foundation.
9+
*
10+
* This program is distributed in the hope that it will be useful, but WITHOUT
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12+
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
13+
* License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License along
16+
* with this program; if not, write to the Free Software Foundation, Inc., 51
17+
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18+
*/
19+
20+
namespace Drupal\Tests\apigee_edge_teams\Functional;
21+
22+
use Drupal\apigee_edge\Entity\Developer;
23+
use Drupal\Core\Url;
24+
use Symfony\Component\HttpFoundation\Response;
25+
26+
/**
27+
* Apigee Edge Teams list builder tests.
28+
*
29+
* @group apigee_edge
30+
* @group apigee_edge_teams
31+
*/
32+
class TeamListBuilderTest extends ApigeeEdgeTeamsFunctionalTestBase {
33+
34+
/**
35+
* Indicates this test class is mock API client ready.
36+
*
37+
* @var bool
38+
*/
39+
protected static $mock_api_client_ready = TRUE;
40+
41+
/**
42+
* {@inheritdoc}
43+
*/
44+
protected static $modules = [
45+
'system',
46+
'user',
47+
'options',
48+
'key',
49+
'apigee_edge',
50+
'apigee_edge_teams',
51+
'apigee_mock_api_client',
52+
];
53+
54+
/**
55+
* The team entity storage.
56+
*
57+
* @var \Drupal\apigee_edge_teams\Entity\Storage\TeamStorageInterface
58+
*/
59+
protected $teamStorage;
60+
61+
/**
62+
* The user 1 account.
63+
*
64+
* @var \Drupal\user\UserInterface
65+
*/
66+
protected $account;
67+
68+
/**
69+
* Drupal user who is a member team A.
70+
*
71+
* @var \Drupal\user\UserInterface
72+
*/
73+
protected $aMemberAccount;
74+
75+
/**
76+
* Drupal user who is a member team B.
77+
*
78+
* @var \Drupal\user\UserInterface
79+
*/
80+
protected $bMemberAccount;
81+
82+
/**
83+
* Drupal user who is an admin.
84+
*
85+
* @var \Drupal\user\UserInterface
86+
*/
87+
protected $cMemberAccount;
88+
89+
/**
90+
* Team A entity to test.
91+
*
92+
* @var \Drupal\apigee_edge_teams\Entity\TeamInterface
93+
*/
94+
protected $teamA;
95+
96+
/**
97+
* Team B entity to test.
98+
*
99+
* @var \Drupal\apigee_edge_teams\Entity\TeamInterface
100+
*/
101+
protected $teamB;
102+
103+
/**
104+
* A role.
105+
*
106+
* @var \Drupal\user\Entity\Role
107+
*/
108+
protected $customRole;
109+
110+
/**
111+
* {@inheritdoc}
112+
*/
113+
protected function setUp() {
114+
parent::setUp();
115+
116+
$this->addOrganizationMatchedResponse();
117+
118+
$this->teamStorage = $this->entityTypeManager->getStorage('team');
119+
120+
$config_factory = \Drupal::configFactory();
121+
$config = $config_factory->getEditable('apigee_edge_teams.team_settings');
122+
$config->set('cache_expiration', 300);
123+
$config->save(TRUE);
124+
125+
// Create accounts: user 1, for members of two teams, and an extra one.
126+
$this->account = $this->rootUser;
127+
$this->aMemberAccount = $this->createNewAccount();
128+
$this->bMemberAccount = $this->createNewAccount();
129+
$this->cMemberAccount = $this->createNewAccount();
130+
131+
$this->customRole = $this->drupalCreateRole(['view any team']);
132+
133+
// Create teams.
134+
$this->teamA = $this->createTeam();
135+
$this->teamB = $this->createTeam();
136+
137+
// Add accounts to teams.
138+
$this->addUserToTeam($this->teamA, $this->aMemberAccount);
139+
$this->addUserToTeam($this->teamB, $this->bMemberAccount);
140+
}
141+
142+
/**
143+
* {@inheritdoc}
144+
*/
145+
protected function tearDown() {
146+
try {
147+
$this->teamStorage->delete([$this->teamA, $this->teamB]);
148+
$this->account->delete();
149+
$this->aMemberAccount->delete();
150+
$this->bMemberAccount->delete();
151+
$this->cMemberAccount->delete();
152+
}
153+
catch (\Error $error) {
154+
// Do nothing.
155+
}
156+
catch (\Exception $exception) {
157+
// Do nothing.
158+
}
159+
}
160+
161+
/**
162+
* Tests team list cache.
163+
*/
164+
public function testTeamListCache() {
165+
$companies = [
166+
$this->teamA->decorated(),
167+
$this->teamB->decorated(),
168+
];
169+
170+
// aMemberAccount should only see teamA.
171+
$this->drupalLogin($this->aMemberAccount);
172+
$this->queueCompaniesResponse($companies);
173+
$this->queueDeveloperResponse($this->aMemberAccount, 200, ['companies' => [$this->teamA->id()]]);
174+
$this->drupalGet(Url::fromRoute('entity.team.collection'));
175+
$assert = $this->assertSession();
176+
$assert->pageTextContains($this->teamA->label());
177+
$assert->pageTextNotContains($this->teamB->label());
178+
$this->drupalLogout();
179+
180+
// bMemberAccount should only see teamB.
181+
$this->drupalLogin($this->bMemberAccount);
182+
$this->queueCompaniesResponse($companies);
183+
$this->queueDeveloperResponse($this->bMemberAccount, 200, ['companies' => [$this->teamB->id()]]);
184+
$this->drupalGet(Url::fromUserInput('/teams'));
185+
$assert = $this->assertSession();
186+
$assert->pageTextNotContains($this->teamA->label());
187+
$assert->pageTextContains($this->teamB->label());
188+
$this->drupalLogout();
189+
190+
// cMemberAccount should not see any teams.
191+
$this->drupalLogin($this->cMemberAccount);
192+
$this->queueCompaniesResponse($companies);
193+
$this->queueDeveloperResponse($this->cMemberAccount);
194+
$this->queueDeveloperResponse($this->cMemberAccount);
195+
$this->drupalGet(Url::fromUserInput('/teams'));
196+
$assert = $this->assertSession();
197+
$assert->pageTextNotContains($this->teamA->label());
198+
$assert->pageTextNotContains($this->teamB->label());
199+
200+
// Give cMemberAccount permission to view all teams.
201+
$this->cMemberAccount->addRole($this->customRole);
202+
$this->cMemberAccount->save();
203+
204+
// cMemberAccount should see both teams now.
205+
$this->queueCompaniesResponse($companies);
206+
$this->drupalGet(Url::fromUserInput('/teams'));
207+
$assert = $this->assertSession();
208+
$assert->pageTextContains($this->teamA->label());
209+
$assert->pageTextContains($this->teamB->label());
210+
}
211+
212+
/**
213+
* Helper function to create a random user account.
214+
*
215+
* @return \Drupal\Core\Entity\EntityInterface
216+
* The user account.
217+
*/
218+
protected function createNewAccount() {
219+
$this->disableUserPresave();
220+
$account = $this->createAccount();
221+
222+
$fields = [
223+
'email' => $account->getEmail(),
224+
'userName' => $account->getAccountName(),
225+
'firstName' => $this->getRandomGenerator()->word(8),
226+
'lastName' => $this->getRandomGenerator()->word(8),
227+
];
228+
229+
// Stack developer responses for "created" and "set active".
230+
$this->queueDeveloperResponse($account, Response::HTTP_CREATED);
231+
$this->stack->queueMockResponse('no_content');
232+
$developer = Developer::create($fields);
233+
$developer->save();
234+
235+
return $account;
236+
}
237+
238+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{#
2+
/**
3+
* @file
4+
* Companies
5+
*
6+
* Usage:
7+
* @code {% include 'companies.json.twig' %} @endcode
8+
*
9+
* Variables:
10+
* - companies: an array of company objects.
11+
*/
12+
#}
13+
{
14+
"company" : [
15+
{% for company in companies %}
16+
{% include 'company.json.twig' with {'company': company} %}{{ loop.last ? '' : ',' }}
17+
{% endfor %}
18+
]
19+
}

0 commit comments

Comments
 (0)