Skip to content

Commit 372f4cd

Browse files
authored
Merge branch '8.x-4.x' into fix-apq-with-page-caches
2 parents 00d5f56 + f4a80c5 commit 372f4cd

File tree

15 files changed

+120
-55
lines changed

15 files changed

+120
-55
lines changed

.github/workflows/testing.yml

+13-8
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,31 @@ jobs:
1414
fail-fast: false
1515
matrix:
1616
php-versions: ['7.3', '7.4', '8.0', '8.1']
17+
# Keep testing Drupal 9 untill 6 months after it got unsupported, so
18+
# untill May 1st 2024.
1719
drupal-core: ['9.5.x']
1820
phpstan: ['0']
1921
include:
2022
# Extra runs to also test on latest Drupal 10.
2123
- php-versions: '8.1'
22-
drupal-core: '10.1.x'
24+
drupal-core: '10.2.x'
2325
phpstan: '0'
24-
# We only need to run PHPStan once on the latest PHP version.
2526
- php-versions: '8.2'
26-
drupal-core: '10.1.x'
27+
drupal-core: '10.2.x'
28+
phpstan: '0'
29+
# We only need to run PHPStan once on the latest PHP version.
30+
- php-versions: '8.3'
31+
drupal-core: '10.2.x'
2732
phpstan: '1'
2833
steps:
2934
- name: Checkout Drupal core
30-
uses: actions/checkout@v3
35+
uses: actions/checkout@v4
3136
with:
3237
repository: drupal/drupal
3338
ref: ${{ matrix.drupal-core }}
3439

3540
- name: Checkout graphql module
36-
uses: actions/checkout@v3
41+
uses: actions/checkout@v4
3742
with:
3843
path: modules/graphql
3944

@@ -48,7 +53,7 @@ jobs:
4853
key: cache-v1
4954

5055
- name: Cache PHP extensions
51-
uses: actions/cache@v3
56+
uses: actions/cache@v4
5257
with:
5358
path: ${{ steps.extcache.outputs.dir }}
5459
key: ${{ steps.extcache.outputs.key }}
@@ -68,7 +73,7 @@ jobs:
6873
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
6974

7075
- name: Cache composer dependencies
71-
uses: actions/cache@v3
76+
uses: actions/cache@v4
7277
with:
7378
path: ${{ steps.composercache.outputs.dir }}
7479
# Use composer.json for key, if composer.lock is not committed.
@@ -110,7 +115,7 @@ jobs:
110115
jangregor/phpstan-prophecy:^1.0.0 \
111116
phpstan/phpstan-phpunit:^1.0.0 \
112117
phpstan/extension-installer:^1.0
113-
composer --no-interaction --no-progress --with-all-dependencies upgrade drupal/coder:8.3.21
118+
composer --no-interaction --no-progress --with-all-dependencies upgrade drupal/coder:8.3.23
114119
115120
- name: Run PHPStan
116121
if: ${{ matrix.phpstan == '1' }}

examples/graphql_composable/src/Wrappers/Response/ArticleResponse.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types = 1);
3+
declare(strict_types=1);
44

55
namespace Drupal\graphql_composable\Wrappers\Response;
66

src/Event/AlterSchemaDataEvent.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types = 1);
3+
declare(strict_types=1);
44

55
namespace Drupal\graphql\Event;
66

src/Event/AlterSchemaExtensionDataEvent.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types = 1);
3+
declare(strict_types=1);
44

55
namespace Drupal\graphql\Event;
66

src/GraphQL/Response/FileUploadResponse.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types = 1);
3+
declare(strict_types=1);
44

55
namespace Drupal\graphql\GraphQL\Response;
66

src/GraphQL/Response/Response.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types = 1);
3+
declare(strict_types=1);
44

55
namespace Drupal\graphql\GraphQL\Response;
66

src/GraphQL/Response/ResponseInterface.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types = 1);
3+
declare(strict_types=1);
44

55
namespace Drupal\graphql\GraphQL\Response;
66

src/GraphQL/Utility/FileUpload.php

