Skip to content

Commit 88effb9

Browse files
committed
Add User::listLogins()
1 parent d5baebe commit 88effb9

File tree

5 files changed

+266
-0
lines changed

5 files changed

+266
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- New method `Redmine\Api\Project::listNames()` for listing the ids and names of all projects.
1717
- New method `Redmine\Api\Role::listNames()` for listing the ids and names of all roles.
1818
- New method `Redmine\Api\TimeEntryActivity::listNames()` for listing the ids and names of all time entry activities.
19+
- New method `Redmine\Api\User::listLogins()` for listing the ids and logins of all users.
1920

2021
### Deprecated
2122

src/Redmine/Api/User.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ class User extends AbstractApi
2323
{
2424
private $users = [];
2525

26+
private $userLogins = null;
27+
2628
/**
2729
* List users.
2830
*
@@ -43,6 +45,43 @@ final public function list(array $params = []): array
4345
}
4446
}
4547

48+
/**
49+
* Returns an array of all users with id/login pairs.
50+
*
51+
* @return array<int,string> list of users (id => login)
52+
*/
53+
final public function listLogins(): array
54+
{
55+
if ($this->userLogins !== null) {
56+
return $this->userLogins;
57+
}
58+
59+
$this->userLogins = [];
60+
61+
$limit = 100;
62+
$offset = 0;
63+
64+
do {
65+
$list = $this->list([
66+
'limit' => $limit,
67+
'offset' => $offset,
68+
]);
69+
70+
$listCount = 0;
71+
$offset += $limit;
72+
73+
if (array_key_exists('users', $list)) {
74+
$listCount = count($list['users']);
75+
76+
foreach ($list['users'] as $user) {
77+
$this->userLogins[(int) $user['id']] = (string) $user['login'];
78+
}
79+
}
80+
} while ($listCount === $limit);
81+
82+
return $this->userLogins;
83+
}
84+
4685
/**
4786
* List users.
4887
*

tests/Behat/Bootstrap/UserContextTrait.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,26 @@
99

1010
trait UserContextTrait
1111
{
12+
/**
13+
* @Given I create :count users
14+
*/
15+
public function iCreateUsers(int $count)
16+
{
17+
while ($count > 0) {
18+
$table = new TableNode([
19+
['property', 'value'],
20+
['login', 'testuser_' . $count],
21+
['firstname', 'first'],
22+
['lastname', 'last'],
23+
['mail', 'mail.' . $count . '@example.net'],
24+
]);
25+
26+
$this->iCreateAUserWithTheFollowingData($table);
27+
28+
$count--;
29+
}
30+
}
31+
1232
/**
1333
* @When I create a user with the following data
1434
*/
@@ -57,6 +77,20 @@ public function iListAllUsers()
5777
);
5878
}
5979

80+
/**
81+
* @When I list all user logins
82+
*/
83+
public function iListAllUserLogins()
84+
{
85+
/** @var User */
86+
$api = $this->getNativeCurlClient()->getApi('user');
87+
88+
$this->registerClientResponse(
89+
$api->listLogins(),
90+
$api->getLastResponse(),
91+
);
92+
}
93+
6094
/**
6195
* @When I update the user with id :id and the following data
6296
*/

tests/Behat/features/user.feature

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,31 @@ Feature: Interacting with the REST API for users
168168
| mail | mail@example.net |
169169
| twofa_scheme | null |
170170

