Skip to content

Commit 6a2556d

Browse files
committed
Add UpdatePinnedTestMeasurementOrder mutation
1 parent 64722d5 commit 6a2556d

File tree

7 files changed

+422
-1
lines changed

7 files changed

+422
-1
lines changed

app/GraphQL/Mutations/DeletePinnedTestMeasurement.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ protected function mutate(array $args): void
2020

2121
Gate::authorize('deletePinnedTestMeasurement', $measurement?->project);
2222

23-
$measurement->delete();
23+
$measurement?->delete();
2424
}
2525
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\GraphQL\Mutations;
6+
7+
use App\Models\PinnedTestMeasurement;
8+
use App\Models\Project;
9+
use Exception;
10+
use Illuminate\Support\Collection;
11+
use Illuminate\Support\Facades\Gate;
12+
13+
final class UpdatePinnedTestMeasurementOrder extends AbstractMutation
14+
{
15+
/** @var ?Collection<PinnedTestMeasurement> */
16+
public ?Collection $pinnedTestMeasurements = null;
17+
18+
/**
19+
* @param array{
20+
* projectId: int,
21+
* pinnedTestMeasurementIds: array<int>,
22+
* } $args
23+
*/
24+
protected function mutate(array $args): void
25+
{
26+
$project = Project::find((int) $args['projectId']);
27+
Gate::authorize('updatePinnedTestMeasurementOrder', $project);
28+
29+
$projectMeasurementIds = $project?->pinnedTestMeasurements()->pluck('id');
30+
$newOrder = collect($args['pinnedTestMeasurementIds']);
31+
32+
if ($projectMeasurementIds->diff($newOrder)->isNotEmpty()) {
33+
throw new Exception('IDs for all PinnedTestMeasurements must be provided.');
34+
}
35+
36+
if ($newOrder->count() !== $projectMeasurementIds->count()) {
37+
throw new Exception('Provided set cannot contain duplicate IDs.');
38+
}
39+
40+
if ($newOrder->isEmpty()) {
41+
throw new Exception("Can't order an empty set.");
42+
}
43+
44+
// We start at the previous maximum ID + 1 to guarantee that there are never any conflicts.
45+
// Only the relative order matters, so we don't care if the minimum position is now 1.
46+
$position = (int) $project?->pinnedTestMeasurements()->max('position') + 1;
47+
foreach ($newOrder as $id) {
48+
/** @var PinnedTestMeasurement $measurement */
49+
$measurement = $project?->pinnedTestMeasurements()->findOrFail((int) $id);
50+
51+
$measurement->position = $position;
52+
$measurement->save();
53+
$position++;
54+
}
55+
56+
$this->pinnedTestMeasurements = $project?->pinnedTestMeasurements()->orderBy('position')->get();
57+
}
58+
}

app/Policies/ProjectPolicy.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ public function deletePinnedTestMeasurement(User $currentUser, Project $project)
151151
return $this->update($currentUser, $project);
152152
}
153153