+9
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ public function saveFileUpload(UploadedFile $uploaded_file, array $settings): Fi
193193
switch ($uploaded_file->getError()) {
194194
case UPLOAD_ERR_INI_SIZE:
195195
case UPLOAD_ERR_FORM_SIZE:
196+
// @todo Drupal 9 compatibility, needs to be converted to ByteSizeMarkup
197+
// later.
198+
// @phpstan-ignore-next-line
196199
$maxUploadSize = format_size($this->getMaxUploadSize($settings));
197200
$response->addViolation($this->t('The file @file could not be saved because it exceeds @maxsize, the maximum allowed size for uploads.', [
198201
'@file' => $uploaded_file->getClientOriginalName(),
@@ -268,6 +271,9 @@ public function saveFileUpload(UploadedFile $uploaded_file, array $settings): Fi
268271
$file->setSize(@filesize($temp_file_path));
269272

270273
// Validate against file_validate() first with the temporary path.
274+
// @todo Drupal 9 compatibility, needs to be converted to file validate
275+
// service later.
276+
// @phpstan-ignore-next-line
271277
$errors = file_validate($file, $validators);
272278
$maxResolution = $settings['max_resolution'] ?? 0;
273279
$minResolution = $settings['min_resolution'] ?? 0;
@@ -499,6 +505,9 @@ protected function prepareFilename(string $filename, array &$validators): string
499505
/** @var \Drupal\file\FileInterface $file */
500506
$file = $this->fileStorage->create([]);
501507
$file->setFilename($filename);
508+
// @todo Drupal 9 compatibility, needs to be converted to file
509+
// validator service later.
510+
// @phpstan-ignore-next-line
502511
$passes_validation = empty(file_validate_extensions($file, $validators['file_validate_extensions'][0]));
503512
}
504513
if (empty($validators['file_validate_extensions'][0]) || $passes_validation) {

src/Plugin/GraphQL/Schema/AlterableComposableSchema.php

+1-9
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,6 @@ protected function getSchemaDocument(array $extensions = []) {
157157
* @see \Drupal\graphql\Plugin\GraphQL\Schema\ComposableSchema::getSchemaDocument()
158158
*/
159159
protected function getExtensionDocument(array $extensions = []) {
160-
// Only use caching of the parsed document if we aren't in development mode.
161-
$cid = "extension:{$this->getPluginId()}";
162-
if (empty($this->inDevelopment) && $cache = $this->astCache->get($cid)) {
163-
return $cache->data;
164-
}
165-
166160
$extensions = array_filter(array_map(function (SchemaExtensionPluginInterface $extension) {
167161
return $extension->getExtensionDefinition();
168162
}, $extensions), function ($definition) {
@@ -176,10 +170,8 @@ protected function getExtensionDocument(array $extensions = []) {
176170
AlterSchemaExtensionDataEvent::EVENT_NAME
177171
);
178172
$ast = !empty($extensions) ? Parser::parse(implode("\n\n", $event->getSchemaExtensionData()), ['noLocation' => TRUE]) : NULL;
179-
if (empty($this->inDevelopment)) {
180-
$this->astCache->set($cid, $ast, CacheBackendInterface::CACHE_PERMANENT, ['graphql']);
181-
}
182173

174+
// No AST caching here as that will be done in getFullSchemaDocument().
183175
return $ast;
184176
}
185177

src/Plugin/GraphQL/Schema/SdlSchemaPluginBase.php

+55-20
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@
1313
use Drupal\graphql\Plugin\SchemaExtensionPluginInterface;
1414
use Drupal\graphql\Plugin\SchemaExtensionPluginManager;
1515
use Drupal\graphql\Plugin\SchemaPluginInterface;
16+
use GraphQL\Language\AST\DocumentNode;
1617
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
1718
use GraphQL\Language\AST\TypeDefinitionNode;
1819
use GraphQL\Language\AST\UnionTypeDefinitionNode;
1920
use GraphQL\Language\Parser;
21+
use GraphQL\Type\Schema;
2022
use GraphQL\Utils\BuildSchema;
2123
use GraphQL\Utils\SchemaExtender;
24+
use GraphQL\Utils\SchemaPrinter;
2225
use Symfony\Component\DependencyInjection\ContainerInterface;
2326

2427
/**
@@ -117,15 +120,8 @@ public function __construct(
117120
*/
118121
public function getSchema(ResolverRegistryInterface $registry) {
119122
$extensions = $this->getExtensions();
120-
$resolver = [$registry, 'resolveType'];
121123
$document = $this->getSchemaDocument($extensions);
122-
$schema = BuildSchema::build($document, function ($config, TypeDefinitionNode $type) use ($resolver) {
123-
if ($type instanceof InterfaceTypeDefinitionNode || $type instanceof UnionTypeDefinitionNode) {
124-
$config['resolveType'] = $resolver;
125-
}
126-
127-
return $config;
128-
});
124+
$schema = $this->buildSchema($document, $registry);
129125

130126
if (empty($extensions)) {
131127
return $schema;
@@ -135,10 +131,31 @@ public function getSchema(ResolverRegistryInterface $registry) {
135131
$extension->registerResolvers($registry);
136132
}
137133

138-
if ($extendSchema = $this->getExtensionDocument($extensions)) {
139-
return SchemaExtender::extend($schema, $extendSchema);
134+
$extendedDocument = $this->getFullSchemaDocument($schema, $extensions);
135+
if (empty($extendedDocument)) {
136+
return $schema;
140137
}
141138

139+
return $this->buildSchema($extendedDocument, $registry);
140+
}
141+
142+
/**
143+
* Create a GraphQL schema object from the given AST document.
144+
*
145+
* This method is private for now as the build/cache approach might change.
146+
*/
147+
private function buildSchema(DocumentNode $astDocument, ResolverRegistryInterface $registry): Schema {
148+
$resolver = [$registry, 'resolveType'];
149+
// Performance: only validate the schema in development mode, skip it in
150+
// production on every request.
151+
$options = empty($this->inDevelopment) ? ['assumeValid' => TRUE] : [];
152+
$schema = BuildSchema::build($astDocument, function ($config, TypeDefinitionNode $type) use ($resolver) {
153+
if ($type instanceof InterfaceTypeDefinitionNode || $type instanceof UnionTypeDefinitionNode) {
154+
$config['resolveType'] = $resolver;
155+
}
156+
157+
return $config;
158+
}, $options);
142159
return $schema;
143160
}
144161

@@ -185,6 +202,33 @@ protected function getSchemaDocument(array $extensions = []) {
185202
return $ast;
186203
}
187204

205+
/**
206+
* Returns the full AST combination of parsed schema with extensions, cached.
207+
*
208+
* This method is private for now as the build/cache approach might change.
209+
*/
210+
private function getFullSchemaDocument(Schema $schema, array $extensions): ?DocumentNode {
211+
// Only use caching of the parsed document if we aren't in development mode.
212+
$cid = "full:{$this->getPluginId()}";
213+
if (empty($this->inDevelopment) && $cache = $this->astCache->get($cid)) {
214+
return $cache->data;
215+
}
216+
217+
$ast = NULL;
218+
if ($extendAst = $this->getExtensionDocument($extensions)) {
219+
$fullSchema = SchemaExtender::extend($schema, $extendAst);
220+
// Performance: export the full schema as string and parse it again. That
221+
// way we can cache the full AST.
222+
$fullSchemaString = SchemaPrinter::doPrint($fullSchema);
223+
$ast = Parser::parse($fullSchemaString, ['noLocation' => TRUE]);
224+
}
225+
226+
if (empty($this->inDevelopment)) {
227+
$this->astCache->set($cid, $ast, CacheBackendInterface::CACHE_PERMANENT, ['graphql']);
228+
}
229+
return $ast;
230+
}
231+
188232
/**
189233
* Retrieves the parsed AST of the schema extension definitions.
190234
*
@@ -196,23 +240,14 @@ protected function getSchemaDocument(array $extensions = []) {
196240
* @throws \GraphQL\Error\SyntaxError
197241
*/
198242
protected function getExtensionDocument(array $extensions = []) {
199-
// Only use caching of the parsed document if we aren't in development mode.
200-
$cid = "extension:{$this->getPluginId()}";
201-
if (empty($this->inDevelopment) && $cache = $this->astCache->get($cid)) {
202-
return $cache->data;
203-
}
204-
205243
$extensions = array_filter(array_map(function (SchemaExtensionPluginInterface $extension) {
206244
return $extension->getExtensionDefinition();
207245
}, $extensions), function ($definition) {
208246
return !empty($definition);
209247
});
210248

211249
$ast = !empty($extensions) ? Parser::parse(implode("\n\n", $extensions), ['noLocation' => TRUE]) : NULL;
212-
if (empty($this->inDevelopment)) {
213-
$this->astCache->set($cid, $ast, CacheBackendInterface::CACHE_PERMANENT, ['graphql']);
214-
}
215-
250+
// No AST caching here as that will be done in getFullSchemaDocument().
216251
return $ast;
217252
}
218253

tests/modules/graphql_alterable_schema_test/src/EventSubscriber/GraphQlSubscriber.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types = 1);
3+
declare(strict_types=1);
44

55
namespace Drupal\graphql_alterable_schema_test\EventSubscriber;
66

tests/src/Kernel/DataProducer/EntityDefinitionTest.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ class EntityDefinitionTest extends GraphQLTestBase {
211211
[
212212
'id' => 'created',
213213
'label' => 'Authored on',
214-
'description' => 'The time that the node was created.',
214+
'description' => 'The date and time that the content was created.',
215215
'type' => 'created',
216216
'required' => FALSE,
217217
'multiple' => FALSE,
@@ -496,6 +496,11 @@ enum FieldTypes {
496496
'entity_form_display_context' => $builder->fromContext('entity_form_display'),
497497
])
498498
);
499+
500+
// @todo Different description between Drupal 9 and 10, can be removed when
501+
// Drupal 9 support is dropped.
502+
$this->fullDefinitionResult['entityDefinition']['fields'][11]['description'] =
503+
$this->container->get('entity_field.manager')->getBaseFieldDefinitions('node')['created']->getDescription();
499504
}
500505

501506
/**

tests/src/Kernel/DataProducer/EntityReferenceTest.php

+26-2
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,40 @@
55
use Drupal\Core\Field\FieldStorageDefinitionInterface;
66
use Drupal\node\Entity\Node;
77
use Drupal\node\Entity\NodeType;
8-
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
98
use Drupal\Tests\graphql\Kernel\GraphQLTestBase;
109

10+
// @todo Drupal 9 compatibility: use the deprecated trait for Drupal 9.
11+
if (strpos(\Drupal::VERSION, '9') === 0) {
12+
13+
/**
14+
* Helper trait for compatibility with Drupal 9.
15+
*
16+
* @phpcs:disable Drupal.Classes.ClassFileName.NoMatch
17+
*/
18+
trait EntityReferenceFieldCreationTrait {
19+
// @phpstan-ignore-next-line
20+
use \Drupal\Tests\field\Traits\EntityReferenceTestTrait;
21+
22+
}
23+
}
24+
else {
25+
26+
/**
27+
* Helper trait for compatibility with Drupal 10.
28+
*/
29+
trait EntityReferenceFieldCreationTrait {
30+
use \Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;
31+
32+
}
33+
}
34+
1135
/**
1236
* Tests the entity_reference data producers.
1337
*
1438
* @group graphql
1539
*/
1640
class EntityReferenceTest extends GraphQLTestBase {
17-
use EntityReferenceTestTrait;
41+
use EntityReferenceFieldCreationTrait;
1842

1943
/**
2044
* Test node that will be referenced.

tests/src/Kernel/DataProducer/EntityTest.php

+1-8
Original file line numberDiff line numberDiff line change
@@ -469,14 +469,7 @@ public function testResolveEntityRendered(): void {
469469

470470
// @todo Add metadata check.
471471
// $this->assertContains('node:1', $metadata->getCacheTags());
472-
// Rendered output is slightly different in Drupal 8 vs. 9.
473-
[$version] = explode('.', \Drupal::VERSION, 2);
474-
if ($version == 8) {
475-
$this->assertStringContainsString('<a href="/node/1" rel="bookmark"><span>' . $this->node->getTitle() . '</span>', $result);
476-
}
477-
else {
478-
$this->assertMatchesRegularExpression('#<a href="/node/1" rel="bookmark">\s*<span>' . $this->node->getTitle() . '</span>#', $result);
479-
}
472+
$this->assertMatchesRegularExpression('#<a href="/node/1" rel="bookmark">\s*<span>' . $this->node->getTitle() . '</span>#', $result);
480473
}
481474

482475
}

tests/src/Kernel/GraphQLTestBase.php

+2
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,13 @@ protected function setUp(): void {
8282
ConfigurableLanguage::create([
8383
'id' => 'fr',
8484
'weight' => 1,
85+
'label' => 'French',
8586
])->save();
8687

8788
ConfigurableLanguage::create([
8889
'id' => 'de',
8990
'weight' => 2,
91+
'label' => 'German',
9092
])->save();
9193

9294
$this->builder = new ResolverBuilder();

0 commit comments

Comments
 (0)