171+
Scenario: Listing of multiple user logins
172+
Given I have a "NativeCurlClient" client
173+
And I create a user with the following data
174+
| property | value |
175+
| login | username |
176+
| firstname | first |
177+
| lastname | last |
178+
| mail | mail@example.net |
179+
When I list all user logins
180+
Then the response has the status code "200"
181+
And the response has the content type "application/json"
182+
And the returned data contains "2" items
183+
And the returned data has proterties with the following data
184+
| property | value |
185+
| 1 | admin |
186+
| 5 | username |
187+
188+
Scenario: Listing of multiple user logins
189+
Given I have a "NativeCurlClient" client
190+
And I create "108" users
191+
When I list all user logins
192+
Then the response has the status code "200"
193+
And the response has the content type "application/json"
194+
And the returned data contains "109" items
195+
171196
Scenario: Updating an user
172197
Given I have a "NativeCurlClient" client
173198
And I create a user with the following data
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Redmine\Tests\Unit\Api\User;
6+
7+
use PHPUnit\Framework\Attributes\CoversClass;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use PHPUnit\Framework\TestCase;
10+
use Redmine\Api\User;
11+
use Redmine\Tests\Fixtures\AssertingHttpClient;
12+
13+
#[CoversClass(User::class)]
14+
class ListLoginsTest extends TestCase
15+
{
16+
/**
17+
* @dataProvider getListLoginsData
18+
*/
19+
#[DataProvider('getListLoginsData')]
20+
public function testListLoginsReturnsCorrectResponse($expectedPath, $responseCode, $response, $expectedResponse)
21+
{
22+
$client = AssertingHttpClient::create(
23+
$this,
24+
[
25+
'GET',
26+
$expectedPath,
27+
'application/json',
28+
'',
29+
$responseCode,
30+
'application/json',
31+
$response,
32+
],
33+
);
34+
35+
// Create the object under test
36+
$api = new User($client);
37+
38+
// Perform the tests
39+
$this->assertSame($expectedResponse, $api->listLogins());
40+
}
41+
42+
public static function getListLoginsData(): array
43+
{
44+
return [
45+
'test without users' => [
46+
'/users.json?limit=100&offset=0',
47+
201,
48+
<<<JSON
49+
{
50+
"users": []
51+
}
52+
JSON,
53+
[],
54+
],
55+
'test with multiple users' => [
56+
'/users.json?limit=100&offset=0',
57+
201,
58+
<<<JSON
59+
{
60+
"users": [
61+
{"id": 7, "login": "username_C"},
62+
{"id": 8, "login": "username_B"},
63+
{"id": 9, "login": "username_A"}
64+
]
65+
}
66+
JSON,
67+
[
68+
7 => "username_C",
69+
8 => "username_B",
70+
9 => "username_A",
71+
],
72+
],
73+
];
74+
}
75+
76+
public function testListLoginsWithALotOfUsersHandlesPagination()
77+
{
78+
$assertData = [];
79+
$usersRequest1 = [];
80+
$usersRequest2 = [];
81+
$usersRequest3 = [];
82+
83+
for ($i = 1; $i <= 100; $i++) {
84+
$login = 'user_' . $i;
85+
86+
$assertData[$i] = $login;
87+
$usersRequest1[] = ['id' => $i, 'login' => $login];
88+
}
89+
90+
for ($i = 101; $i <= 200; $i++) {
91+
$login = 'user_' . $i;
92+
93+
$assertData[$i] = $login;
94+
$usersRequest2[] = ['id' => $i, 'login' => $login];
95+
}
96+
97+
$client = AssertingHttpClient::create(
98+
$this,
99+
[
100+
'GET',
101+
'/users.json?limit=100&offset=0',
102+
'application/json',
103+
'',
104+
200,
105+
'application/json',
106+
json_encode(['users' => $usersRequest1]),
107+
],
108+
[
109+
'GET',
110+
'/users.json?limit=100&offset=100',
111+
'application/json',
112+
'',
113+
200,
114+
'application/json',
115+
json_encode(['users' => $usersRequest2]),
116+
],
117+
[
118+
'GET',
119+
'/users.json?limit=100&offset=200',
120+
'application/json',
121+
'',
122+
200,
123+
'application/json',
124+
json_encode(['users' => $usersRequest3]),
125+
],
126+
);
127+
128+
// Create the object under test
129+
$api = new User($client);
130+
131+
// Perform the tests
132+
$this->assertSame($assertData, $api->listLogins());
133+
}
134+
135+
public function testListLoginsCallsHttpClientOnlyOnce()
136+
{
137+
$client = AssertingHttpClient::create(
138+
$this,
139+
[
140+
'GET',
141+
'/users.json?limit=100&offset=0',
142+
'application/json',
143+
'',
144+
200,
145+
'application/json',
146+
<<<JSON
147+
{
148+
"users": [
149+
{
150+
"id": 1,
151+
"login": "username"
152+
}
153+
]
154+
}
155+
JSON,
156+
],
157+
);
158+
159+
// Create the object under test
160+
$api = new User($client);
161+
162+
// Perform the tests
163+
$this->assertSame([1 => 'username'], $api->listLogins());
164+
$this->assertSame([1 => 'username'], $api->listLogins());
165+
$this->assertSame([1 => 'username'], $api->listLogins());
166+
}
167+
}

0 commit comments

Comments
 (0)