154+
public function updatePinnedTestMeasurementOrder(User $currentUser, Project $project): bool
155+
{
156+
return $this->update($currentUser, $project);
157+
}
158+
154159
private function isLdapControlledMembership(Project $project): bool
155160
{
156161
// If a LDAP filter has been specified and LDAP is enabled, CDash controls the entire members list.

app/cdash/tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ add_feature_test_in_transaction(/Feature/GraphQL/Mutations/CreatePinnedTestMeasu
280280

281281
add_feature_test_in_transaction(/Feature/GraphQL/Mutations/DeletePinnedTestMeasurementTest)
282282

283+
add_feature_test_in_transaction(/Feature/GraphQL/Mutations/UpdatePinnedTestMeasurementOrderTest)
284+
283285
add_feature_test_in_transaction(/Feature/GlobalInvitationAcceptanceTest)
284286

285287
add_feature_test_in_transaction(/Feature/GraphQL/GlobalInvitationTypeTest)

graphql/schema.graphql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ type Mutation {
117117

118118
"Remove a pinned test measurement. Does not re-number positions."
119119
deletePinnedTestMeasurement(input: DeletePinnedTestMeasurementInput! @spread): DeletePinnedTestMeasurementMutationPayload! @field(resolver: "DeletePinnedTestMeasurement")
120+
121+
"""
122+
Reorder the pinned measurements. Note: positions will be sequential, starting at 1 + the
123+
previous maximum position. Only the relative order is guaranteed.
124+
"""
125+
updatePinnedTestMeasurementOrder(input: UpdatePinnedTestMeasurementOrderInput! @spread): UpdatePinnedTestMeasurementOrderMutationPayload! @field(resolver: "UpdatePinnedTestMeasurementOrder")
120126
}
121127

122128

@@ -542,6 +548,23 @@ type DeletePinnedTestMeasurementMutationPayload implements MutationPayloadInterf
542548
}
543549

544550

551+
input UpdatePinnedTestMeasurementOrderInput {
552+
projectId: ID!
553+
554+
"A list of PinnedTestMeasurement IDs. Relative position will be set based on position in the list."
555+
pinnedTestMeasurementIds: [ID!]!
556+
}
557+
558+
559+
type UpdatePinnedTestMeasurementOrderMutationPayload implements MutationPayloadInterface {
560+
"Optional error message."
561+
message: String
562+
563+
"The new list of pinned measurements."
564+
pinnedTestMeasurements: [PinnedTestMeasurement!]
565+
}
566+
567+
545568
"Configure."
546569
type Configure {
547570
"Unique primary key."
@@ -1187,5 +1210,6 @@ type PinnedTestMeasurement {
11871210

11881211
name: String!
11891212

1213+
"Note: Only the relative positions of PinnedTestMeasurements are guaranteed."
11901214
position: Int!
11911215
}

phpstan-baseline.neon

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,12 @@ parameters:
282282
count: 1
283283
path: app/GraphQL/Mutations/CreatePinnedTestMeasurement.php
284284

285+
-
286+
rawMessage: 'Parameter #1 $args (array{id: int}) of method App\GraphQL\Mutations\DeletePinnedTestMeasurement::mutate() should be contravariant with parameter $args (array<string, mixed>) of method App\GraphQL\Mutations\AbstractMutation::mutate()'
287+
identifier: method.childParameterType
288+
count: 1
289+
path: app/GraphQL/Mutations/DeletePinnedTestMeasurement.php
290+
285291
-
286292
rawMessage: 'Parameter #1 $args (array{email: string, projectId: int, role: App\Enums\ProjectRole}) of method App\GraphQL\Mutations\InviteToProject::mutate() should be contravariant with parameter $args (array<string, mixed>) of method App\GraphQL\Mutations\AbstractMutation::mutate()'
287293
identifier: method.childParameterType
@@ -324,6 +330,36 @@ parameters:
324330
count: 1
325331
path: app/GraphQL/Mutations/UnclaimSite.php
326332

333+
-
334+
rawMessage: 'Cannot call method count() on Illuminate\Support\Collection<(int|string), mixed>|null.'
335+
identifier: method.nonObject
336+
count: 1
337+
path: app/GraphQL/Mutations/UpdatePinnedTestMeasurementOrder.php
338+
339+
-
340+
rawMessage: 'Cannot call method diff() on Illuminate\Support\Collection<(int|string), mixed>|null.'
341+
identifier: method.nonObject
342+
count: 1
343+
path: app/GraphQL/Mutations/UpdatePinnedTestMeasurementOrder.php
344+
345+
-
346+
rawMessage: Cannot cast mixed to int.
347+
identifier: cast.int
348+
count: 1
349+
path: app/GraphQL/Mutations/UpdatePinnedTestMeasurementOrder.php
350+
351+
-
352+
rawMessage: 'Parameter #1 $args (array{projectId: int, pinnedTestMeasurementIds: array<int>}) of method App\GraphQL\Mutations\UpdatePinnedTestMeasurementOrder::mutate() should be contravariant with parameter $args (array<string, mixed>) of method App\GraphQL\Mutations\AbstractMutation::mutate()'
353+
identifier: method.childParameterType
354+
count: 1
355+
path: app/GraphQL/Mutations/UpdatePinnedTestMeasurementOrder.php
356+
357+
-
358+
rawMessage: 'Property App\GraphQL\Mutations\UpdatePinnedTestMeasurementOrder::$pinnedTestMeasurements with generic class Illuminate\Support\Collection does not specify its types: TKey, TValue'
359+
identifier: missingType.generics
360+
count: 1
361+
path: app/GraphQL/Mutations/UpdatePinnedTestMeasurementOrder.php
362+
327363
-
328364
rawMessage: 'Parameter #1 $args (array{siteId: int, description: string}) of method App\GraphQL\Mutations\UpdateSiteDescription::mutate() should be contravariant with parameter $args (array<string, mixed>) of method App\GraphQL\Mutations\AbstractMutation::mutate()'
329365
identifier: method.childParameterType

0 commit comments

Comments
 (0)