Skip to content

Commit aec7253

Browse files
Merge pull request #56342 from nextcloud/backport/54953/stable31
[stable31] fix(team-api): get all teams details in a single request
2 parents 82d1274 + 822744c commit aec7253

File tree

9 files changed

+203
-51
lines changed

9 files changed

+203
-51
lines changed

build/psalm-baseline.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2525,6 +2525,11 @@
25252525
<code><![CDATA[$path]]></code>
25262526
</InvalidReturnStatement>
25272527
</file>
2528+
<file src="lib/private/Teams/TeamManager.php">
2529+
<UndefinedDocblockClass>
2530+
<code><![CDATA[Circle]]></code>
2531+
</UndefinedDocblockClass>
2532+
</file>
25282533
<file src="lib/private/User/Database.php">
25292534
<FalsableReturnStatement>
25302535
<code><![CDATA[false]]></code>

core/Controller/TeamsApiController.php

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
use OCP\IRequest;
1717
use OCP\Teams\ITeamManager;
1818
use OCP\Teams\Team;
19+
use OCP\Teams\TeamResource;
1920

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

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

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

7776
return new DataResponse([
78-
'teams' => $teams,
77+
'teams' => $listTeams,
7978
]);
8079
}
8180
}

core/ResponseDefinitions.php

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -148,19 +148,30 @@
148148
* }
149149
*
150150
* @psalm-type CoreTeam = array{
151-
* id: string,
152-
* name: string,
153-
* icon: string,
151+
* teamId: string,
152+
* displayName: string,
153+
* link: ?string,
154154
* }
155155
*
156156
* @psalm-type CoreTeamResource = array{
157-
* id: int,
158-
* label: string,
159-
* url: string,
160-
* iconSvg: ?string,
161-
* iconURL: ?string,
162-
* iconEmoji: ?string,
163-
* }
157+
* id: string,
158+
* label: string,
159+
* url: string,
160+
* iconSvg: ?string,
161+
* iconURL: ?string,
162+
* iconEmoji: ?string,
163+
* provider: array{
164+
* id: string,
165+
* name: string,
166+
* icon: string,
167+
* },
168+
* }
169+
*
170+
* @psalm-type CoreTeamWithResources = CoreTeam&array{
171+
* resources: list<CoreTeamResource>,
172+
* }
173+
*
174+
*
164175
*
165176
* @psalm-type CoreTaskProcessingShape = array{
166177
* name: string,

core/openapi-full.json

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -869,19 +869,20 @@
869869
"Team": {
870870
"type": "object",
871871
"required": [
872-
"id",
873-
"name",
874-
"icon"
872+
"teamId",
873+
"displayName",
874+
"link"
875875
],
876876
"properties": {
877-
"id": {
877+
"teamId": {
878878
"type": "string"
879879
},
880-
"name": {
880+
"displayName": {
881881
"type": "string"
882882
},
883-
"icon": {
884-
"type": "string"
883+
"link": {
884+
"type": "string",
885+
"nullable": true
885886
}
886887
}
887888
},
@@ -893,12 +894,12 @@
893894
"url",
894895
"iconSvg",
895896
"iconURL",
896-
"iconEmoji"
897+
"iconEmoji",
898+
"provider"
897899
],
898900
"properties": {
899901
"id": {
900-
"type": "integer",
901-
"format": "int64"
902+
"type": "string"
902903
},
903904
"label": {
904905
"type": "string"
@@ -917,9 +918,49 @@
917918
"iconEmoji": {
918919
"type": "string",
919920
"nullable": true
921+
},
922+
"provider": {
923+
"type": "object",
924+
"required": [
925+
"id",
926+
"name",
927+
"icon"
928+
],
929+
"properties": {
930+
"id": {
931+
"type": "string"
932+
},
933+
"name": {
934+
"type": "string"
935+
},
936+
"icon": {
937+
"type": "string"
938+
}
939+
}
920940
}
921941
}
922942
},
943+
"TeamWithResources": {
944+
"allOf": [
945+
{
946+
"$ref": "#/components/schemas/Team"
947+
},
948+
{
949+
"type": "object",
950+
"required": [
951+
"resources"
952+
],
953+
"properties": {
954+
"resources": {
955+
"type": "array",
956+
"items": {
957+
"$ref": "#/components/schemas/TeamResource"
958+
}
959+
}
960+
}
961+
}
962+
]
963+
},
923964
"TextProcessingTask": {
924965
"type": "object",
925966
"required": [
@@ -5379,7 +5420,7 @@
53795420
"teams": {
53805421
"type": "array",
53815422
"items": {
5382-
"$ref": "#/components/schemas/Team"
5423+
"$ref": "#/components/schemas/TeamWithResources"
53835424
}
53845425
}
53855426
}

core/openapi.json

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -869,19 +869,20 @@
869869
"Team": {
870870
"type": "object",
871871
"required": [
872-
"id",
873-
"name",
874-
"icon"
872+
"teamId",
873+
"displayName",
874+
"link"
875875
],
876876
"properties": {
877-
"id": {
877+
"teamId": {
878878
"type": "string"
879879
},
880-
"name": {
880+
"displayName": {
881881
"type": "string"
882882
},
883-
"icon": {
884-
"type": "string"
883+
"link": {
884+
"type": "string",
885+
"nullable": true
885886
}
886887
}
887888
},
@@ -893,12 +894,12 @@
893894
"url",
894895
"iconSvg",
895896
"iconURL",
896-
"iconEmoji"
897+
"iconEmoji",
898+
"provider"
897899
],
898900
"properties": {
899901
"id": {
900-
"type": "integer",
901-
"format": "int64"
902+
"type": "string"
902903
},
903904
"label": {
904905
"type": "string"
@@ -917,9 +918,49 @@
917918
"iconEmoji": {
918919
"type": "string",
919920
"nullable": true
921+
},
922+
"provider": {
923+
"type": "object",
924+
"required": [
925+
"id",
926+
"name",
927+
"icon"
928+
],
929+
"properties": {
930+
"id": {
931+
"type": "string"
932+
},
933+
"name": {
934+
"type": "string"
935+
},
936+
"icon": {
937+
"type": "string"
938+
}
939+
}
920940
}
921941
}
922942
},
943+
"TeamWithResources": {
944+
"allOf": [
945+
{
946+
"$ref": "#/components/schemas/Team"
947+
},
948+
{
949+
"type": "object",
950+
"required": [
951+
"resources"
952+
],
953+
"properties": {
954+
"resources": {
955+
"type": "array",
956+
"items": {
957+
"$ref": "#/components/schemas/TeamResource"
958+
}
959+
}
960+
}
961+
}
962+
]
963+
},
923964
"TextProcessingTask": {
924965
"type": "object",
925966
"required": [
@@ -5379,7 +5420,7 @@
53795420
"teams": {
53805421
"type": "array",
53815422
"items": {
5382-
"$ref": "#/components/schemas/Team"
5423+
"$ref": "#/components/schemas/TeamWithResources"
53835424
}
53845425
}
53855426
}

lib/private/Teams/TeamManager.php

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

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

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

102+
return array_merge_recursive(...$resources);
103+
}
104+
105+
public function getTeamsForResource(string $providerId, string $resourceId, string $userId): array {
106+
if (!$this->hasTeamSupport()) {
107+
return [];
108+
}
109+
110+
$provider = $this->getProvider($providerId);
111+
return array_map(function (Circle $team) {
98112
return new Team(
99-
$teamId,
113+
$team->getSingleId(),
100114
$team->getDisplayName(),
101-
$this->urlGenerator->linkToRouteAbsolute('contacts.contacts.directcircle', ['singleId' => $teamId]),
115+
$this->urlGenerator->linkToRouteAbsolute('contacts.contacts.directcircle', ['singleId' => $team->getSingleId()]),
102116
);
103-
}, $provider->getTeamsForResource($resourceId))));
117+
}, $this->getTeams($provider->getTeamsForResource($resourceId), $userId));
104118
}
105119

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

lib/public/Teams/ITeamManager.php

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

0 commit comments

Comments
 (0)