Skip to content

Commit

Permalink
NEW: DBField arguments, Enum, Composite support (#398)
Browse files Browse the repository at this point in the history
* Initial commit

* Refactor of args work

* Ready for tests

* Scalar dbfield

* Begin integration test

* Tests done

* Allow ignore enum, prevent duplication

* Nullable fixes, duplication protection

* New tests for dedupe, custom name, etc

* Fix race condition in interface builder adding to queries before interfaces are ready

* Update src/Schema/Plugin/PaginationPlugin.php

Co-authored-by: Steve Boyd <emteknetnz@gmail.com>

* BUGFIX: Don't apply metadata when explicit type is specified

* Revisions per emteknetnz

* Fix tests

Co-authored-by: Steve Boyd <emteknetnz@gmail.com>
  • Loading branch information
Aaron Carlino and emteknetnz authored Sep 6, 2021
1 parent 285de89 commit d2a0318
Show file tree
Hide file tree
Showing 37 changed files with 1,543 additions and 64 deletions.
14 changes: 0 additions & 14 deletions _config/assets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,6 @@ Name: graphqlassets
Only:
moduleexists: 'silverstripe/assets'
---
SilverStripe\GraphQL\Schema\Schema:
schemas:
'*':
types:
DBFile:
fields:
filename: String
hash: String
variant: String
url: String

SilverStripe\Assets\Storage\DBFile:
graphql_type: DBFile

SilverStripe\Assets\File:
allowed_extensions:
- graphql
Expand Down
18 changes: 18 additions & 0 deletions _config/dbargs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
Name: graphql-db-args
---
# For the DBFieldArgsPlugin, assign each DBField type an args factory
SilverStripe\ORM\FieldType\DBText:
graphql_args: SilverStripe\GraphQL\Schema\DataObject\Plugin\DBFieldArgs\DBTextArgs
SilverStripe\ORM\FieldType\DBHTMLText:
graphql_args: SilverStripe\GraphQL\Schema\DataObject\Plugin\DBFieldArgs\DBHTMLTextArgs
SilverStripe\ORM\FieldType\DBDate:
graphql_args: SilverStripe\GraphQL\Schema\DataObject\Plugin\DBFieldArgs\DBDateArgs
SilverStripe\ORM\FieldType\DBDateTime:
graphql_args: SilverStripe\GraphQL\Schema\DataObject\Plugin\DBFieldArgs\DBDatetimeArgs
SilverStripe\ORM\FieldType\DBTime:
graphql_args: SilverStripe\GraphQL\Schema\DataObject\Plugin\DBFieldArgs\DBTimeArgs
SilverStripe\ORM\FieldType\DBDecimal:
graphql_args: SilverStripe\GraphQL\Schema\DataObject\Plugin\DBFieldArgs\DBDecimalArgs
SilverStripe\ORM\FieldType\DBFloat:
graphql_args: SilverStripe\GraphQL\Schema\DataObject\Plugin\DBFieldArgs\DBFloatArgs
3 changes: 3 additions & 0 deletions _config/plugins.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ SilverStripe\Core\Injector\Injector:
- 'SilverStripe\GraphQL\Schema\DataObject\Plugin\CanViewPermission'
- 'SilverStripe\GraphQL\Schema\DataObject\Plugin\FirstResult'
- 'SilverStripe\GraphQL\Schema\DataObject\Plugin\InheritedPlugins'
- 'SilverStripe\GraphQL\Schema\DataObject\Plugin\DBFieldArgs\DBFieldArgsPlugin'
- 'SilverStripe\GraphQL\Schema\DataObject\Plugin\DBFieldTypes'
- 'SilverStripe\GraphQL\Schema\Plugin\SortPlugin'
- 'SilverStripe\GraphQL\Schema\DataObject\Plugin\ScalarDBField'
1 change: 1 addition & 0 deletions _config/schema-default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ SilverStripe\GraphQL\Schema\Schema:
config:
modelConfig:
DataObject:
parseShortcodes: true
operations:
read:
plugins:
Expand Down
9 changes: 7 additions & 2 deletions _config/schema-global.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,16 @@ SilverStripe\GraphQL\Schema\Schema:
base_fields:
ID: ID!
plugins:
dbFieldArgs: true
dbFieldTypes:
ignore:
className: true
before: scalarDBField
inheritance:
useUnionQueries: false
hideAncestors:
- SilverStripe\CMS\Model\SiteTree
after: 'versioning'
scalarDBField:
after: dbFieldArgs
inheritedPlugins:
after: '*'
operations:
Expand Down
27 changes: 25 additions & 2 deletions src/Schema/DataObject/DataObjectModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use SilverStripe\GraphQL\Schema\Field\ModelQuery;
use SilverStripe\GraphQL\Schema\Interfaces\BaseFieldsProvider;
use SilverStripe\GraphQL\Schema\Interfaces\DefaultFieldsProvider;
use SilverStripe\GraphQL\Schema\Interfaces\ExtraTypeProvider;
use SilverStripe\GraphQL\Schema\Interfaces\ModelBlacklist;
use SilverStripe\GraphQL\Schema\Resolver\ResolverReference;
use SilverStripe\GraphQL\Schema\SchemaConfig;
Expand Down Expand Up @@ -114,18 +115,28 @@ public function getField(string $fieldName, array $config = []): ?ModelField
return null;
}

