Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions app/GraphQL/Directives/FilterDirective.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
use GraphQL\Language\AST\InputValueDefinitionNode;
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
use GraphQL\Language\AST\ListTypeNode;
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
use GraphQL\Language\Parser;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
Expand Down Expand Up @@ -47,8 +48,7 @@ public function manipulateArgDefinition(
FieldDefinitionNode &$parentField,
ObjectTypeDefinitionNode|InterfaceTypeDefinitionNode &$parentType,
): void {
// A hack to get the return type, assuming all lists are paginated...
$returnType = Str::replaceEnd('Connection', '', $parentField->type->type->name->value);
$returnType = Str::replaceEnd('Connection', '', ASTHelper::getUnderlyingTypeName($parentField));

$multiFilterName = ASTHelper::qualifiedArgType($argDefinition, $parentField, $parentType) . 'MultiFilterInput';
$argDefinition->type = Parser::namedType($multiFilterName);
Expand All @@ -58,7 +58,10 @@ public function manipulateArgDefinition(

if (
$this->getSubFilterableFieldsForType($argDefinition, $parentType) === []
|| !str_ends_with($parentField->type->type->name->value, 'Connection')
|| (
!($parentField->type instanceof ListTypeNode)
&& Str::doesntEndWith(ASTHelper::getUnderlyingTypeName($parentField), 'Connection')
)
|| $this->getSubFilterableFieldsForType($argDefinition, $documentAST->types[$returnType]) === []
) {
// Don't create a relationship filter input type because this type has no relationships
Expand Down Expand Up @@ -245,13 +248,12 @@ private function getSubFilterableFieldsForType(InputValueDefinitionNode $argDefi
$subFilterableFieldNames = [];
foreach ($type->fields as $field) {
foreach ($field->arguments as $argument) {
foreach ($argument->directives as $directive) {
// We abuse the fact that all list return values are paginated to exclude filterable fields which
// cannot have subqueries.
if ($directive->name->value === 'filter' && str_ends_with($field->type->type->name->value, 'Connection')) {
$subFilterableFieldNames[(string) $field->name->value] = ASTHelper::qualifiedArgType($argDefinition, $field, $type) . 'MultiFilterInput';
break 2;
}
if (
ASTHelper::hasDirective($argument, 'filter')
&& Str::endsWith(ASTHelper::getUnderlyingTypeName($field), 'Connection')
) {
$subFilterableFieldNames[(string) $field->name->value] = ASTHelper::qualifiedArgType($argDefinition, $field, $type) . 'MultiFilterInput';
break;
}
}
}
Expand Down
6 changes: 1 addition & 5 deletions graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -553,13 +553,9 @@ type Test {

details: String! @filterable

"""
Test measurements for this test, sorted in descending order so newer results
appear first when using paginated queries.
"""
testMeasurements(
filters: _ @filter
): [TestMeasurement!]! @hasMany(type: CONNECTION) @orderBy(column: "id", direction: DESC)
): [TestMeasurement!]! @hasMany @orderBy(column: "id", direction: DESC)

labels(
filters: _ @filter
Expand Down
4 changes: 2 additions & 2 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,13 @@ parameters:
-
rawMessage: Access to an undefined property GraphQL\Language\AST\ListTypeNode|GraphQL\Language\AST\NamedTypeNode|GraphQL\Language\AST\NonNullTypeNode::$name.
identifier: property.notFound
count: 4
count: 1
path: app/GraphQL/Directives/FilterDirective.php

-
rawMessage: Access to an undefined property GraphQL\Language\AST\ListTypeNode|GraphQL\Language\AST\NamedTypeNode|GraphQL\Language\AST\NonNullTypeNode::$type.
identifier: property.notFound
count: 4
count: 1
path: app/GraphQL/Directives/FilterDirective.php

-
Expand Down
26 changes: 9 additions & 17 deletions resources/js/vue/components/BuildTestsPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,10 @@ export default {
details
runningTime
timeStatusCategory
testMeasurements(filters: $measurementFilters, first: 100) {
edges {
node {
id
name
value
}
}
testMeasurements(filters: $measurementFilters) {
id
name
value
}
}
}
Expand All @@ -148,14 +144,10 @@ export default {
details
runningTime
timeStatusCategory
testMeasurements(filters: $measurementFilters, first: 100) {
edges {
node {
id
name
value
}
}
testMeasurements(filters: $measurementFilters) {
id
name
value
}
}
}
Expand Down Expand Up @@ -254,7 +246,7 @@ export default {
},
...this.pinnedMeasurements.reduce((acc, name) => ({
...acc,
[name]: edge.node.testMeasurements.edges.find((measurementEdge) => measurementEdge.node.name === name)?.node.value ?? '',
[name]: edge.node.testMeasurements.find((measurementEdge) => measurementEdge.name === name)?.value ?? '',
}), {}),
};
});
Expand Down
127 changes: 127 additions & 0 deletions tests/Feature/GraphQL/FilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
namespace Tests\Feature\GraphQL;

use App\Enums\TargetType;
use App\Models\Build;
use App\Models\Project;
use App\Models\Site;
use App\Models\Test;
use App\Models\TestOutput;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Illuminate\Support\Facades\DB;
Expand Down Expand Up @@ -36,6 +39,8 @@ class FilterTest extends TestCase
*/
private array $sites = [];

private TestOutput $testOutput;

protected function setUp(): void
{
parent::setUp();
Expand All @@ -54,6 +59,12 @@ protected function setUp(): void
'normal' => $this->makeNormalUser(),
'admin' => $this->makeAdminUser(),
];

$this->testOutput = TestOutput::create([
'path' => 'a',
'command' => 'b',
'output' => 'c',
]);
}

protected function tearDown(): void
Expand All @@ -73,6 +84,8 @@ protected function tearDown(): void
}
$this->sites = [];

$this->testOutput->delete();

parent::tearDown();
}

