Skip to content

Commit dec91a1

Browse files
Load build comments via GraphQL (#3475)
Incremental progress towards our eventual removal of the legacy API in favor of GraphQL. This PR exposes comments via GraphQL and updates the build summary page to use the new GraphQL fields.
1 parent 4f35b21 commit dec91a1

11 files changed

Lines changed: 527 additions & 148 deletions

File tree

app/Models/Comment.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
namespace App\Models;
66

77
use Carbon\Carbon;
8+
use Database\Factories\CommentFactory;
89
use Illuminate\Database\Eloquent\Builder;
10+
use Illuminate\Database\Eloquent\Factories\HasFactory;
911
use Illuminate\Database\Eloquent\Model;
1012
use Illuminate\Database\Eloquent\Relations\HasOne;
1113

@@ -21,6 +23,9 @@
2123
*/
2224
class Comment extends Model
2325
{
26+
/** @use HasFactory<CommentFactory> */
27+
use HasFactory;
28+
2429
public const STATUS_NORMAL = 0;
2530
public const STATUS_FIX_IN_PROGRESS = 1;
2631
public const STATUS_FIXED = 2;
@@ -35,6 +40,7 @@ class Comment extends Model
3540
'buildid',
3641
'text',
3742
'status',
43+
'timestamp',
3844
];
3945

4046
protected $casts = [

app/cdash/tests/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ add_feature_test_in_transaction(/Feature/GraphQL/PinnedTestMeasurementTypeTest)
229229

230230
add_feature_test_in_transaction(/Feature/GraphQL/BuildErrorTypeTest)
231231

232+
add_feature_test_in_transaction(/Feature/GraphQL/CommentTypeTest)
233+
232234
add_feature_test_in_transaction(/Feature/RouteAccessTest)
233235

234236
add_feature_test_in_transaction(/Feature/Monitor)
@@ -399,6 +401,8 @@ add_browser_test(/Browser/Pages/BuildCoveragePageTest)
399401

400402
add_browser_test(/Browser/Pages/BuildDynamicAnalysisIdPageTest)
401403

404+
add_browser_test(/Browser/Pages/BuildSummaryPageTest)
405+
402406
add_php_test(image)
403407
set_tests_properties(image PROPERTIES DEPENDS /CDash/XmlHandler/UpdateHandler)
404408

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Database\Factories;
4+
5+
use App\Models\Comment;
6+
use Illuminate\Database\Eloquent\Factories\Factory;
7+
use Illuminate\Support\Carbon;
8+
use Illuminate\Support\Str;
9+
10+
/**
11+
* @extends Factory<Comment>
12+
*/
13+
class CommentFactory extends Factory
14+
{
15+
/**
16+
* Define the model's default state.
17+
*
18+
* @return array<string, mixed>
19+
*/
20+
public function definition(): array
21+
{
22+
return [
23+
'text' => Str::uuid()->toString(),
24+
'status' => 0,
25+
'timestamp' => Carbon::now(),
26+
];
27+
}
28+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Support\Facades\DB;
5+
6+
return new class extends Migration {
7+
public function up(): void
8+
{
9+
DB::statement("ALTER TABLE comments DROP CONSTRAINT {$this->getConstraintName('buildid')}");
10+
DB::statement("ALTER TABLE comments DROP CONSTRAINT {$this->getConstraintName('userid')}");
11+
12+
// Re-add constraints with ON DELETE CASCADE.
13+
DB::statement('ALTER TABLE comments ADD FOREIGN KEY (buildid) REFERENCES build (id) ON DELETE CASCADE');
14+
DB::statement('ALTER TABLE comments ADD FOREIGN KEY (userid) REFERENCES users (id) ON DELETE CASCADE');
15+
}
16+
17+
private function getConstraintName(string $columnName): string
18+
{
19+
// Get foreign key constraint name. The constraint name isn't guaranteed to be the same on
20+
// all systems, due to MySQL->Postgres migration differences.
21+
return DB::select("
22+
SELECT tc.constraint_name as constraint_name
23+
FROM information_schema.table_constraints AS tc
24+
JOIN information_schema.key_column_usage AS kcu
25+
ON tc.constraint_name = kcu.constraint_name
26+
AND tc.table_schema = kcu.table_schema
27+
WHERE tc.constraint_type = 'FOREIGN KEY'
28+
AND tc.table_name = 'comments'
29+
AND kcu.column_name = '{$columnName}'
30+
LIMIT 1;
31+
")[0]->constraint_name;
32+
}
33+
34+
public function down(): void
35+
{
36+
}
37+
};

graphql/schema.graphql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,8 @@ type Build {
821821
): [DynamicAnalysis!]! @hasMany(type: CONNECTION) @orderBy(column: "id", direction: ASC)
822822

823823
updateStep: Update @belongsTo
824+
825+
comments: [Comment!]! @hasMany(type: CONNECTION) @orderBy(column: "timestamp", direction: DESC)
824826
}
825827

826828

@@ -1362,3 +1364,17 @@ type PinnedTestMeasurement {
13621364
"Note: Only the relative positions of PinnedTestMeasurements are guaranteed."
13631365
position: Int!
13641366
}
1367+
1368+
1369+
type Comment {
1370+
"Unique primary key."
1371+
id: ID!
1372+
1373+
"Comment body."
1374+
text: String!
1375+
1376+
"Creation timestamp."
1377+
timestamp: DateTimeTz!
1378+
1379+
user: User!
1380+
}

resources/js/vue/components/BuildSummary.vue

Lines changed: 108 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -574,95 +574,77 @@
574574
<br>
575575

576576
<!-- Display comments for this build -->
577-
<div
578-
v-if="cdash.notes.length > 0 || cdash.user.id > 0"
579-
class="title-divider"
580-
>
581-
Comments ({{ cdash.notes.length }})
582-
</div>
583-
584-
<div v-if="cdash.notes.length > 0">
585-
<div v-for="note in cdash.notes">
586-
<b>{{ note.status }}</b> by <b>{{ note.user }}</b> at {{ note.date }}
587-
<code-box :text="note.text" />
588-
<hr>
577+
<loading-indicator :is-loading="!comments">
578+
<div
579+
v-if="comments.length > 0 || cdash.user.id > 0"
580+
class="title-divider"
581+
>
582+
Comments ({{ comments.length }})
589583
</div>
590-
</div>
591584

585+
<div v-if="comments.length > 0">
586+
<div v-for="{node: comment} in comments">
587+
<b>{{ comment.user.firstname }} {{ comment.user.lastname }}</b> {{ Utils.formatRelativeTimestamp(comment.timestamp) }}
588+
<code-box :text="comment.text" />
589+
<hr>
590+
</div>
591+
<br>
592+
</div>
592593

593-
<div v-if="cdash.user.id > 0">
594-
<!-- Add Comments -->
595-
<div class="tw-flex tw-flex-row">
596-
<img
597-
width="20"
598-
height="20"
599-
:src="$baseURL + '/img/document.png'"
600-
title="graph"
601-
>
602-
<a
603-
id="toggle_note"
604-
class="tw-link tw-link-hover"
605-
@click="toggleNote()"
594+
<div v-if="cdash.user.id > 0">
595+
<!-- Add Comments -->
596+
<div class="tw-flex tw-flex-row">
597+
<img
598+
width="20"
599+
height="20"
600+
:src="$baseURL + '/img/document.png'"
601+
title="graph"
602+
>
603+
<a
604+
id="toggle_note"
605+
class="tw-link tw-link-hover"
606+
@click="toggleNote()"
607+
>
608+
Add a comment to this Build
609+
</a>
610+
</div>
611+
<div
612+
v-show="showNote"
613+
id="new_note_div"
606614
>
607-
Add a comment to this Build
608-
</a>
609-
</div>
610-
<div
611-
v-show="showNote"
612-
id="new_note_div"
613-
>
614-
<table>
615-
<tbody>
616-
<tr>
617-
<td><b>Comment:</b></td>
618-
<td>
619-
<textarea
620-
id="note_text"
621-
v-model="cdash.noteText"
622-
class="tw-textarea tw-textarea-bordered"
623-
cols="50"
624-
rows="5"
625-
/>
626-
</td>
627-
</tr>
628-
<tr>
629-
<td><b>Status:</b></td>
630-
<td>
631-
<select
632-
id="note_status"
633-
v-model="cdash.noteStatus"
634-
class="tw-select tw-select-bordered"
635-
>
636-
<option value="0">
637-
Simple Note
638-
</option>
639-
<option value="1">
640-
Fix in progress
641-
</option>
642-
<option value="2">
643-
Fixed
644-
</option>
645-
</select>
646-
</td>
647-
</tr>
648-
<tr>
649-
<td />
650-
<td>
651-
<button
652-
id="add_note"
653-
class="tw-btn"
654-
:disabled="!cdash.noteText"
655-
@click="addNote()"
656-
>
657-
Add Note
658-
</button>
659-
</td>
660-
</tr>
661-
</tbody>
662-
</table>
615+
<table>
616+
<tbody>
617+
<tr>
618+
<td><b>Comment:</b></td>
619+
<td>
620+
<textarea
621+
id="note_text"
622+
v-model="cdash.noteText"
623+
class="tw-textarea tw-textarea-bordered"
624+
cols="50"
625+
rows="5"
626+
/>
627+
</td>
628+
</tr>
629+
<tr>
630+
<td />
631+
<td>
632+
<button
633+
id="add_note"
634+
class="tw-btn"
635+
:disabled="!cdash.noteText"
636+
@click="addNote()"
637+
>
638+
Add Note
639+
</button>
640+
</td>
641+
</tr>
642+
</tbody>
643+
</table>
644+
</div>
645+
<br>
663646
</div>
664-
<br>
665-
</div>
647+
</loading-indicator>
666648

667649
<!-- Graphs -->
668650
<div class="title-divider">
@@ -1029,6 +1011,9 @@ import {
10291011
import CodeBox from './shared/CodeBox.vue';
10301012
import LoadingIndicator from './shared/LoadingIndicator.vue';
10311013
import BuildSummaryCard from './shared/BuildSummaryCard.vue';
1014+
import gql from 'graphql-tag';
1015+
import Utils from './shared/Utils';
1016+
10321017
export default {
10331018
name: 'BuildSummary',
10341019
components: {BuildSummaryCard, LoadingIndicator, CodeBox, FontAwesomeIcon},
@@ -1067,12 +1052,48 @@ export default {
10671052
};
10681053
},
10691054
1055+
apollo: {
1056+
comments: {
1057+
query: gql`
1058+
query($buildId: ID) {
1059+
build(id: $buildId) {
1060+
id
1061+
comments {
1062+
edges {
1063+
node {
1064+
id
1065+
text
1066+
timestamp
1067+
user {
1068+
id
1069+
firstname
1070+
lastname
1071+
}
1072+
}
1073+
}
1074+
}
1075+
}
1076+
}
1077+
`,
1078+
update: data => data?.build?.comments?.edges,
1079+
variables() {
1080+
return {
1081+
buildId: this.buildId,
1082+
};
1083+
},
1084+
},
1085+
},
1086+
10701087
computed: {
10711088
FA() {
10721089
return {
10731090
faQuestionCircle,
10741091
};
10751092
},
1093+
1094+
Utils() {
1095+
return Utils;
1096+
},
10761097
},
10771098
10781099
async mounted () {
@@ -1302,17 +1323,16 @@ export default {
13021323
Status: this.cdash.noteStatus,
13031324
AddNote: this.cdash.noteText,
13041325
})
1305-
.then(response => {
1306-
// Add the newly created note to our list.
1307-
this.cdash.notes.push(response.data.note);
1326+
.then(() => {
1327+
// Add the newly created note to our list.
1328+
this.$apollo.queries.comments.refetch();
13081329
})
13091330
.catch(error => {
13101331
// Display the error.
13111332
this.cdash.error = error;
13121333
console.log(error);
13131334
});
13141335
},
1315-
13161336
},
13171337
};
13181338
</script>

0 commit comments

Comments
 (0)