$hasExplicitType = isset($config['type']);

if ($result instanceof DBField) {
$fieldConfig = array_merge([
'type' => $result->config()->get('graphql_type'),
], $config);

return ModelField::create($fieldName, $fieldConfig, $this);
$modelField = ModelField::create($fieldName, $fieldConfig, $this);
if (!$hasExplicitType) {
$this->applyMetadataClass($modelField, get_class($result));
}
return $modelField;
}

$class = $this->getModelClass($result);
if (!$class) {
if ($this->isList($result)) {
return ModelField::create($fieldName, $config, $this);
$modelField = ModelField::create($fieldName, $config, $this);
if (!$hasExplicitType) {
$this->applyMetadataClass($modelField, $class);
}
return $modelField;
}
return null;
}
Expand All @@ -136,6 +147,7 @@ public function getField(string $fieldName, array $config = []): ?ModelField
], $config);
$query = ModelQuery::create($this, $fieldName, $queryConfig);
$query->setDefaultPlugins($this->getModelConfiguration()->getNestedQueryPlugins());

return $query;
}
return ModelField::create($fieldName, $type, $this);
Expand Down Expand Up @@ -399,4 +411,15 @@ private function isList($result): bool
{
return $result instanceof SS_List || $result instanceof UnsavedRelationList;
}

/**
* @param ModelField $field
* @param string | null $class
* @throws SchemaBuilderException
*/
private function applyMetadataClass(ModelField $field, ?string $class = null): void
{
$field->getMetadata()
->set('dataClass', $class);
}
}
22 changes: 20 additions & 2 deletions src/Schema/DataObject/InterfaceBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,20 @@ class InterfaceBuilder
*/
private $schema;

/**
* @var array
*/
private $hideAncestors = [];

/**
* InterfaceBuilderTest constructor.
* @param Schema $schema
* @param array $hideAncestors
*/
public function __construct(Schema $schema)
public function __construct(Schema $schema, array $hideAncestors = [])
{
$this->setSchema($schema);
$this->hideAncestors = $hideAncestors;
}

/**
Expand Down Expand Up @@ -86,7 +93,9 @@ public function createInterfaces(ModelType $modelType, array $interfaceStack = [
$interfaceStack[] = $interface;
$modelType->addInterface($interface->getName());

$chain = InheritanceChain::create($modelType->getModel()->getSourceClass());
$chain = InheritanceChain::create($modelType->getModel()->getSourceClass())
->hideAncestors($this->hideAncestors);

foreach ($chain->getDirectDescendants() as $class) {
if ($childType = $this->getSchema()->getModelByClassName($class)) {
$this->createInterfaces($childType, $interfaceStack);
Expand Down Expand Up @@ -130,6 +139,7 @@ public function applyBaseInterface(): InterfaceBuilder

/**
* @param ModelType $type
* @throws ReflectionException
* @throws SchemaBuilderException
* @return $this
*/
Expand Down Expand Up @@ -157,6 +167,14 @@ public function applyInterfacesToQueries(ModelType $type): InterfaceBuilder
$this->schema->eagerLoad($modelType->getName());
}
}
$chain = InheritanceChain::create($type->getModel()->getSourceClass())
->hideAncestors($this->hideAncestors);

foreach ($chain->getDirectDescendants() as $class) {
if ($modelType = $schema->getModelByClassName($class)) {
$this->applyInterfacesToQueries($modelType);
}
}