Expand Down Expand Up @@ -1000,4 +1013,118 @@ public function testFilterByRelationshipsOfRelationships(): void
],
]);
}

public function testFilterAggregateField(): void
{
$this->projects['public1']->builds()->create([
'name' => 'build1',
'uuid' => Str::uuid()->toString(),
]);

$this->projects['public1']->builds()->create([
'name' => 'build2',
'uuid' => Str::uuid()->toString(),
]);

$this->projects['public2']->builds()->create([
'name' => 'build1',
'uuid' => Str::uuid()->toString(),
]);

$this->actingAs($this->users['admin'])->graphQL('
query($projectid: ID!, $buildname: String!) {
project(id: $projectid) {
countWithoutFilters: buildCount
countWithFilters: buildCount(filters: {
eq: {
name: $buildname
}
})
}
}
', [
'projectid' => $this->projects['public1']->id,
'buildname' => 'build1',
])->assertExactJson([
'data' => [
'project' => [
'countWithoutFilters' => 2,
'countWithFilters' => 1,
],
],
]);
}

public function testFilterNonPaginatedList(): void
{
/** @var Build $build */
$build = $this->projects['public1']->builds()->create([
'name' => 'build1',
'uuid' => Str::uuid()->toString(),
]);

/** @var Test $test */
$test = $build->tests()->create([
'testname' => Str::uuid()->toString(),
'status' => 'passed',
'outputid' => $this->testOutput->id,
]);

$measurement1 = $test->testMeasurements()->create([
'name' => Str::uuid()->toString(),
'type' => 'text/string',
'value' => Str::uuid()->toString(),
]);

$measurement2 = $test->testMeasurements()->create([
'name' => Str::uuid()->toString(),
'type' => 'text/string',
'value' => Str::uuid()->toString(),
]);

$this->actingAs($this->users['admin'])->graphQL('
query($buildid: ID!, $measurementname: String!) {
build(id: $buildid) {
tests {
edges {
node {
testMeasurements(filters: {
eq: {
name: $measurementname
}
}) {
name
type
value
}
}
}
}
}
}
', [
'buildid' => $build->id,
'measurementname' => $measurement1->name,
])->assertExactJson([
'data' => [
'build' => [
'tests' => [
'edges' => [
[
'node' => [
'testMeasurements' => [
[
'name' => $measurement1->name,
'type' => $measurement1->type,
'value' => $measurement1->value,
],
],
],
],
],
],
],
],
]);
}
}
34 changes: 12 additions & 22 deletions tests/Feature/GraphQL/TestMeasurementTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,9 @@ public function testBasicFieldAccess(): void
node {
name
testMeasurements {
edges {
node {
name
type
value
}
}
name
type
value
}
}
}
Expand All @@ -114,21 +110,15 @@ public function testBasicFieldAccess(): void
'node' => [
'name' => 'test1',
'testMeasurements' => [
'edges' => [
[
'node' => [
'name' => 'measurement 2',
'type' => 'numeric/double',
'value' => '6',
],
],
[
'node' => [
'name' => 'measurement 1',
'type' => 'text/string',
'value' => 'test',
],
],
[
'name' => 'measurement 2',
'type' => 'numeric/double',
'value' => '6',
],
[
'name' => 'measurement 1',
'type' => 'text/string',
'value' => 'test',
],
],
],
Expand Down