Skip to content

Commit

Permalink
Add support for mutations
Browse files Browse the repository at this point in the history
  • Loading branch information
jorrit committed Aug 23, 2024
1 parent 17d9a75 commit 5f16e12
Show file tree
Hide file tree
Showing 6 changed files with 312 additions and 39 deletions.
126 changes: 126 additions & 0 deletions src/SchemaGenerator/CodeGenerator/MutationObjectClassBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php

namespace GraphQL\SchemaGenerator\CodeGenerator;

use GraphQL\Enumeration\FieldTypeKindEnum;
use GraphQL\SchemaGenerator\CodeGenerator\CodeFile\ClassFile;
use GraphQL\SchemaObject\QueryObject;
use GraphQL\Util\StringLiteralFormatter;

/**
* Class QueryObjectClassBuilder
*
* @package GraphQL\SchemaManager\CodeGenerator
*/
class MutationObjectClassBuilder extends ObjectClassBuilder
{
/**
* QueryObjectClassBuilder constructor.
*
* @param string $writeDir
* @param string $objectName
* @param string $namespace
*/
public function __construct(string $writeDir, string $objectName, string $namespace = self::DEFAULT_NAMESPACE)
{
$className = $objectName . 'MutationObject';

$this->classFile = new ClassFile($writeDir, $className);
$this->classFile->setNamespace($namespace);
if ($namespace !== self::DEFAULT_NAMESPACE) {
$this->classFile->addImport('GraphQL\\SchemaObject\\MutationObject');
}
$this->classFile->extendsClass('MutationObject');

// Special case for handling root query object
if ($objectName === QueryObject::ROOT_QUERY_OBJECT_NAME) {
$objectName = '';
}
$this->classFile->addConstant('OBJECT_NAME', $objectName);
}

/**
* @param string $fieldName
*/
public function addScalarField(string $fieldName, bool $isDeprecated, ?string $deprecationReason)
{
$upperCamelCaseProp = StringLiteralFormatter::formatUpperCamelCase($fieldName);
$this->addSimpleSelector($fieldName, $upperCamelCaseProp, $isDeprecated, $deprecationReason);
}

/**
* @param string $fieldName
* @param string $typeName
* @param string $typeKind
* @param string|null $argsObjectName
* @param bool $isDeprecated
* @param string|null $deprecationReason
*/
public function addObjectField(string $fieldName, string $typeName, string $typeKind, ?string $argsObjectName, bool $isDeprecated, ?string $deprecationReason)
{
$upperCamelCaseProp = StringLiteralFormatter::formatUpperCamelCase($fieldName);
$this->addObjectSelector($fieldName, $upperCamelCaseProp, $typeName, $typeKind, $argsObjectName, $isDeprecated, $deprecationReason);
}

/**
* @param string $propertyName
* @param string $upperCamelName
* @param bool $isDeprecated
* @param string|null $deprecationReason
*/
protected function addSimpleSelector(string $propertyName, string $upperCamelName, bool $isDeprecated, ?string $deprecationReason)
{
$method = "public function select$upperCamelName()
{
\$this->selectField(\"$propertyName\");
return \$this;
}";
$this->classFile->addMethod($method, $isDeprecated, $deprecationReason);
}

/**
* @param string $fieldName
* @param string $upperCamelName
* @param string $fieldTypeName
* @param string $fieldTypeKind
* @param string|null $argsObjectName
* @param bool $isDeprecated
* @param string|null $deprecationReason
*/
protected function addObjectSelector(string $fieldName, string $upperCamelName, string $fieldTypeName, string $fieldTypeKind, ?string $argsObjectName, bool $isDeprecated, ?string $deprecationReason)
{
$objectClass = $fieldTypeName . ($fieldTypeKind === FieldTypeKindEnum::UNION_OBJECT ? 'UnionObject' : 'QueryObject');

if ($argsObjectName === null) {
$method = "public function select$upperCamelName()
{
\$object = new $objectClass(\"$fieldName\");
\$this->selectField(\$object);
return \$object;
}";
} else {
$method = "public function select$upperCamelName($argsObjectName \$argsObject = null)
{
\$object = new $objectClass(\"$fieldName\");
if (\$argsObject !== null) {
\$object->appendArguments(\$argsObject->toArray());
}
\$this->selectField(\$object);
return \$object;
}";
}

$this->classFile->addMethod($method, $isDeprecated, $deprecationReason);
}

/**
* This method builds the class and writes it to the file system
*/
public function build(): void
{
$this->classFile->writeFile();
}
}
104 changes: 71 additions & 33 deletions src/SchemaGenerator/SchemaClassGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
use GraphQL\SchemaGenerator\CodeGenerator\EnumObjectBuilder;
use GraphQL\SchemaGenerator\CodeGenerator\InputObjectClassBuilder;
use GraphQL\SchemaGenerator\CodeGenerator\InterfaceObjectBuilder;
use GraphQL\SchemaGenerator\CodeGenerator\MutationObjectClassBuilder;
use GraphQL\SchemaGenerator\CodeGenerator\ObjectBuilderInterface;
use GraphQL\SchemaGenerator\CodeGenerator\ObjectClassBuilder;
use GraphQL\SchemaGenerator\CodeGenerator\QueryObjectClassBuilder;
use GraphQL\SchemaGenerator\CodeGenerator\UnionObjectBuilder;
use GraphQL\SchemaObject\MutationObject;
use GraphQL\SchemaObject\QueryObject;
use GraphQL\Util\StringLiteralFormatter;
use RuntimeException;
Expand All @@ -32,12 +35,12 @@ class SchemaClassGenerator
/**
* @var string
*/
private $writeDir;
private $writeDir;

/**
* @var string
*/
private $generationNamespace;
private $generationNamespace;

/**
* This array is used as a set to store the already generated objects
Expand All @@ -47,40 +50,75 @@ class SchemaClassGenerator
*/
private $generatedObjects;

private $objectBuilderClassName = QueryObjectClassBuilder::class;

/**
* SchemaClassGenerator constructor.
*
* @param Client $client
* @param string $writeDir
* @param string $namespace
*/
public function __construct(Client $client, string $writeDir = '', string $namespace = ObjectBuilderInterface::DEFAULT_NAMESPACE)
public function __construct(Client $client, string $writeDir = '', string $namespace = ObjectBuilderInterface::DEFAULT_NAMESPACE)
{
$this->schemaInspector = new SchemaInspector($client);
$this->generatedObjects = [];
$this->writeDir = $writeDir;
$this->schemaInspector = new SchemaInspector($client);
$this->generatedObjects = [];
$this->writeDir = $writeDir;
$this->generationNamespace = $namespace;
$this->setWriteDir();
}

public function generateRootObjects(): bool
{
$this->generateRootQueryObject();
$this->generateRootMutationObject();

return true;
}

/**
* @return bool
*/
public function generateRootQueryObject(): bool
{
$objectArray = $this->schemaInspector->getQueryTypeSchema();
public function generateRootQueryObject(): bool
{
$this->objectBuilderClassName = QueryObjectClassBuilder::class;
$objectArray = $this->schemaInspector->getRootSchema('query');
$rootObjectName = QueryObject::ROOT_QUERY_OBJECT_NAME;
$queryTypeName = $objectArray['name'];
//$rootObjectDescr = $objectArray['description'];
$queryTypeName = $objectArray['name'];

if (array_key_exists($queryTypeName, $this->generatedObjects)) {
return true;
}

$this->generatedObjects[$queryTypeName] = true;

$className = $this->objectBuilderClassName;
$queryObjectBuilder = new $className($this->writeDir, $rootObjectName, $this->generationNamespace);
$this->appendObjectFields($queryObjectBuilder, $rootObjectName, $objectArray['fields']);
$queryObjectBuilder->build();

return true;
}

/**
* @return bool
*/
public function generateRootMutationObject(): bool
{
$this->objectBuilderClassName = MutationObjectClassBuilder::class;
$objectArray = $this->schemaInspector->getRootSchema('mutation');
$rootObjectName = MutationObject::ROOT_MUTATION_OBJECT_NAME;
$queryTypeName = $objectArray['name'];

if (array_key_exists($queryTypeName, $this->generatedObjects)) {
return true;
}

$this->generatedObjects[$queryTypeName] = true;

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

return true;
Expand All @@ -89,18 +127,17 @@ public function generateRootQueryObject(): bool
/**
* This method receives the array of object fields as an input and adds the fields to the query object building
*
* @param QueryObjectClassBuilder $queryObjectBuilder
* @param string $currentTypeName
* @param array $fieldsArray
* @param ObjectClassBuilder $queryObjectBuilder
* @param string $currentTypeName
* @param array $fieldsArray
*/
private function appendQueryObjectFields(QueryObjectClassBuilder $queryObjectBuilder, string $currentTypeName, array $fieldsArray)
private function appendObjectFields(ObjectClassBuilder $queryObjectBuilder, string $currentTypeName, array $fieldsArray)
{
foreach ($fieldsArray as $fieldArray) {
$name = $fieldArray['name'];
// Skip fields with name "query"
if ($name === 'query') continue;

//$description = $fieldArray['description'];
[$typeName, $typeKind] = $this->getTypeInfo($fieldArray);

if ($typeKind === FieldTypeKindEnum::SCALAR) {
Expand Down Expand Up @@ -168,19 +205,20 @@ protected function generateQueryObject(string $objectName): bool
}

$this->generatedObjects[$objectName] = true;
$objectArray = $this->schemaInspector->getObjectSchema($objectName);
$objectName = $objectArray['name'];
$objectArray = $this->schemaInspector->getObjectSchema($objectName);
$objectName = $objectArray['name'];

if ($objectArray['kind'] === FieldTypeKindEnum::INTERFACE_OBJECT) {
$objectBuilder = new InterfaceObjectBuilder($this->writeDir, $objectName, $this->generationNamespace);
foreach ($objectArray['possibleTypes'] as $possibleType) {
$objectBuilder->addImplementation($possibleType['name']);
}
} else {
$objectBuilder = new QueryObjectClassBuilder($this->writeDir, $objectName, $this->generationNamespace);
$className = $this->objectBuilderClassName;
$objectBuilder = new $className($this->writeDir, $objectName, $this->generationNamespace);
}

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

return true;
Expand All @@ -198,8 +236,8 @@ protected function generateInputObject(string $objectName): bool
}

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

foreach ($objectArray['inputFields'] as $inputFieldArray) {
Expand Down Expand Up @@ -244,12 +282,12 @@ protected function generateEnumObject(string $objectName): bool

$this->generatedObjects[$objectName] = true;

$objectArray = $this->schemaInspector->getEnumObjectSchema($objectName);
$objectName = $objectArray['name'];
$objectArray = $this->schemaInspector->getEnumObjectSchema($objectName);
$objectName = $objectArray['name'];
$objectBuilder = new EnumObjectBuilder($this->writeDir, $objectName, $this->generationNamespace);

foreach ($objectArray['enumValues'] as $enumValue) {
$name = $enumValue['name'];
$name = $enumValue['name'];
//$description = $enumValue['description'];
$objectBuilder->addEnumValue($name);
}
Expand All @@ -271,8 +309,8 @@ protected function generateUnionObject(string $objectName): bool

$this->generatedObjects[$objectName] = true;

$objectArray = $this->schemaInspector->getUnionObjectSchema($objectName);
$objectName = $objectArray['name'];
$objectArray = $this->schemaInspector->getUnionObjectSchema($objectName);
$objectName = $objectArray['name'];
$objectBuilder = new UnionObjectBuilder($this->writeDir, $objectName, $this->generationNamespace);

foreach ($objectArray['possibleTypes'] as $possibleType) {
Expand All @@ -297,8 +335,8 @@ protected function generateInterfaceObject(string $objectName): bool

$this->generatedObjects[$objectName] = true;

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

foreach ($objectArray['possibleTypes'] as $possibleType) {
Expand All @@ -312,7 +350,7 @@ protected function generateInterfaceObject(string $objectName): bool

/**
* @param string $argsObjectName
* @param array $arguments
* @param array $arguments
*
* @return bool
*/
Expand Down Expand Up @@ -382,7 +420,7 @@ protected function getTypeInfo(array $dataArray): array
/**
* Sets the write directory if it's not set for the class
*/
private function setWriteDir(): void
private function setWriteDir(): void
{
if ($this->writeDir !== '') return;

Expand Down
7 changes: 4 additions & 3 deletions src/SchemaGenerator/SchemaInspector.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,14 @@ public function __construct(Client $client)
}

/**
* @param string $type query or mutation
* @return array
*/
public function getQueryTypeSchema(): array
public function getRootSchema(string $type): array
{
$schemaQuery = "{
__schema{
queryType{
${type}Type{
name
kind
description
Expand All @@ -82,7 +83,7 @@ public function getQueryTypeSchema(): array
}";
$response = $this->client->runRawQuery($schemaQuery, true);

return $response->getData()['__schema']['queryType'];
return $response->getData()['__schema'][$type.'Type'];
}

/**
Expand Down
Loading

0 comments on commit 5f16e12

Please sign in to comment.