return $this;
}
Expand Down
92 changes: 92 additions & 0 deletions src/Schema/DataObject/Plugin/DBFieldArgs/DBDateArgs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php


namespace SilverStripe\GraphQL\Schema\DataObject\Plugin\DBFieldArgs;

use SilverStripe\GraphQL\Schema\Field\ModelField;
use SilverStripe\GraphQL\Schema\Type\Enum;
use SilverStripe\ORM\FieldType\DBDate;
use Exception;
use SilverStripe\ORM\FieldType\DBField;

class DBDateArgs extends DBFieldArgs
{
public function getEnum(): Enum
{
return Enum::create(
'DBDateFormattingOptions',
$this->getValues(),
'Formatting options for fields that map to DBDate data types'
);
}

public function applyToField(ModelField $field): void
{
$field
->addArg('format', [
'type' => $this->getEnum()->getName(),
'description' => 'Formatting options for this field',
])
->addArg('customFormat', [
'type' => 'String',
'description' => 'If format is CUSTOM, the format string, e.g. "y-MM-dd HH:mm:ss"',
])
->addResolverAfterware($this->getResolver());
}

/**
* @return callable
*/
protected function getResolver(): callable
{
return [static::class, 'resolve'];
}

/**
* @param mixed $obj
* @param array $args
* @return DBField | string
* @throws Exception
*/
public static function resolve($obj, array $args)
{
if (!$obj instanceof DBDate) {
return $obj;
}
$format = $args['format'] ?? null;
$custom = $args['customFormat'] ?? null;

if ($format === 'Format') {
if (!$custom) {
throw new Exception('The "custom" option requires a value for "customFormat"');
}
return $obj->Format($custom);
}
if ($custom) {
throw new Exception('The "customFormat" argument should not be set for formats that are not "custom"');
}

if ($obj->hasMethod($format)) {
return $obj->obj($format);
}

return $obj;
}

public function getValues(): array
{
return [
'TIMESTAMP' => 'Timestamp',
'NICE' => 'Nice',
'DAY_OF_WEEK' => 'DayOfWeek',
'MONTH' => 'Month',
'YEAR' => 'Year',
'SHORT_MONTH' => 'ShortMonth',
'DAY_OF_MONTH' => 'DayOfMonth',
'SHORT' => 'Short',
'LONG' => 'Long',
'FULL' => 'Full',
'CUSTOM' => 'Format',
];
}
}
31 changes: 31 additions & 0 deletions src/Schema/DataObject/Plugin/DBFieldArgs/DBDatetimeArgs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php


namespace SilverStripe\GraphQL\Schema\DataObject\Plugin\DBFieldArgs;

use SilverStripe\GraphQL\Schema\Type\Enum;

class DBDatetimeArgs extends DBDateArgs
{
public function getEnum(): Enum
{
return Enum::create(
'DBDatetimeFormattingOption',
$this->getValues(),
'Formatting options for fields that map to DBDatetime data types'
);
}

public function getValues(): array
{
return array_merge(
parent::getValues(),
[
'DATE' => 'Date',
'TIME' => 'Time',
'TIME12' => 'Time12',
'TIME24' => 'Time24',
]
);
}
}
45 changes: 45 additions & 0 deletions src/Schema/DataObject/Plugin/DBFieldArgs/DBDecimalArgs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php


namespace SilverStripe\GraphQL\Schema\DataObject\Plugin\DBFieldArgs;

use SilverStripe\GraphQL\Schema\Field\ModelField;
use SilverStripe\GraphQL\Schema\Type\Enum;

class DBDecimalArgs extends DBFieldArgs
{
public function getEnum(): Enum
{
return Enum::create(
'DBDecimalFormattingOptions',
$this->getValues(),
'Formatting options for fields that map to DBDecimal data types'
);
}

public function applyToField(ModelField $field): void
{
$field->addArg('format', [
'type' => $this->getEnum()->getName(),
'description' => 'Formatting options for this field',
])->addResolverAfterware(
$this->getResolver()
);
}

/**
* @return callable
*/
protected function getResolver(): callable
{
return [DBFieldArgs::class, 'baseFormatResolver'];
}


public function getValues(): array
{
return [
'INT' => 'Int',
];
}
}
Loading

0 comments on commit d2a0318

Please sign in to comment.