Skip to content

Commit

Permalink
NEW: Field formatting API, forward compatibility (#313)
Browse files Browse the repository at this point in the history
* NEW: field formatting API

* Finalising new case sensitivity

* Add tests for field formatting

* Mark field accessor, formatter internal

* Linting

* Remove test code

* Change notice to warning

* Use phpunit notice

* Add PHP8 condition to unit test

* Generic error check
  • Loading branch information
Aaron Carlino authored Nov 13, 2020
1 parent 263c924 commit 25f654c
Show file tree
Hide file tree
Showing 15 changed files with 437 additions and 35 deletions.
35 changes: 35 additions & 0 deletions src/FieldAccessorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php


namespace SilverStripe\GraphQL;

use SilverStripe\View\ViewableData;

interface FieldAccessorInterface
{
/**
* @param ViewableData $object
* @param $fieldName
* @param array $opts
* @param bool $asObject
* @return mixed
*/
public function getValue(ViewableData $object, $fieldName, $opts = [], $asObject = false);

/**
* @param ViewableData $object
* @param $fieldName
* @param $value
* @param array $opts
* @return mixed
*/
public function setValue(ViewableData $object, $fieldName, $value, $opts = []);

/**
* @param ViewableData $object
* @param $fieldName
* @param array $opts
* @return string|null
*/
public function getObjectFieldName(ViewableData $object, $fieldName, $opts = []);
}
13 changes: 11 additions & 2 deletions src/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use SilverStripe\GraphQL\PersistedQuery\PersistedQueryMappingProvider;
use SilverStripe\GraphQL\Scaffolding\StaticSchema;
use SilverStripe\GraphQL\Middleware\QueryMiddleware;
use SilverStripe\GraphQL\Util\NaiveFieldAccessor;
use SilverStripe\ORM\ValidationException;
use SilverStripe\Security\Member;
use SilverStripe\GraphQL\Scaffolding\Interfaces\ScaffoldingProvider;
Expand Down Expand Up @@ -204,12 +205,20 @@ public function applyConfig(array $config)
$this->extend('updateConfig', $config);

// Bootstrap schema class mapping from config
if ($config && array_key_exists('typeNames', $config)) {
if (array_key_exists('typeNames', $config)) {
StaticSchema::inst()->setTypeNames($config['typeNames']);
}
if (array_key_exists('fieldFormatter', $config)) {
StaticSchema::inst()->setFieldFormatter($config['fieldFormatter']);
}
if (array_key_exists('fieldAccessor', $config)) {
StaticSchema::inst()->setFieldAccessor(Injector::inst()->get($config['fieldAccessor']));
} else {
StaticSchema::inst()->setFieldAccessor(Injector::inst()->get(NaiveFieldAccessor::class));
}

// Types (incl. Interfaces and InputTypes)
if ($config && array_key_exists('types', $config)) {
if (array_key_exists('types', $config)) {
foreach ($config['types'] as $name => $typeCreatorClass) {
$typeCreator = Injector::inst()->create($typeCreatorClass, $this);
if (!($typeCreator instanceof TypeCreator)) {
Expand Down
7 changes: 6 additions & 1 deletion src/Pagination/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,10 @@ public function fields()
'edges' => [
'type' => Type::listOf($this->getEdgeType()),
'description' => 'Collection of records'
],
'nodes' => [
'type' => Type::listOf($this->getConnectionType()),
'description' => 'The node at the end of the collections edge',
]
];
}
Expand Down Expand Up @@ -410,11 +414,12 @@ public function resolveList($list, array $args, $context = null, ResolveInfo $in

return [
'edges' => $list,
'nodes' => $list,
'pageInfo' => [
'totalCount' => $count,
'hasNextPage' => $nextPage,
'hasPreviousPage' => $previousPage
]
],
];
}

Expand Down
14 changes: 12 additions & 2 deletions src/Scaffolding/Scaffolders/CRUD/Create.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use SilverStripe\GraphQL\Scaffolding\Extensions\TypeCreatorExtension;
use SilverStripe\GraphQL\Scaffolding\Interfaces\CRUDInterface;
use SilverStripe\GraphQL\Scaffolding\Scaffolders\MutationScaffolder;
use SilverStripe\GraphQL\Scaffolding\StaticSchema;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectSchema;
use SilverStripe\ORM\FieldType\DBField;
Expand Down Expand Up @@ -59,8 +60,9 @@ public function addToManager(Manager $manager)
*/
protected function createDefaultArgs(Manager $manager)
{
$argName = $this->argName();
return [
'Input' => [
$argName => [
'type' => Type::nonNull($manager->getType($this->inputTypeName())),
]
];
Expand Down Expand Up @@ -120,7 +122,7 @@ public function resolve($object, array $args, $context, ResolveInfo $info)

/** @var DataObject $newObject */
$newObject = Injector::inst()->create($this->getDataObjectClass());
$newObject->update($args['Input']);
$newObject->update($args[$this->argName()]);

// Extension points that return false should kill the create
$results = $this->extend('augmentMutation', $newObject, $args, $context, $info);
Expand All @@ -136,4 +138,12 @@ public function resolve($object, array $args, $context, ResolveInfo $info)

return $newObject;
}

/**
* @return string
*/
private function argName()
{
return StaticSchema::inst()->formatField('Input');
}
}
14 changes: 11 additions & 3 deletions src/Scaffolding/Scaffolders/CRUD/Delete.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use SilverStripe\GraphQL\OperationResolver;
use SilverStripe\GraphQL\Scaffolding\Interfaces\CRUDInterface;
use SilverStripe\GraphQL\Scaffolding\Scaffolders\MutationScaffolder;
use SilverStripe\GraphQL\Scaffolding\StaticSchema;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DB;
Expand Down Expand Up @@ -48,8 +49,9 @@ public function getName()
*/
protected function createDefaultArgs(Manager $manager)
{
$argName = $this->argName();
return [
'IDs' => [
$argName => [
'type' => Type::nonNull($this->generateInputType()),
],
];
Expand All @@ -65,10 +67,11 @@ protected function generateInputType()

public function resolve($object, array $args, $context, ResolveInfo $info)
{
DB::get_conn()->withTransaction(function () use ($args, $context, $info) {
$argName = $this->argName();
DB::get_conn()->withTransaction(function () use ($args, $context, $info, $argName) {
// Build list to filter
$results = DataList::create($this->getDataObjectClass())
->byIDs($args['IDs']);
->byIDs($args[$argName]);
$extensionResults = $this->extend('augmentMutation', $results, $args, $context, $info);

// Extension points that return false should kill the deletion
Expand Down Expand Up @@ -97,4 +100,9 @@ public function resolve($object, array $args, $context, ResolveInfo $info)
$this->extend('afterMutation', $resultsList, $args, $context, $info);
});
}

private function argName()
{
return StaticSchema::inst()->formatField('IDs');
}
}
12 changes: 8 additions & 4 deletions src/Scaffolding/Scaffolders/CRUD/Read.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use SilverStripe\GraphQL\Scaffolding\Interfaces\CRUDInterface;
use SilverStripe\GraphQL\Scaffolding\Scaffolders\ListQueryScaffolder;
use SilverStripe\GraphQL\Scaffolding\Scaffolders\SchemaScaffolder;
use SilverStripe\GraphQL\Scaffolding\StaticSchema;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\Security\Member;
Expand All @@ -37,8 +38,8 @@ public function __construct($dataObjectClass)
{
parent::__construct(null, null, $this, $dataObjectClass);
$filter = Injector::inst()->create(DataObjectQueryFilter::class, $dataObjectClass)
->setFilterKey(self::FILTER)
->setExcludeKey(self::EXCLUDE);
->setFilterKey(StaticSchema::inst()->formatField(self::FILTER))
->setExcludeKey(StaticSchema::inst()->formatField(self::EXCLUDE));
$this->setQueryFilter($filter);
}

Expand Down Expand Up @@ -131,11 +132,14 @@ protected function createDefaultArgs(Manager $manager)
if (!$this->queryFilter->exists()) {
return [];
}
$filterKey = StaticSchema::inst()->formatField(self::FILTER);
$excludeKey = StaticSchema::inst()->formatField(self::EXCLUDE);

return [
self::FILTER => [
$filterKey => [
'type' => $manager->getType($this->inputTypeName(self::FILTER)),
],
self::EXCLUDE => [
$excludeKey => [
'type' => $manager->getType($this->inputTypeName(self::EXCLUDE)),
],
];
Expand Down
20 changes: 12 additions & 8 deletions src/Scaffolding/Scaffolders/CRUD/ReadOne.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use SilverStripe\GraphQL\Scaffolding\Interfaces\CRUDInterface;
use SilverStripe\GraphQL\Scaffolding\Scaffolders\ItemQueryScaffolder;
use SilverStripe\GraphQL\Scaffolding\Scaffolders\SchemaScaffolder;
use SilverStripe\GraphQL\Scaffolding\StaticSchema;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObjectInterface;
use InvalidArgumentException;
Expand All @@ -33,8 +34,8 @@ public function __construct($dataObjectClass)
{
parent::__construct(null, null, $this, $dataObjectClass);
$filter = Injector::inst()->create(DataObjectQueryFilter::class, $dataObjectClass)
->setFilterKey(Read::FILTER)
->setExcludeKey(Read::EXCLUDE);
->setFilterKey(StaticSchema::inst()->formatField(Read::FILTER))
->setExcludeKey(StaticSchema::inst()->formatField(Read::EXCLUDE));
$this->setQueryFilter($filter);
}

Expand All @@ -54,17 +55,19 @@ public function getName()
*/
protected function createDefaultArgs(Manager $manager)
{
$id = StaticSchema::inst()->formatField('ID');
$args = [
'ID' => [
$id => [
'type' => Type::id()
],
];

$filterKey = StaticSchema::inst()->formatField(Read::FILTER);
$excludeKey = StaticSchema::inst()->formatField(Read::EXCLUDE);
if ($this->queryFilter->exists()) {
$args[Read::FILTER] = [
$args[$filterKey] = [
'type' => $this->queryFilter->getInputType($this->inputTypeName(Read::FILTER)),
];
$args[Read::EXCLUDE] = [
$args[$excludeKey] = [
'type' => $this->queryFilter->getInputType($this->inputTypeName(Read::EXCLUDE)),
];
}
Expand All @@ -91,10 +94,11 @@ protected function inputTypeName($key = '')
*/
public function resolve($object, array $args, $context, ResolveInfo $info)
{
$id = StaticSchema::inst()->formatField('ID');
// get as a list so extensions can influence it pre-query
$list = DataList::create($this->getDataObjectClass());
if (isset($args['ID'])) {
$list = $list->filter('ID', $args['ID']);
if (isset($args[$id])) {
$list = $list->filter('ID', $args[$id]);
}
if ($this->queryFilter->exists()) {
$list = $this->queryFilter->applyArgsToList($list, $args);
Expand Down
15 changes: 13 additions & 2 deletions src/Scaffolding/Scaffolders/CRUD/Update.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use SilverStripe\GraphQL\Scaffolding\Extensions\TypeCreatorExtension;
use SilverStripe\GraphQL\Scaffolding\Interfaces\CRUDInterface;
use SilverStripe\GraphQL\Scaffolding\Scaffolders\MutationScaffolder;
use SilverStripe\GraphQL\Scaffolding\StaticSchema;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\ORM\DataObjectSchema;
Expand Down Expand Up @@ -62,8 +63,9 @@ public function addToManager(Manager $manager)
*/
protected function createDefaultArgs(Manager $manager)
{
$input = $this->argName();
return [
'Input' => [
$input => [
'type' => Type::nonNull($manager->getType($this->inputTypeName())),
],
];
Expand Down Expand Up @@ -127,7 +129,8 @@ protected function inputTypeName()
*/
public function resolve($object, array $args, $context, ResolveInfo $info)
{
$input = $args['Input'];
$input = $this->argName();
$input = $args[$input];
$obj = DataList::create($this->getDataObjectClass())
->byID($input['ID']);
if (!$obj) {
Expand Down Expand Up @@ -158,4 +161,12 @@ public function resolve($object, array $args, $context, ResolveInfo $info)

return $obj;
}

/**
* @return string
*/
private function argName()
{
return StaticSchema::inst()->formatField('Input');
}
}
13 changes: 6 additions & 7 deletions src/Scaffolding/Scaffolders/DataObjectScaffolder.php
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ public function nestedQuery($fieldName, QueryScaffolder $queryScaffolder = null)

if (!$queryScaffolder) {
// If no scaffolder if provided, try to infer the type by resolving the field
$result = $this->getDataObjectInstance()->obj($fieldName);
$result = StaticSchema::inst()->accessField($this->getDataObjectInstance(), $fieldName);

if (!$result instanceof DataList && !$result instanceof ArrayList) {
throw new InvalidArgumentException(
Expand All @@ -352,14 +352,13 @@ public function nestedQuery($fieldName, QueryScaffolder $queryScaffolder = null)
)
);
}

$queryScaffolder = Injector::inst()->create(
ListQueryScaffolder::class,
$fieldName,
null,
function ($obj) use ($fieldName) {
/* @var DataObject $obj */
return $obj->obj($fieldName);
return StaticSchema::inst()->accessField($obj, $fieldName);
},
$result->dataClass()
);
Expand Down Expand Up @@ -669,7 +668,7 @@ protected function createFields(Manager $manager)
/**
* @var DataObject $obj
*/
$field = $obj->obj($info->fieldName);
$field = StaticSchema::inst()->accessField($obj, $info->fieldName);
// return the raw field value, or checks like `is_numeric()` fail
if ($field instanceof DBField && $field->isInternalGraphQLType()) {
return $field->getValue();
Expand All @@ -689,7 +688,7 @@ protected function createFields(Manager $manager)
);
}

$result = $instance->obj($fieldName);
$result = StaticSchema::inst()->accessField($instance, $fieldName);

if ($result instanceof SS_List) {
throw new InvalidArgumentException(
Expand Down Expand Up @@ -722,10 +721,10 @@ protected function createFields(Manager $manager)

foreach ($this->nestedQueries as $name => $scaffolder) {
$scaffold = $scaffolder->scaffold($manager);
$scaffold['name'] = $name;
$scaffold['name'] = StaticSchema::inst()->formatField($name);
$fieldMap[$name] = $scaffold;
}

return $fieldMap;
return StaticSchema::inst()->formatKeys($fieldMap);
}
}
Loading

0 comments on commit 25f654c

Please sign in to comment.