Skip to content

Commit 5738de2

Browse files
Add updateProject GraphQL mutation (#3438)
This commit adds an `updateProject` mutation which will form the basis for the overhaul of the project settings page. Note that our infrastructure doesn't support multi-part GraphQL requests yet, so I plan to create a separate dedicated route for project logo uploads, outside the GraphQL API.
1 parent 959e54b commit 5738de2

File tree

7 files changed

+588
-2
lines changed

7 files changed

+588
-2
lines changed

app/GraphQL/Mutations/AbstractMutation.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ abstract class AbstractMutation
1313
/**
1414
* @param array<string,mixed> $args
1515
*/
16-
final public function __invoke(null $_, array $args): self
16+
public function __invoke(null $_, array $args): self
1717
{
1818
try {
1919
$this->mutate($args);
@@ -30,5 +30,7 @@ final public function __invoke(null $_, array $args): self
3030
*
3131
* @param array<string,mixed> $args
3232
*/
33-
abstract protected function mutate(array $args): void;
33+
protected function mutate(array $args): void
34+
{
35+
}
3436
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\GraphQL\Mutations;
6+
7+
use App\Models\Project;
8+
use Illuminate\Support\Facades\Gate;
9+
10+
final class UpdateProject extends AbstractMutation
11+
{
12+
public ?Project $project = null;
13+
14+
/**
15+
* @param array<string,mixed> $args
16+
*/
17+
public function __invoke(null $_, array $args): self
18+
{
19+
$project = Project::find((int) $args['id']);
20+
21+
Gate::authorize('update', $project);
22+
23+
unset($args['id']);
24+
25+
$project?->update($args);
26+
27+
$this->project = $project;
28+
29+
return $this;
30+
}
31+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace App\GraphQL\Validators;
4+
5+
use App\Models\Project;
6+
use App\Rules\ProjectAuthenticateSubmissionsRule;
7+
use App\Rules\ProjectNameRule;
8+
use App\Rules\ProjectVisibilityRule;
9+
use Illuminate\Validation\Rule;
10+
use Nuwave\Lighthouse\Validation\Validator;
11+
12+
final class UpdateProjectInputValidator extends Validator
13+
{
14+
public function rules(): array
15+
{
16+
return [
17+
'name' => [
18+
Rule::unique(Project::class, 'name'),
19+
new ProjectNameRule(),
20+
],
21+
'visibility' => [
22+
new ProjectVisibilityRule(),
23+
],
24+
'authenticateSubmissions' => [
25+
new ProjectAuthenticateSubmissionsRule(),
26+
],
27+
];
28+
}
29+
30+
public function messages(): array
31+
{
32+
return [
33+
'name.unique' => 'A project with this name already exists.',
34+
];
35+
}
36+
}

app/cdash/tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@ add_feature_test_in_transaction(/Feature/Mail/AuthTokenExpiredTest)
256256

257257
add_feature_test_in_transaction(/Feature/GraphQL/Mutations/CreateProjectTest)
258258

259+
add_feature_test_in_transaction(/Feature/GraphQL/Mutations/UpdateProjectTest)
260+
259261
add_feature_test_in_transaction(/Feature/GraphQL/Mutations/UpdateSiteDescriptionTest)
260262

261263
add_feature_test_in_transaction(/Feature/GraphQL/Mutations/InviteToProjectTest)

graphql/schema.graphql

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ type Mutation {
7676
"Create a new project."
7777
createProject(input: CreateProjectInput! @spread): Project @field(resolver: "CreateProject")
7878

79+
updateProject(input: UpdateProjectInput! @spread): UpdateProjectMutationPayload! @field(resolver: "UpdateProject")
80+
7981
"Subscribe the current user to the specified site."
8082
claimSite(input: ClaimSiteInput! @spread): ClaimSiteMutationPayload! @field(resolver: "ClaimSite")
8183

@@ -376,6 +378,77 @@ input CreateProjectInput @validator {
376378
}
377379

378380

381+
input UpdateProjectInput @validator {
382+
id: ID!
383+
384+
name: String
385+
386+
description: String
387+
388+
homeUrl: Url @rename(attribute: "homeurl")
389+
390+
vcsViewer: VcsViewer @rename(attribute: "cvsviewertype")
391+
392+
vcsUrl: Url @rename(attribute: "cvsurl")
393+
394+
bugTracker: BugTracker @rename(attribute: "bugtrackertype")
395+
396+
bugTrackerUrl: Url @rename(attribute: "bugtrackerurl")
397+
398+
bugTrackerNewIssueUrl: Url @rename(attribute: "bugtrackernewissueurl")
399+
400+
documentationUrl: Url @rename(attribute: "documentationurl")
401+
402+
testDataUrl: Url @rename(attribute: "testingdataurl")
403+
404+
visibility: ProjectVisibility @rename(attribute: "public")
405+
406+
authenticateSubmissions: Boolean @rename(attribute: "authenticatesubmissions")
407+
408+
ldapFilter: String @rename(attribute: "ldapfilter")
409+
410+
coverageThreshold: Int @rename(attribute: "coveragethreshold")
411+
412+
nightlyTime: Time @rename(attribute: "nightlytime")
413+
414+
emailLowCoverage: Boolean @rename(attribute: "emaillowcoverage")
415+
416+
emailTestTimingChanged: Boolean @rename(attribute: "emailtesttimingchanged")
417+
418+
emailBrokenSubmissions: Boolean @rename(attribute: "emailbrokensubmission")
419+
420+
emailRedundantFailures: Boolean @rename(attribute: "emailredundantfailures")
421+
422+
testTimeStdMultiplier: Float @rename(attribute: "testtimestd")
423+
424+
testTimeStdThreshold: Float @rename(attribute: "testtimestdthreshold")
425+
426+
enableTestTiming: Boolean @rename(attribute: "showtesttime")
427+
428+
timeStatusFailureThreshold: Int @rename(attribute: "testtimemaxstatus")
429+
430+
emailMaxItems: Int @rename(attribute: "emailmaxitems")
431+
432+
emailMaxCharacters: Int @rename(attribute: "emailmaxchars")
433+
434+
displayLabels: Boolean @rename(attribute: "displaylabels")
435+
436+
autoRemoveTimeFrame: Int @rename(attribute: "autoremovetimeframe")
437+
438+
autoRemoveMaxBuilds: Int @rename(attribute: "autoremovemaxbuilds")
439+
440+
fileUploadLimit: Int @rename(attribute: "uploadquota")
441+
442+
showCoverageCode: Boolean @rename(attribute: "showcoveragecode")
443+
444+
shareLabelFilters: Boolean @rename(attribute: "sharelabelfilters")
445+
446+
showViewSubProjectsLink: Boolean @rename(attribute: "viewsubprojectslink")
447+
448+
banner: String
449+
}
450+
451+
379452
type ProjectInvitation {
380453
id: ID!
381454

@@ -917,6 +990,15 @@ type Site {
917990
): [User!]! @belongsToMany(type: CONNECTION) @orderBy(column: "id")
918991
}
919992

993+
994+
type UpdateProjectMutationPayload implements MutationPayloadInterface {
995+
"Optional error message."
996+
message: String
997+
998+
project: Project
999+
}
1000+
1001+
9201002
input ClaimSiteInput {
9211003
siteId: ID!
9221004
}

phpstan-baseline.neon

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,12 @@ parameters:
360360
count: 1
361361
path: app/GraphQL/Mutations/UpdatePinnedTestMeasurementOrder.php
362362

363+
-
364+
rawMessage: Cannot cast mixed to int.
365+
identifier: cast.int
366+
count: 1
367+
path: app/GraphQL/Mutations/UpdateProject.php
368+
363369
-
364370
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()'
365371
identifier: method.childParameterType
@@ -27153,6 +27159,12 @@ parameters:
2715327159
count: 1
2715427160
path: tests/Feature/GraphQL/Mutations/RevokeProjectInvitationTest.php
2715527161

27162+
-
27163+
rawMessage: 'Parameter #1 $user of method Illuminate\Foundation\Testing\TestCase::actingAs() expects Illuminate\Contracts\Auth\Authenticatable, App\Models\User|null given.'
27164+
identifier: argument.type
27165+
count: 2
27166+
path: tests/Feature/GraphQL/Mutations/UpdateProjectTest.php
27167+
2715627168
-
2715727169
rawMessage: 'Method Tests\Feature\GraphQL\ProjectInvitationTypeTest::testAdminCanViewInvitations() throws checked exception OverflowException but it''s missing from the PHPDoc @throws tag.'
2715827170
identifier: missingType.checkedException

0 commit comments

Comments
 (0)