Skip to content

[ISSUE-36] Allow to configure how deep the schema inspector should go for type/ofType sub-queries #37

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ schema_object/*
!schema_object/.gitkeep
.phpunit.result.cache
/build/
composer.lock
composer.lock
composer.phar
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ php vendor/bin/generate_schema_objects \
--namespace "Vendor\Custom\Namespace"
```

## Notes

### Dealing with `PHP Fatal error: Uncaught RuntimeException: Reached the limit of nesting in type info in ...SchemaGenerator\SchemaClassGenerator.php`
You might encounter this error when the schema is deeply nested for the types.
You can increase the depth of "type / ofType" by providing the `-D` option
(or `--type-of-type-depth`) when running the command. The default value is 4.

# Usage
In all the examples below I'm going to use the super cool public Pokemon GraphQL API as an illustration.

Expand Down
29 changes: 24 additions & 5 deletions bin/generate_schema_objects
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,24 @@ use GraphQL\SchemaGenerator\SchemaClassGenerator;

$autoLoadFiles = [__DIR__ . '/../vendor/autoload.php', __DIR__ . '/../../../autoload.php'];

function readConfig()
/**
* @return array{
* 0: string,
* 1: string,
* 2: array<string, string>,
* 3: string,
* 4: int,
* }
*/
function readConfig(): array
{
$shortOptions = implode("", [
'u:',
'h:',
'v:',
'd:',
'n:'
'n:',
'D:',
]);

$longOptions = [
Expand All @@ -23,6 +33,7 @@ function readConfig()
'authorization-header-value:',
'directory:',
'namespace:',
'type-of-type-depth:',
];

$options = getopt($shortOptions, $longOptions);
Expand All @@ -32,14 +43,21 @@ 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 = [];

if (!empty($authHeaderName)) {
$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,
(int) $typeOfTypeDepth,
];
}

// Require autoload.php depending on environment
Expand All @@ -55,16 +73,17 @@ 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);

print "-------------------------------------------\n";
print "Generating schema objects from schema types\n";
print "Using \"type / ofType\" depth: $typeOfTypeDepth\n";
print "-------------------------------------------\n";

$scanner->generateRootQueryObject();
$scanner->generateRootQueryObject($typeOfTypeDepth);

print "-------------------------------------------\n";
print "Schema objects generation complete\n";
Expand Down
49 changes: 29 additions & 20 deletions src/SchemaGenerator/SchemaClassGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -55,19 +56,21 @@ class SchemaClassGenerator
*/
public function __construct(Client $client, string $writeDir = '', string $namespace = ObjectBuilderInterface::DEFAULT_NAMESPACE)
{
$this->schemaInspector = new SchemaInspector($client);
$this->schemaInspector = new SchemaInspector($client, new TypeSubQueryGenerator());
$this->generatedObjects = [];
$this->writeDir = $writeDir;
$this->generationNamespace = $namespace;
$this->setWriteDir();
}

