Skip to content

Commit 166f43a

Browse files
Merge pull request #55762 from nextcloud/backport/54953/stable32
[stable32] fix(team-api): get all teams details in a single request
2 parents 14aef68 + 83badb3 commit 166f43a

File tree

10 files changed

+253
-63
lines changed

10 files changed

+253
-63
lines changed

build/psalm-baseline.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4315,6 +4315,11 @@
43154315
<code><![CDATA[$path]]></code>
43164316
</InvalidReturnStatement>
43174317
</file>
4318+
<file src="lib/private/Teams/TeamManager.php">
4319+
<UndefinedDocblockClass>
4320+
<code><![CDATA[Circle]]></code>
4321+
</UndefinedDocblockClass>
4322+
</file>
43184323
<file src="lib/private/User/Database.php">
43194324
<FalsableReturnStatement>
43204325
<code><![CDATA[false]]></code>

core/Controller/TeamsApiController.php

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
use OCP\IRequest;
1818
use OCP\Teams\ITeamManager;
1919
use OCP\Teams\Team;
20+
use OCP\Teams\TeamResource;
2021

2122
/**
2223
* @psalm-import-type CoreTeamResource from ResponseDefinitions
2324
* @psalm-import-type CoreTeam from ResponseDefinitions
25+
* @psalm-import-type CoreTeamWithResources from ResponseDefinitions
2426
* @property $userId string
2527
*/
2628
class TeamsApiController extends OCSController {
@@ -44,21 +46,18 @@ public function __construct(
4446
#[NoAdminRequired]
4547
#[ApiRoute(verb: 'GET', url: '/{teamId}/resources', root: '/teams')]
4648
public function resolveOne(string $teamId): DataResponse {
47-
/**
48-
* @var list<CoreTeamResource> $resolvedResources
49-
* @psalm-suppress PossiblyNullArgument The route is limited to logged-in users
50-
*/
49+
/** @psalm-suppress PossiblyNullArgument The route is limited to logged-in users */
5150
$resolvedResources = $this->teamManager->getSharedWith($teamId, $this->userId);
5251

53-
return new DataResponse(['resources' => $resolvedResources]);
52+
return new DataResponse(['resources' => array_map(static fn (TeamResource $resource) => $resource->jsonSerialize(), $resolvedResources)]);
5453
}
5554

5655
/**
5756
* Get all teams of a resource
5857
*
5958
* @param string $providerId Identifier of the provider (e.g. deck, talk, collectives)
6059
* @param string $resourceId Unique id of the resource to list teams for (e.g. deck board id)
61-
* @return DataResponse<Http::STATUS_OK, array{teams: list<CoreTeam>}, array{}>
60+
* @return DataResponse<Http::STATUS_OK, array{teams: list<CoreTeamWithResources>}, array{}>
6261
*
6362
* 200: Teams returned
6463
*/
@@ -67,16 +66,15 @@ public function resolveOne(string $teamId): DataResponse {
6766
public function listTeams(string $providerId, string $resourceId): DataResponse {
6867
/** @psalm-suppress PossiblyNullArgument The route is limited to logged-in users */
6968
$teams = $this->teamManager->getTeamsForResource($providerId, $resourceId, $this->userId);
70-
/** @var list<CoreTeam> $teams */
71-
$teams = array_values(array_map(function (Team $team) {
69+
$sharesPerTeams = $this->teamManager->getSharedWithList(array_map(fn (Team $team): string => $team->getId(), $teams), $this->userId);
70+
$listTeams = array_values(array_map(function (Team $team) use ($sharesPerTeams) {
7271
$response = $team->jsonSerialize();
73-
/** @psalm-suppress PossiblyNullArgument The route is limited to logged in users */
74-
$response['resources'] = $this->teamManager->getSharedWith($team->getId(), $this->userId);
72+
$response['resources'] = array_map(static fn (TeamResource $resource) => $resource->jsonSerialize(), $sharesPerTeams[$team->getId()] ?? []);
7573
return $response;
7674
}, $teams));
7775

7876
return new DataResponse([
79-
'teams' => $teams,
77+
'teams' => $listTeams,
8078
]);
8179
}
8280
}

core/ResponseDefinitions.php

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -149,19 +149,28 @@
149149
* }
150150
*
151151
* @psalm-type CoreTeam = array{
152-
* id: string,
153-
* name: string,
154-
* icon: string,
152+
* teamId: string,
153+
* displayName: string,
154+
* link: ?string,
155155
* }
156156
*
157157
* @psalm-type CoreTeamResource = array{
158-
* id: int,
159-
* label: string,
160-
* url: string,
161-
* iconSvg: ?string,
162-
* iconURL: ?string,
163-
* iconEmoji: ?string,
164-
* }
158+
* id: string,
159+
* label: string,
160+
* url: string,
161+
* iconSvg: ?string,
162+
* iconURL: ?string,
163+
* iconEmoji: ?string,
164+
* provider: array{
165+
* id: string,
166+
* name: string,
167+
* icon: string,
168+
* },
169+
* }
170+
*
171+
* @psalm-type CoreTeamWithResources = CoreTeam&array{
172+
* resources: list<CoreTeamResource>,
173+
* }
165174
*
166175
* @psalm-type CoreTaskProcessingShape = array{
167176
* name: string,

core/openapi-full.json

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -898,19 +898,20 @@
898898
"Team": {
899899
"type": "object",
900900
"required": [
901-
"id",
902-
"name",
903-
"icon"
901+
"teamId",
902+
"displayName",
903+
"link"
904904
],
905905
"properties": {
906-
"id": {
906+
"teamId": {
907907
"type": "string"
908908
},
909-
"name": {
909+
"displayName": {
910910
"type": "string"
911911
},
912-
"icon": {
913-
"type": "string"
912+
"link": {
913+
"type": "string",
914+
"nullable": true
914915
}
915916
}
916917
},
@@ -922,12 +923,12 @@
922923
"url",
923924
"iconSvg",
924925
"iconURL",
925-
"iconEmoji"
926+
"iconEmoji",
927+
"provider"
926928
],
927929
"properties": {
928930
"id": {
929-
"type": "integer",
930-
"format": "int64"
931+
"type": "string"
931932
},
932933
"label": {
933934
"type": "string"
@@ -946,9 +947,49 @@
946947
"iconEmoji": {
947948
"type": "string",
948949
"nullable": true
950+
},
951+
"provider": {
952+
"type": "object",
953+
"required": [
954+
"id",
955+
"name",
956+
"icon"
957+
],
958+
"properties": {
959+
"id": {
960+
"type": "string"
961+
},
962+
"name": {
963+
"type": "string"
964+
},
965+
"icon": {
966+
"type": "string"
967+
}
968+
}
949969
}
950970
}
951971
},
972+
"TeamWithResources": {
973+
"allOf": [
974+
{
975+
"$ref": "#/components/schemas/Team"
976+
},
977+
{
978+
"type": "object",
979+
"required": [
980+
"resources"
981+
],
982+
"properties": {
983+
"resources": {
984+
"type": "array",
985+
"items": {
986+
"$ref": "#/components/schemas/TeamResource"
987+
}
988+
}
989+
}
990+
}
991+
]
992+
},
952993
"TextProcessingTask": {
953994
"type": "object",
954995
"required": [
@@ -6306,7 +6347,7 @@
63066347
"teams": {
63076348
"type": "array",
63086349
"items": {
6309-
"$ref": "#/components/schemas/Team"
6350+
"$ref": "#/components/schemas/TeamWithResources"
63106351
}
63116352
}
63126353
}

core/openapi.json

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -898,19 +898,20 @@
898898
"Team": {
899899
"type": "object",
900900
"required": [
901-
"id",
902-
"name",
903-
"icon"
901+
"teamId",
902+
"displayName",
903+
"link"
904904
],
905905
"properties": {
906-
"id": {
906+
"teamId": {
907907
"type": "string"
908908
},
909-
"name": {
909+
"displayName": {
910910
"type": "string"
911911
},
912-
"icon": {
913-
"type": "string"
912+
"link": {
913+
"type": "string",
914+
"nullable": true
914915
}
915916
}
916917
},
@@ -922,12 +923,12 @@
922923
"url",
923924
"iconSvg",
924925
"iconURL",
925-
"iconEmoji"
926+
"iconEmoji",
927+
"provider"
926928
],
927929
"properties": {
928930
"id": {
929-
"type": "integer",
930-
"format": "int64"
931+
"type": "string"
931932
},
932933
"label": {
933934
"type": "string"
@@ -946,9 +947,49 @@
946947
"iconEmoji": {
947948
"type": "string",
948949
"nullable": true
950+
},
951+
"provider": {
952+
"type": "object",
953+
"required": [
954+
"id",
955+
"name",
956+
"icon"
957+
],
958+
"properties": {
959+
"id": {
960+
"type": "string"
961+
},
962+
"name": {
963+
"type": "string"
964+
},
965+
"icon": {
966+
"type": "string"
967+
}
968+
}
949969
}
950970
}
951971
},
972+
"TeamWithResources": {
973+
"allOf": [
974+
{
975+
"$ref": "#/components/schemas/Team"
976+
},
977+
{
978+
"type": "object",
979+
"required": [
980+
"resources"
981+
],
982+
"properties": {
983+
"resources": {
984+
"type": "array",
985+
"items": {
986+
"$ref": "#/components/schemas/TeamResource"
987+
}
988+
}
989+
}
990+
}
991+
]
992+
},
952993
"TextProcessingTask": {
953994
"type": "object",
954995
"required": [
@@ -6306,7 +6347,7 @@
63066347
"teams": {
63076348
"type": "array",
63086349
"items": {
6309-
"$ref": "#/components/schemas/Team"
6350+
"$ref": "#/components/schemas/TeamWithResources"
63106351
}
63116352
}
63126353
}

lib/private/Teams/TeamManager.php

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,24 +84,38 @@ public function getSharedWith(string $teamId, string $userId): array {
8484
return array_values($resources);
8585
}
8686

87-
public function getTeamsForResource(string $providerId, string $resourceId, string $userId): array {
87+
public function getSharedWithList(array $teams, string $userId): array {
8888
if (!$this->hasTeamSupport()) {
8989
return [];
9090
}
9191

92-
$provider = $this->getProvider($providerId);
93-
return array_values(array_filter(array_map(function ($teamId) use ($userId) {
94-
$team = $this->getTeam($teamId, $userId);
95-
if ($team === null) {
96-
return null;
92+
$resources = [];
93+
foreach ($this->getProviders() as $provider) {
94+
if (method_exists($provider, 'getSharedWithList')) {
95+
$resources[] = $provider->getSharedWithList($teams, $userId);
96+
} else {
97+
foreach ($teams as $team) {
98+
$resources[] = [$team->getId() => $provider->getSharedWith($team->getId())];
99+
}
97100
}
101+
}
98102

103+
return array_merge_recursive(...$resources);
104+
}
105+
106+
public function getTeamsForResource(string $providerId, string $resourceId, string $userId): array {
107+
if (!$this->hasTeamSupport()) {
108+
return [];
109+
}
110+
111+
$provider = $this->getProvider($providerId);
112+
return array_map(function (Circle $team) {
99113
return new Team(
100-
$teamId,
114+
$team->getSingleId(),
101115
$team->getDisplayName(),
102-
$this->urlGenerator->linkToRouteAbsolute('contacts.contacts.directcircle', ['singleId' => $teamId]),
116+
$this->urlGenerator->linkToRouteAbsolute('contacts.contacts.directcircle', ['singleId' => $team->getSingleId()]),
103117
);
104-
}, $provider->getTeamsForResource($resourceId))));
118+
}, $this->getTeams($provider->getTeamsForResource($resourceId), $userId));
105119
}
106120

107121
private function getTeam(string $teamId, string $userId): ?Circle {
@@ -117,4 +131,17 @@ private function getTeam(string $teamId, string $userId): ?Circle {
117131
return null;
118132
}
119133
}
134+
135+
/**
136+
* @return Circle[]
137+
*/
138+
private function getTeams(array $teams, string $userId): array {
139+
if (!$this->hasTeamSupport()) {
140+
return [];
141+
}
142+
143+
$federatedUser = $this->circlesManager->getFederatedUser($userId, Member::TYPE_USER);
144+
$this->circlesManager->startSession($federatedUser);
145+
return $this->circlesManager->getCirclesByIds($teams);
146+
}
120147
}

lib/public/Teams/ITeamManager.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,12 @@ public function getSharedWith(string $teamId, string $userId): array;
4040
* @since 29.0.0
4141
*/
4242
public function getTeamsForResource(string $providerId, string $resourceId, string $userId): array;
43+
44+
/**
45+
* @param list<Team> $teams
46+
* @return array<string, list<TeamResource>>
47+
*
48+
* @since 32.0.2
49+
*/
50+
public function getSharedWithList(array $teams, string $userId): array;
4351
}

0 commit comments

Comments
 (0)