Skip to content

Commit 339c128

Browse files
dima koushhajenkins-bot
authored andcommitted
GQL: Validate Property ID arg on statements and qualifiers
Bug: T404834 Change-Id: Ic1f0a1b25ac81c09148878e8b2a11fd6679689d1
1 parent ae30914 commit 339c128

File tree

5 files changed

+89
-5
lines changed

5 files changed

+89
-5
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php declare( strict_types=1 );
2+
3+
namespace Wikibase\Repo\Domains\Reuse\Infrastructure\GraphQL\Schema;
4+
5+
use GraphQL\Error\Error;
6+
use GraphQL\Error\InvariantViolation;
7+
use GraphQL\Language\AST\Node;
8+
use GraphQL\Language\AST\StringValueNode;
9+
use GraphQL\Type\Definition\ScalarType;
10+
use GraphQL\Utils\Utils;
11+
use InvalidArgumentException;
12+
use Wikibase\DataModel\Entity\NumericPropertyId;
13+
14+
/**
15+
* @license GPL-2.0-or-later
16+
*/
17+
class PropertyIdType extends ScalarType {
18+
public function serialize( mixed $value ): string {
19+
if ( !$this->isValidPropertyId( $value ) ) {
20+
throw new InvariantViolation( 'Could not serialize the following value as Property ID: ' . Utils::printSafe( $value ) );
21+
}
22+
23+
return $value;
24+
}
25+
26+
public function parseValue( mixed $value ): string {
27+
if ( !$this->isValidPropertyId( $value ) ) {
28+
throw new Error( 'Cannot represent the following value as Property ID: ' . Utils::printSafeJson( $value ) );
29+
}
30+
31+
return $value;
32+
}
33+
34+
public function parseLiteral( Node $valueNode, ?array $variables = null ): string {
35+
if ( !$valueNode instanceof StringValueNode ) {
36+
throw new Error( 'Query error: Can only parse strings got: ' . $valueNode->kind, [ $valueNode ] );
37+
}
38+
39+
if ( !$this->isValidPropertyId( $valueNode->value ) ) {
40+
throw new Error( 'Not a valid Property ID: ' . Utils::printSafeJson( $valueNode->value ), [ $valueNode ] );
41+
}
42+
43+
return $valueNode->value;
44+
}
45+
46+
private function isValidPropertyId( mixed $id ): bool {
47+
if ( !is_string( $id ) ) {
48+
return false;
49+
}
50+
51+
try {
52+
new NumericPropertyId( $id ); // @phan-suppress-current-line PhanNoopNew
53+
return true;
54+
} catch ( InvalidArgumentException ) {
55+
return false;
56+
}
57+
}
58+
}

repo/domains/reuse/src/Infrastructure/GraphQL/Schema/Schema.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public function __construct(
2626
private readonly PropertyValuePairType $propertyValuePairType,
2727
private readonly ValueType $valueType,
2828
private readonly ValueTypeType $valueTypeType,
29+
private readonly PropertyIdType $propertyIdType
2930
) {
3031
parent::__construct( [
3132
'query' => new ObjectType( [
@@ -99,7 +100,7 @@ private function itemType(): ObjectType {
99100
// @phan-suppress-next-line PhanUndeclaredInvokeInCallable
100101
'type' => Type::nonNull( Type::listOf( $this->statementType() ) ),
101102
'args' => [
102-
'propertyId' => Type::nonNull( Type::string() ),
103+
'propertyId' => Type::nonNull( $this->propertyIdType ),
103104
],
104105
'resolve' => fn( Item $item, array $args ) => $item->statements
105106
->getStatementsByPropertyId( new NumericPropertyId( $args[ 'propertyId' ] ) ),
@@ -124,7 +125,7 @@ private function statementType(): ObjectType {
124125
// @phan-suppress-next-line PhanUndeclaredInvokeInCallable
125126
'type' => Type::nonNull( Type::listOf( $this->propertyValuePairType ) ),
126127
'args' => [
127-
'propertyId' => Type::nonNull( Type::string() ),
128+
'propertyId' => Type::nonNull( $this->propertyIdType ),
128129
],
129130
'resolve' => fn( Statement $statement, $args ) => $statement->qualifiers
130131
->getQualifiersByPropertyId( new NumericPropertyId( $args[ 'propertyId' ] ) ),

repo/domains/reuse/src/Infrastructure/GraphQL/schema.graphql

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type Item {
1010
description(languageCode: LanguageCode!): String
1111
aliases(languageCode: LanguageCode!): [String]!
1212
sitelink(siteId: SiteId!): Sitelink
13-
statements(propertyId: String!): [Statement]!
13+
statements(propertyId: PropertyId!): [Statement]!
1414
}
1515

1616
scalar LanguageCode
@@ -22,10 +22,12 @@ type Sitelink {
2222
url: String!
2323
}
2424

25+
scalar PropertyId
26+
2527
type Statement {
2628
id: String!
2729
rank: Rank!
28-
qualifiers(propertyId: String!): [PropertyValuePair]!
30+
qualifiers(propertyId: PropertyId!): [PropertyValuePair]!
2931
references: [Reference]!
3032
property: PredicateProperty!
3133
value: Value

repo/domains/reuse/src/WbReuse.ServiceWiring.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Wikibase\Repo\Domains\Reuse\Infrastructure\GraphQL\Schema\ItemIdType;
1515
use Wikibase\Repo\Domains\Reuse\Infrastructure\GraphQL\Schema\LanguageCodeType;
1616
use Wikibase\Repo\Domains\Reuse\Infrastructure\GraphQL\Schema\PredicatePropertyType;
17+
use Wikibase\Repo\Domains\Reuse\Infrastructure\GraphQL\Schema\PropertyIdType;
1718
use Wikibase\Repo\Domains\Reuse\Infrastructure\GraphQL\Schema\PropertyValuePairType;
1819
use Wikibase\Repo\Domains\Reuse\Infrastructure\GraphQL\Schema\Schema;
1920
use Wikibase\Repo\Domains\Reuse\Infrastructure\GraphQL\Schema\SiteIdType;
@@ -58,7 +59,8 @@
5859
$predicatePropertyType,
5960
new PropertyValuePairType( $predicatePropertyType, $valueType, $valueTypeType ),
6061
$valueType,
61-
$valueTypeType
62+
$valueTypeType,
63+
new PropertyIdType()
6264
);
6365
},
6466
'WbReuse.GraphQLService' => function( MediaWikiServices $services ): GraphQLService {

repo/domains/reuse/tests/phpunit/Infrastructure/GraphQL/GraphQLServiceTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,27 @@ public static function errorsProvider(): Generator {
590590
"Not a valid language code: \"$languageCode\"",
591591
];
592592
}
593+
594+
$propertyId = 'not-a-valid-property-id';
595+
yield 'validates property ID' => [
596+
"{ item(id: \"$itemId\") {
597+
statements(propertyId: \"$propertyId\") { id }
598+
} }",
599+
"Not a valid Property ID: \"$propertyId\"",
600+
];
601+
602+
$qualifierPropertyId = 'not-a-valid-property-id';
603+
$validPropertyId = 'P123';
604+
yield 'validates property ID for qualifiers' => [
605+
"{ item(id: \"$itemId\") {
606+
statements(propertyId: \"$validPropertyId\") {
607+
qualifiers(propertyId: \"$qualifierPropertyId\") {
608+
valueType
609+
}
610+
}
611+
} }",
612+
"Not a valid Property ID: \"$propertyId\"",
613+
];
593614
}
594615

595616
private function newGraphQLService(

0 commit comments

Comments
 (0)