/**
* @param int $typeOfTypeDepth
*
* @return bool
*/
public function generateRootQueryObject(): bool
public function generateRootQueryObject(int $typeOfTypeDepth = 4): bool
{
$objectArray = $this->schemaInspector->getQueryTypeSchema();
$objectArray = $this->schemaInspector->getQueryTypeSchema($typeOfTypeDepth);
$rootObjectName = QueryObject::ROOT_QUERY_OBJECT_NAME;
$queryTypeName = $objectArray['name'];
//$rootObjectDescr = $objectArray['description'];
Expand All @@ -79,7 +82,7 @@ public function generateRootQueryObject(): bool
$this->generatedObjects[$queryTypeName] = true;

$queryObjectBuilder = new QueryObjectClassBuilder($this->writeDir, $rootObjectName, $this->generationNamespace);
$this->appendQueryObjectFields($queryObjectBuilder, $rootObjectName, $objectArray['fields']);
$this->appendQueryObjectFields($queryObjectBuilder, $rootObjectName, $objectArray['fields'], $typeOfTypeDepth);
$queryObjectBuilder->build();

return true;
Expand All @@ -91,8 +94,9 @@ public function generateRootQueryObject(): bool
* @param QueryObjectClassBuilder $queryObjectBuilder
* @param string $currentTypeName
* @param array $fieldsArray
* @param int $typeOfTypeDepth
*/
private function appendQueryObjectFields(QueryObjectClassBuilder $queryObjectBuilder, string $currentTypeName, array $fieldsArray)
private function appendQueryObjectFields(QueryObjectClassBuilder $queryObjectBuilder, string $currentTypeName, array $fieldsArray, int $typeOfTypeDepth = 4): void
{
foreach ($fieldsArray as $fieldArray) {
$name = $fieldArray['name'];
Expand All @@ -110,7 +114,7 @@ private function appendQueryObjectFields(QueryObjectClassBuilder $queryObjectBui
} else {

// Generate nested type object if it wasn't generated
$objectGenerated = $this->generateObject($typeName, $typeKind);
$objectGenerated = $this->generateObject($typeName, $typeKind, $typeOfTypeDepth);
if ($objectGenerated) {

// Generate nested type arguments object if it wasn't generated
Expand All @@ -129,20 +133,21 @@ private function appendQueryObjectFields(QueryObjectClassBuilder $queryObjectBui
/**
* @param string $objectName
* @param string $objectKind
* @param int $typeOfTypeDepth
*
* @return bool
*/
protected function generateObject(string $objectName, string $objectKind): bool
protected function generateObject(string $objectName, string $objectKind, int $typeOfTypeDepth = 4): bool
{
switch ($objectKind) {
case FieldTypeKindEnum::OBJECT:
return $this->generateQueryObject($objectName);
return $this->generateQueryObject($objectName, $typeOfTypeDepth);
case FieldTypeKindEnum::INPUT_OBJECT:
return $this->generateInputObject($objectName);
return $this->generateInputObject($objectName, $typeOfTypeDepth);
case FieldTypeKindEnum::ENUM_OBJECT:
return $this->generateEnumObject($objectName);
case FieldTypeKindEnum::UNION_OBJECT:
return $this->generateUnionObject($objectName);
return $this->generateUnionObject($objectName, $typeOfTypeDepth);
default:
print "Couldn't generate type $objectName: generating $objectKind kind is not supported yet" . PHP_EOL;
return false;
Expand All @@ -151,39 +156,41 @@ protected function generateObject(string $objectName, string $objectKind): bool

/**
* @param string $objectName
* @param int $typeOfTypeDepth
*
* @return bool
*/
protected function generateQueryObject(string $objectName): bool
protected function generateQueryObject(string $objectName, int $typeOfTypeDepth = 4): bool
{
if (array_key_exists($objectName, $this->generatedObjects)) {
return true;
}

$this->generatedObjects[$objectName] = true;
$objectArray = $this->schemaInspector->getObjectSchema($objectName);
$objectArray = $this->schemaInspector->getObjectSchema($objectName, $typeOfTypeDepth);
$objectName = $objectArray['name'];
$objectBuilder = new QueryObjectClassBuilder($this->writeDir, $objectName, $this->generationNamespace);

$this->appendQueryObjectFields($objectBuilder, $objectName, $objectArray['fields']);
$this->appendQueryObjectFields($objectBuilder, $objectName, $objectArray['fields'], $typeOfTypeDepth);
$objectBuilder->build();

return true;
}

/**
* @param string $objectName
* @param int $typeOfTypeDepth
*
* @return bool
*/
protected function generateInputObject(string $objectName): bool
protected function generateInputObject(string $objectName, int $typeOfTypeDepth = 4): bool
{
if (array_key_exists($objectName, $this->generatedObjects)) {
return true;
}

$this->generatedObjects[$objectName] = true;
$objectArray = $this->schemaInspector->getInputObjectSchema($objectName);
$objectArray = $this->schemaInspector->getInputObjectSchema($objectName, $typeOfTypeDepth);
$objectName = $objectArray['name'];
$objectBuilder = new InputObjectClassBuilder($this->writeDir, $objectName, $this->generationNamespace);

Expand All @@ -195,7 +202,7 @@ protected function generateInputObject(string $objectName): bool

$objectGenerated = true;
if ($typeKind !== FieldTypeKindEnum::SCALAR) {
$objectGenerated = $this->generateObject($typeName, $typeKind);
$objectGenerated = $this->generateObject($typeName, $typeKind, $typeOfTypeDepth);
}

if ($objectGenerated) {
Expand Down Expand Up @@ -245,10 +252,11 @@ protected function generateEnumObject(string $objectName): bool

/**
* @param string $objectName
* @param int $typeOfTypeDepth
*
* @return bool
*/
protected function generateUnionObject(string $objectName): bool
protected function generateUnionObject(string $objectName, int $typeOfTypeDepth = 4): bool
{
if (array_key_exists($objectName, $this->generatedObjects)) {
return true;
Expand All @@ -261,7 +269,7 @@ protected function generateUnionObject(string $objectName): bool
$objectBuilder = new UnionObjectBuilder($this->writeDir, $objectName, $this->generationNamespace);

foreach ($objectArray['possibleTypes'] as $possibleType) {
$this->generateObject($possibleType['name'], $possibleType['kind']);
$this->generateObject($possibleType['name'], $possibleType['kind'], $typeOfTypeDepth);
$objectBuilder->addPossibleType($possibleType['name']);
}
$objectBuilder->build();
Expand All @@ -272,10 +280,11 @@ protected function generateUnionObject(string $objectName): bool
/**
* @param string $argsObjectName
* @param array $arguments
* @param int $typeOfTypeDepth
*
* @return bool
*/
protected function generateArgumentsObject(string $argsObjectName, array $arguments): bool
protected function generateArgumentsObject(string $argsObjectName, array $arguments, int $typeOfTypeDepth = 4): bool
{
if (array_key_exists($argsObjectName, $this->generatedObjects)) {
return true;
Expand All @@ -293,7 +302,7 @@ protected function generateArgumentsObject(string $argsObjectName, array $argume

$objectGenerated = true;
if ($typeKind !== FieldTypeKindEnum::SCALAR) {
$objectGenerated = $this->generateObject($typeName, $typeKind);
$objectGenerated = $this->generateObject($typeName, $typeKind, $typeOfTypeDepth);
}

if ($objectGenerated) {
Expand Down
Loading