-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* First pass at bulk loading * Refactor to remove class loader, add filepathLoader * Integration tests * More tests * Ready for review * New default loader * Allow module path resolution * Add module resolution to exclude * Update src/Schema/BulkLoader/ExtensionLoader.php Co-authored-by: Steve Boyd <emteknetnz@gmail.com> * Update src/Schema/Interfaces/Identifiable.php Co-authored-by: Steve Boyd <emteknetnz@gmail.com> * Revisions per review, fix elemental bug Co-authored-by: Steve Boyd <emteknetnz@gmail.com>
- Loading branch information
1 parent
e189879
commit 191ac40
Showing
30 changed files
with
1,401 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
<?php | ||
|
||
namespace SilverStripe\GraphQL\Schema\BulkLoader; | ||
|
||
use SilverStripe\Core\Injector\Injectable; | ||
use SilverStripe\GraphQL\Schema\Exception\SchemaBuilderException; | ||
use SilverStripe\GraphQL\Schema\Interfaces\ConfigurationApplier; | ||
use SilverStripe\GraphQL\Schema\Interfaces\Identifiable; | ||
use SilverStripe\GraphQL\Schema\Schema; | ||
|
||
/** | ||
* Provides base functionality to all bulk loaders. Should override the collect() | ||
* method with computations that parse the include/exclude directives and return | ||
* a collection of classes. | ||
*/ | ||
abstract class AbstractBulkLoader implements Identifiable, ConfigurationApplier | ||
{ | ||
use Injectable; | ||
|
||
/** | ||
* @var string[] | ||
*/ | ||
protected $includeList; | ||
|
||
/** | ||
* @var string[] | ||
*/ | ||
protected $excludeList; | ||
|
||
/** | ||
* AbstractBulkLoader constructor. | ||
* @param array $include | ||
* @param array $exclude | ||
*/ | ||
public function __construct(array $include = [], array $exclude = []) | ||
{ | ||
$this->includeList = $include; | ||
$this->excludeList = $exclude; | ||
} | ||
|
||
/** | ||
* @param array $include | ||
* @return $this | ||
*/ | ||
public function include(array $include): self | ||
{ | ||
$this->includeList = $include; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @param array $exclude | ||
* @return $this | ||
*/ | ||
public function exclude(array $exclude): self | ||
{ | ||
$this->excludeList = $exclude; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @param array $config | ||
* @return AbstractBulkLoader | ||
* @throws SchemaBuilderException | ||
*/ | ||
public function applyConfig(array $config): self | ||
{ | ||
Schema::assertValidConfig($config, ['include', 'exclude'], ['include']); | ||
$include = $config['include']; | ||
$exclude = $config['exclude'] ?? []; | ||
if (!is_array($include)) { | ||
$include = [$include]; | ||
} | ||
if (!is_array($exclude)) { | ||
$exclude = [$exclude]; | ||
} | ||
return $this | ||
->include($include) | ||
->exclude($exclude); | ||
} | ||
|
||
/** | ||
* @param Collection $collection | ||
* @return Collection | ||
*/ | ||
public function collect(Collection $collection): Collection | ||
{ | ||
return Collection::create($collection->getManifest()); | ||
} | ||
|
||
/** | ||
* @return string | ||
*/ | ||
abstract public static function getIdentifier(): string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
<?php | ||
|
||
namespace SilverStripe\GraphQL\Schema\BulkLoader; | ||
|
||
use InvalidArgumentException; | ||
use SilverStripe\Core\ClassInfo; | ||
use ReflectionException; | ||
use SilverStripe\Core\Injector\Injectable; | ||
use SilverStripe\GraphQL\Schema\Exception\SchemaBuilderException; | ||
use SilverStripe\GraphQL\Schema\Interfaces\ConfigurationApplier; | ||
use SilverStripe\GraphQL\Schema\Logger; | ||
use SilverStripe\GraphQL\Schema\Schema; | ||
|
||
/** | ||
* Composed with a list of bulk loaders to be executed in serial and return the aggregate result | ||
* of all their collect() calls | ||
*/ | ||
class BulkLoaderSet implements ConfigurationApplier | ||
{ | ||
use Injectable; | ||
|
||
/** | ||
* @var AbstractBulkLoader[] | ||
*/ | ||
private $loaders; | ||
|
||
/** | ||
* @var Collection | ||
*/ | ||
private $initialCollection; | ||
|
||
/** | ||
* BulkLoaderSet constructor. | ||
* @param AbstractBulkLoader[] $loaders | ||
* @param Collection|null $initialCollection | ||
* @throws ReflectionException | ||
*/ | ||
public function __construct(array $loaders = [], ?Collection $initialCollection = null) | ||
{ | ||
$this->setLoaders($loaders); | ||
if ($initialCollection) { | ||
$this->initialCollection = $initialCollection; | ||
} else { | ||
$this->initialCollection = Collection::createFromClassList(ClassInfo::allClasses()); | ||
} | ||
} | ||
|
||
/** | ||
* @param array $config | ||
* @return $this | ||
* @throws SchemaBuilderException | ||
*/ | ||
public function applyConfig(array $config): self | ||
{ | ||
$registry = Registry::inst(); | ||
foreach ($config as $loaderID => $loaderConfig) { | ||
/* @var AbstractBulkLoader $loader */ | ||
$loader = $registry->getByID($loaderID); | ||
Schema::invariant($loader, 'Loader "%s" does not exist', $loaderID); | ||
$loader->applyConfig($loaderConfig); | ||
$this->addLoader($loader); | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @param AbstractBulkLoader $loader | ||
* @return $this | ||
*/ | ||
public function addLoader(AbstractBulkLoader $loader): self | ||
{ | ||
$this->loaders[] = $loader; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @return Collection | ||
*/ | ||
public function process(): Collection | ||
{ | ||
$logger = Logger::singleton(); | ||
$collection = $this->initialCollection; | ||
$logger->debug(sprintf( | ||
'Bulk loader initial collection size: %s', | ||
count($collection->getClasses()) | ||
)); | ||
foreach ($this->loaders as $loader) { | ||
$collection = $loader->collect($collection); | ||
$logger->debug(sprintf( | ||
'Loader %s reduced bulk load to %s', | ||
$loader->getIdentifier(), | ||
count($collection->getClasses()) | ||
)); | ||
} | ||
|
||
return $collection; | ||
} | ||
|
||
/** | ||
* @param array $loaders | ||
* @return $this | ||
*/ | ||
public function setLoaders(array $loaders): self | ||
{ | ||
foreach ($loaders as $loader) { | ||
if (!$loader instanceof AbstractBulkLoader) { | ||
throw new InvalidArgumentException(sprintf( | ||
'%s only accepts instances of %s', | ||
static::class, | ||
AbstractBulkLoader::class | ||
)); | ||
} | ||
} | ||
$this->loaders = $loaders; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @return AbstractBulkLoader[] | ||
*/ | ||
public function getLoaders(): array | ||
{ | ||
return $this->loaders; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
<?php | ||
|
||
namespace SilverStripe\GraphQL\Schema\BulkLoader; | ||
|
||
use SilverStripe\Core\Injector\Injectable; | ||
use Exception; | ||
use ReflectionClass; | ||
use ReflectionException; | ||
|
||
/** | ||
* Defines a collection of class names paired with file paths | ||
*/ | ||
class Collection | ||
{ | ||
use Injectable; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private $manifest; | ||
|
||
/** | ||
* Collection constructor. | ||
* @param array $manifest An array of classname keys to filepath values ['My\Class' => '/path/to/Class.php'] | ||
* @throws Exception | ||
*/ | ||
public function __construct(array $manifest = []) | ||
{ | ||
$this->setManifest($manifest); | ||
} | ||
|
||
/** | ||
* @param array $manifest | ||
* @return $this | ||
* @throws Exception | ||
*/ | ||
public function setManifest(array $manifest): self | ||
{ | ||
$this->manifest = $manifest; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @param string $class | ||
* @return $this | ||
*/ | ||
public function removeClass(string $class): self | ||
{ | ||
unset($this->manifest[$class]); | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @param string $path | ||
* @return $this | ||
*/ | ||
public function removeFile(string $path): self | ||
{ | ||
$class = array_search($path, $this->manifest); | ||
unset($this->manifest[$class]); | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public function getClasses(): array | ||
{ | ||
return array_keys($this->manifest); | ||
} | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public function getFiles(): array | ||
{ | ||
return array_values($this->manifest); | ||
} | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public function getManifest(): array | ||
{ | ||
return $this->manifest; | ||
} | ||
|
||
/** | ||
* @param array $classList | ||
* @return Collection | ||
* @throws ReflectionException | ||
* @throws Exception | ||
*/ | ||
public static function createFromClassList(array $classList): Collection | ||
{ | ||
$manifest = []; | ||
foreach ($classList as $class) { | ||
if (!class_exists($class)) { | ||
continue; | ||
} | ||
$reflection = new ReflectionClass($class); | ||
$filePath = $reflection->getFileName(); | ||
if (!$filePath) { | ||
continue; | ||
} | ||
|
||
$manifest[$class] = $filePath; | ||
} | ||
|
||
return new static($manifest); | ||
} | ||
} |
Oops, something went wrong.