From 266c29bb9687f3cdd6ef5184563830e0f076f9c0 Mon Sep 17 00:00:00 2001 From: Philippe Jausions Date: Sun, 2 Feb 2025 10:13:11 +0100 Subject: [PATCH] [ISSUE-36] Allow to configure how deep the schema inspector should go for type/ofType sub-queries --- .gitignore | 3 +- bin/generate_schema_objects | 30 +++++-- src/SchemaGenerator/SchemaClassGenerator.php | 12 +-- src/SchemaGenerator/SchemaInspector.php | 85 ++++++++++-------- .../SchemaInspector/TypeSubQueryGenerator.php | 90 +++++++++++++++++++ tests/TypeSubQueryGeneratorTest.php | 77 ++++++++++++++++ 6 files changed, 251 insertions(+), 46 deletions(-) create mode 100644 src/SchemaGenerator/SchemaInspector/TypeSubQueryGenerator.php create mode 100644 tests/TypeSubQueryGeneratorTest.php diff --git a/.gitignore b/.gitignore index ced3610..73b9821 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ schema_object/* !schema_object/.gitkeep .phpunit.result.cache /build/ -composer.lock \ No newline at end of file +composer.lock +composer.phar diff --git a/bin/generate_schema_objects b/bin/generate_schema_objects index 5c51f53..c6d9c36 100755 --- a/bin/generate_schema_objects +++ b/bin/generate_schema_objects @@ -4,17 +4,28 @@ use GraphQL\Client; use GraphQL\SchemaGenerator\CodeGenerator\ObjectBuilderInterface; use GraphQL\SchemaGenerator\SchemaClassGenerator; +use GraphQL\SchemaGenerator\SchemaInspector\TypeSubQueryGenerator; $autoLoadFiles = [__DIR__ . '/../vendor/autoload.php', __DIR__ . '/../../../autoload.php']; -function readConfig() +/** + * @return array{ + * 0: string, + * 1: string, + * 2: array, + * 3: string, + * 4: int, + * } + */ +function readConfig(): array { $shortOptions = implode("", [ 'u:', 'h:', 'v:', 'd:', - 'n:' + 'n:', + 'D:', ]); $longOptions = [ @@ -23,6 +34,7 @@ function readConfig() 'authorization-header-value:', 'directory:', 'namespace:', + 'type-of-type-depth:', ]; $options = getopt($shortOptions, $longOptions); @@ -32,6 +44,7 @@ function readConfig() $authHeaderName = $options['authorization-header-name'] ?? $options['h'] ?? readline('Authorization header name: '); $authHeaderValue = $options['authorization-header-value'] ?? $options['v'] ?? readline('Authorization header value: '); $namespace = $options['n'] ?? $options['namespace'] ?? trim(readline('Custom namespace (optional): ')); + $typeOfTypeDepth = $options['type-of-type-depth'] ?? $options['D'] ?? 4; $authHeaders = []; @@ -39,7 +52,13 @@ function readConfig() $authHeaders = [$authHeaderName => $authHeaderValue]; } - return [$url, empty($customWriteDir) ? "" : $customWriteDir, $authHeaders, empty($namespace) ? ObjectBuilderInterface::DEFAULT_NAMESPACE : $namespace]; + return [ + $url, + empty($customWriteDir) ? "" : $customWriteDir, + $authHeaders, + empty($namespace) ? ObjectBuilderInterface::DEFAULT_NAMESPACE : $namespace, + $typeOfTypeDepth, + ]; } // Require autoload.php depending on environment @@ -55,13 +74,14 @@ if (!$autoLoadFound) { throw new RuntimeException('Could not find vendor/autoload.php'); } -[$endpointUrl, $customWriteDir, $authHeaders, $namespace] = readConfig(); +[$endpointUrl, $customWriteDir, $authHeaders, $namespace, $typeOfTypeDepth] = readConfig(); $client = new Client($endpointUrl, $authHeaders); -$scanner = new SchemaClassGenerator($client, $customWriteDir, $namespace); +$scanner = new SchemaClassGenerator($client, $customWriteDir, $namespace, new TypeSubQueryGenerator($typeOfTypeDepth)); print "-------------------------------------------\n"; print "Generating schema objects from schema types\n"; +print "Using \"type / ofType\" depth: $typeOfTypeDepth\n"; print "-------------------------------------------\n"; $scanner->generateRootQueryObject(); diff --git a/src/SchemaGenerator/SchemaClassGenerator.php b/src/SchemaGenerator/SchemaClassGenerator.php index e4920ad..4ae381c 100644 --- a/src/SchemaGenerator/SchemaClassGenerator.php +++ b/src/SchemaGenerator/SchemaClassGenerator.php @@ -10,6 +10,7 @@ use GraphQL\SchemaGenerator\CodeGenerator\ObjectBuilderInterface; use GraphQL\SchemaGenerator\CodeGenerator\QueryObjectClassBuilder; use GraphQL\SchemaGenerator\CodeGenerator\UnionObjectBuilder; +use GraphQL\SchemaGenerator\SchemaInspector\TypeSubQueryGenerator; use GraphQL\SchemaObject\QueryObject; use GraphQL\Util\StringLiteralFormatter; use RuntimeException; @@ -49,13 +50,14 @@ class SchemaClassGenerator /** * SchemaClassGenerator constructor. * - * @param Client $client - * @param string $writeDir - * @param string $namespace + * @param Client $client + * @param string $writeDir + * @param string $namespace + * @param TypeSubQueryGenerator|null $typeSubQueryGenerate */ - public function __construct(Client $client, string $writeDir = '', string $namespace = ObjectBuilderInterface::DEFAULT_NAMESPACE) + public function __construct(Client $client, string $writeDir = '', string $namespace = ObjectBuilderInterface::DEFAULT_NAMESPACE, ?TypeSubQueryGenerator $typeSubQueryGenerate = null) { - $this->schemaInspector = new SchemaInspector($client); + $this->schemaInspector = new SchemaInspector($client, $typeSubQueryGenerate ?? new TypeSubQueryGenerator(4)); $this->generatedObjects = []; $this->writeDir = $writeDir; $this->generationNamespace = $namespace; diff --git a/src/SchemaGenerator/SchemaInspector.php b/src/SchemaGenerator/SchemaInspector.php index 424ccc7..c425c5f 100644 --- a/src/SchemaGenerator/SchemaInspector.php +++ b/src/SchemaGenerator/SchemaInspector.php @@ -3,6 +3,7 @@ namespace GraphQL\SchemaGenerator; use GraphQL\Client; +use GraphQL\SchemaGenerator\SchemaInspector\TypeSubQueryGenerator; /** * Class SchemaInspector @@ -13,46 +14,44 @@ */ class SchemaInspector { - private const TYPE_SUB_QUERY = <<client = $client; + $this->typeSubQueryGenerate = ($typeSubQueryGenerate ?? new TypeSubQueryGenerator(4)); + + // End __construct(). + } + + + /** + * @return string + */ + private function getTypeSubQuery(): string + { + return $this->typeSubQueryGenerate->getSubTypeQuery(); + + // End getTypeSubQuery(). } + /** * @return array */ @@ -69,12 +68,12 @@ public function getQueryTypeSchema(): array description isDeprecated deprecationReason - " . static::TYPE_SUB_QUERY . " + ".$this->getTypeSubQuery()." args{ name description defaultValue - " . static::TYPE_SUB_QUERY . " + ".$this->getTypeSubQuery()." } } } @@ -83,10 +82,13 @@ public function getQueryTypeSchema(): array $response = $this->client->runRawQuery($schemaQuery, true); return $response->getData()['__schema']['queryType']; + + // End getQueryTypeSchema(). } + /** - * @param string $objectName + * @param string $objectName The name of the object * * @return array */ @@ -101,12 +103,12 @@ public function getObjectSchema(string $objectName): array description isDeprecated deprecationReason - " . static::TYPE_SUB_QUERY . " + ".$this->getTypeSubQuery()." args{ name description defaultValue - " . static::TYPE_SUB_QUERY . " + ".$this->getTypeSubQuery()." } } } @@ -114,10 +116,13 @@ public function getObjectSchema(string $objectName): array $response = $this->client->runRawQuery($schemaQuery, true); return $response->getData()['__type']; + + // End getObjectSchema(). } + /** - * @param string $objectName + * @param string $objectName The name of the object * * @return array */ @@ -131,17 +136,20 @@ public function getInputObjectSchema(string $objectName): array name description defaultValue - " . static::TYPE_SUB_QUERY . " + ".$this->getTypeSubQuery()." } } }"; $response = $this->client->runRawQuery($schemaQuery, true); return $response->getData()['__type']; + + // End getInputObjectSchema(). } + /** - * @param string $objectName + * @param string $objectName The name of the enum object * * @return array */ @@ -160,10 +168,13 @@ enumValues { $response = $this->client->runRawQuery($schemaQuery, true); return $response->getData()['__type']; + + // End getEnumObjectSchema(). } + /** - * @param string $objectName + * @param string $objectName The name of the union object * * @return array */ @@ -182,5 +193,9 @@ public function getUnionObjectSchema(string $objectName): array $response = $this->client->runRawQuery($schemaQuery, true); return $response->getData()['__type']; + + // End getUnionObjectSchema(). } + + } diff --git a/src/SchemaGenerator/SchemaInspector/TypeSubQueryGenerator.php b/src/SchemaGenerator/SchemaInspector/TypeSubQueryGenerator.php new file mode 100644 index 0000000..39c082d --- /dev/null +++ b/src/SchemaGenerator/SchemaInspector/TypeSubQueryGenerator.php @@ -0,0 +1,90 @@ + + */ + private static $ofTypes = []; + + + /** + * @param integer $depth How many levels of `ofType` to generate + */ + public function __construct(int $depth=4) + { + $this->depth = $depth; + + // End __construct(). + } + + + /** + * @return string + */ + public function getSubTypeQuery(): string + { + $ofType = $this->getOfTypeSubQuery($this->depth); + + return "type{ + name + kind + description{$ofType} +}"; + + // End getSubTypeQuery(). + } + + + /** + * @param integer $depth How many levels of `ofType` to generate + * + * @return string + */ + private function getOfTypeSubQuery($depth): string + { + if (isset(self::$ofTypes[$depth]) === false) { + self::$ofTypes[$depth] = $this->generateOfTypeSubQuery($depth, ' '); + } + + return self::$ofTypes[$depth]; + + // End getOfTypeSubQuery(). + } + + + /** + * @param integer $depth How many levels of `ofType` to generate + * @param string $indent Indentation for the sub-query + * + * @return string + */ + private function generateOfTypeSubQuery(int $depth, string $indent): string + { + if ($depth <= 0) { + return ''; + } + + $subQuery = $this->generateOfTypeSubQuery(($depth - 1), $indent.' '); + + return " +{$indent}ofType{ +{$indent} name +{$indent} kind{$subQuery} +{$indent}}"; + + // End generateOfTypeSubQuery(). + } + + +} diff --git a/tests/TypeSubQueryGeneratorTest.php b/tests/TypeSubQueryGeneratorTest.php new file mode 100644 index 0000000..c9ff682 --- /dev/null +++ b/tests/TypeSubQueryGeneratorTest.php @@ -0,0 +1,77 @@ +getSubTypeQuery(); + + $expected = <<assertEquals($expected, $actual); + + // End testItShouldGenerateSubQueryWith4OfTypeLevelsByDefault(). + } + + + /** + * @covers \GraphQL\SchemaGenerator\SchemaInspector\TypeSubQueryGenerator::getSubTypeQuery + * + * @return void + */ + public function testItShouldGenerateSubQueryWithoutOfTypeForZeroDepth(): void + { + $sut = new TypeSubQueryGenerator(0); + + $actual = $sut->getSubTypeQuery(); + + $expected = <<assertEquals($expected, $actual); + + // End testItShouldGenerateSubQueryWithoutOfTypeForZeroDepth(). + } + + +}