diff --git a/_config/config.yml b/_config/config.yml index b2be377b..86a1488c 100644 --- a/_config/config.yml +++ b/_config/config.yml @@ -1,7 +1,6 @@ --- Name: graphqlconfig --- -# Define the type parsers SilverStripe\Core\Injector\Injector: SilverStripe\GraphQL\QueryHandler\QueryHandlerInterface: class: SilverStripe\GraphQL\QueryHandler\QueryHandler diff --git a/_config/schema-global.yml b/_config/schema-global.yml index 03fdfec7..2869aba0 100644 --- a/_config/schema-global.yml +++ b/_config/schema-global.yml @@ -14,6 +14,10 @@ SilverStripe\GraphQL\Schema\Schema: defaultResolver: 'SilverStripe\GraphQL\Schema\Resolver\DefaultResolver::defaultFieldResolver' modelCreators: - 'SilverStripe\GraphQL\Schema\DataObject\ModelCreator' + defaultBulkLoad: + inheritanceLoader: + include: + - SilverStripe\ORM\DataObject modelConfig: DataObject: type_formatter: 'SilverStripe\Core\ClassInfo::shortName' diff --git a/src/Schema/BulkLoader/AbstractBulkLoader.php b/src/Schema/BulkLoader/AbstractBulkLoader.php new file mode 100644 index 00000000..34c6b3d9 --- /dev/null +++ b/src/Schema/BulkLoader/AbstractBulkLoader.php @@ -0,0 +1,97 @@ +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; +} diff --git a/src/Schema/BulkLoader/BulkLoaderSet.php b/src/Schema/BulkLoader/BulkLoaderSet.php new file mode 100644 index 00000000..057b0df3 --- /dev/null +++ b/src/Schema/BulkLoader/BulkLoaderSet.php @@ -0,0 +1,128 @@ +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; + } +} diff --git a/src/Schema/BulkLoader/Collection.php b/src/Schema/BulkLoader/Collection.php new file mode 100644 index 00000000..3ee68239 --- /dev/null +++ b/src/Schema/BulkLoader/Collection.php @@ -0,0 +1,115 @@ + '/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); + } +} diff --git a/src/Schema/BulkLoader/ExtensionLoader.php b/src/Schema/BulkLoader/ExtensionLoader.php new file mode 100644 index 00000000..32dcd185 --- /dev/null +++ b/src/Schema/BulkLoader/ExtensionLoader.php @@ -0,0 +1,92 @@ +getClasses() as $class) { + $isIncluded = false; + foreach ($this->includeList as $pattern) { + if (Extensible::has_extension($class, $pattern)) { + $isIncluded = true; + break; + } + } + foreach ($this->excludeList as $pattern) { + if (Extensible::has_extension($class, $pattern)) { + $isIncluded = false; + break; + } + } + + if (!$isIncluded) { + $newCollection->removeClass($class); + } + } + + return $newCollection; + } + + /** + * @param array $include + * @return AbstractBulkLoader + */ + public function include(array $include): AbstractBulkLoader + { + foreach ($include as $class) { + if (!class_exists($class) || !is_subclass_of($class, Extension::class)) { + throw new InvalidArgumentException(sprintf( + 'Class %s given to %s is not a valid extension', + $class, + static::class + )); + } + } + + return parent::include($include); + } + + /** + * @param array $exclude + * @return AbstractBulkLoader + */ + public function exclude(array $exclude): AbstractBulkLoader + { + foreach ($exclude as $class) { + if (!class_exists($class) || !is_subclass_of($class, Extension::class)) { + throw new InvalidArgumentException(sprintf( + 'Class %s given to %s is not a valid extension', + $class, + static::class + )); + } + } + + return parent::exclude($exclude); + } +} diff --git a/src/Schema/BulkLoader/FilepathLoader.php b/src/Schema/BulkLoader/FilepathLoader.php new file mode 100644 index 00000000..99b0fc67 --- /dev/null +++ b/src/Schema/BulkLoader/FilepathLoader.php @@ -0,0 +1,57 @@ +includeList as $include) { + $resolvedDir = ModuleResourceLoader::singleton()->resolvePath($include); + $absGlob = Director::is_absolute($include) ? $include : Path::join(BASE_PATH, $resolvedDir); + + foreach (glob(Path::join($absGlob)) as $path) { + $includedFiles[$path] = true; + } + } + foreach ($this->excludeList as $exclude) { + $resolvedDir = ModuleResourceLoader::singleton()->resolvePath($exclude); + $absGlob = Director::is_absolute($exclude) ? $exclude : Path::join(BASE_PATH, $resolvedDir); + + foreach (glob(Path::join($absGlob)) as $path) { + $excludedFiles[$path] = true; + } + } + + foreach ($collection->getFiles() as $file) { + $isIncluded = isset($includedFiles[$file]) && !isset($excludedFiles[$file]); + if (!$isIncluded) { + $newCollection->removeFile($file); + } + } + + return $newCollection; + } +} diff --git a/src/Schema/BulkLoader/InheritanceLoader.php b/src/Schema/BulkLoader/InheritanceLoader.php new file mode 100644 index 00000000..1cbce91c --- /dev/null +++ b/src/Schema/BulkLoader/InheritanceLoader.php @@ -0,0 +1,89 @@ +getClasses() as $class) { + $isIncluded = false; + foreach ($this->includeList as $pattern) { + if ($class === $pattern || is_subclass_of($class, $pattern)) { + $isIncluded = true; + break; + } + } + foreach ($this->excludeList as $pattern) { + if ($class === $pattern || is_subclass_of($class, $pattern)) { + $isIncluded = false; + break; + } + } + + if (!$isIncluded) { + $newCollection->removeClass($class); + } + } + + return $newCollection; + } + + /** + * @param array $include + * @return AbstractBulkLoader + */ + public function include(array $include): AbstractBulkLoader + { + foreach ($include as $class) { + if (!class_exists($class)) { + throw new InvalidArgumentException(sprintf( + 'Class %s given to %s does not exist', + $class, + static::class + )); + } + } + return parent::include($include); + } + + /** + * @param array $exclude + * @return AbstractBulkLoader + */ + public function exclude(array $exclude): AbstractBulkLoader + { + foreach ($exclude as $class) { + if (!class_exists($class)) { + throw new InvalidArgumentException(sprintf( + 'Class %s given to %s does not exist', + $class, + static::class + )); + } + } + + return parent::exclude($exclude); + } +} diff --git a/src/Schema/BulkLoader/NamespaceLoader.php b/src/Schema/BulkLoader/NamespaceLoader.php new file mode 100644 index 00000000..49fddd22 --- /dev/null +++ b/src/Schema/BulkLoader/NamespaceLoader.php @@ -0,0 +1,50 @@ +getClasses() as $class) { + $isIncluded = false; + foreach ($this->includeList as $pattern) { + if (fnmatch($pattern, $class, FNM_NOESCAPE)) { + $isIncluded = true; + break; + } + } + foreach ($this->excludeList as $pattern) { + if (fnmatch($pattern, $class, FNM_NOESCAPE)) { + $isIncluded = false; + break; + } + } + + if (!$isIncluded) { + $newCollection->removeClass($class); + } + } + + return $newCollection; + } +} diff --git a/src/Schema/BulkLoader/Registry.php b/src/Schema/BulkLoader/Registry.php new file mode 100644 index 00000000..fe7fc81d --- /dev/null +++ b/src/Schema/BulkLoader/Registry.php @@ -0,0 +1,36 @@ +get($className); + }, $subclasses); + + self::$inst = RegistryBackend::create(...$bulkLoaders); + + return self::$inst; + } +} diff --git a/src/Schema/BulkLoader/RegistryBackend.php b/src/Schema/BulkLoader/RegistryBackend.php new file mode 100644 index 00000000..ad3e00d9 --- /dev/null +++ b/src/Schema/BulkLoader/RegistryBackend.php @@ -0,0 +1,59 @@ +setInstances($instances); + } + + /** + * @param Identifiable[] $instances + * @return $this + */ + public function setInstances(array $instances): self + { + foreach ($instances as $instance) { + if (!is_subclass_of($instance, Identifiable::class)) { + throw new InvalidArgumentException(sprintf( + 'Class %s does not implement %s', + get_class($instance), + Identifiable::class + )); + } + + $this->instances[$instance->getIdentifier()] = $instance; + } + + return $this; + } + + /** + * @param string $id + * @return Identifiable|null + */ + public function getByID(string $id): ?Identifiable + { + return $this->instances[$id] ?? null; + } +} diff --git a/src/Schema/DataObject/DataObjectModel.php b/src/Schema/DataObject/DataObjectModel.php index bede304a..92a65661 100644 --- a/src/Schema/DataObject/DataObjectModel.php +++ b/src/Schema/DataObject/DataObjectModel.php @@ -3,6 +3,7 @@ namespace SilverStripe\GraphQL\Schema\DataObject; +use Psr\Container\NotFoundExceptionInterface; use SilverStripe\Core\Config\Configurable; use SilverStripe\Core\Injector\Injectable; use SilverStripe\Core\Injector\Injector; @@ -11,7 +12,6 @@ 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; @@ -80,6 +80,7 @@ public static function getIdentifier(): string * @param string $class * @param SchemaConfig $config * @throws SchemaBuilderException + * @throws NotFoundExceptionInterface */ public function __construct(string $class, SchemaConfig $config) { @@ -140,7 +141,7 @@ public function getField(string $fieldName, array $config = []): ?ModelField } return null; } - $type = $this->getModelConfiguration()->getTypeName($class); + $type = (new static($class, $this->getSchemaConfig()))->getTypeName(); if ($this->isList($result)) { $queryConfig = array_merge([ 'type' => sprintf('[%s!]!', $type), diff --git a/src/Schema/Interfaces/Identifiable.php b/src/Schema/Interfaces/Identifiable.php new file mode 100644 index 00000000..1966ce67 --- /dev/null +++ b/src/Schema/Interfaces/Identifiable.php @@ -0,0 +1,15 @@ +schemaConfig = $schemaConfig ?: SchemaConfig::create(); $this->state = Configuration::create(); + $this->logger = Logger::singleton(); } /** @@ -163,6 +173,7 @@ public function applyConfig(array $schemaConfig): Schema self::MUTATIONS, self::INTERFACES, self::UNIONS, + self::BULK_LOAD, self::MODELS, self::ENUMS, self::SCALARS, @@ -177,11 +188,13 @@ public function applyConfig(array $schemaConfig): Schema $mutations = $schemaConfig[self::MUTATIONS] ?? []; $interfaces = $schemaConfig[self::INTERFACES] ?? []; $unions = $schemaConfig[self::UNIONS] ?? []; + $bulkLoad = $schemaConfig[self::BULK_LOAD] ?? []; $models = $schemaConfig[self::MODELS] ?? []; $enums = $schemaConfig[self::ENUMS] ?? []; $scalars = $schemaConfig[self::SCALARS] ?? []; $config = $schemaConfig[self::SCHEMA_CONFIG] ?? []; + $this->getConfig()->apply($config); static::assertValidConfig($types); @@ -219,6 +232,22 @@ public function applyConfig(array $schemaConfig): Schema $this->addUnion($union); } + if (!empty($bulkLoad)) { + static::assertValidConfig($bulkLoad); + + foreach ($bulkLoad as $name => $loaderData) { + if ($loaderData === false) { + continue; + } + static::assertValidConfig($loaderData, ['load', 'apply'], ['load', 'apply']); + + $this->logger->info(sprintf('Processing bulk load "%s"', $name)); + + $set = $this->getDefaultBulkLoaderSet() + ->applyConfig($loaderData['load']); + $this->applyBulkLoaders($set, $loaderData['apply']); + } + } static::assertValidConfig($models); foreach ($models as $modelName => $modelConfig) { $model = $this->createModel($modelName, $modelConfig); @@ -1124,6 +1153,65 @@ public function getUnions(): array return $this->unions; } + /** + * @param AbstractBulkLoader $loader + * @param array $modelConfig + * @return $this + * @throws SchemaBuilderException + */ + public function applyBulkLoader(AbstractBulkLoader $loader, array $modelConfig): self + { + $set = $this->getDefaultBulkLoaderSet(); + $set->addLoader($loader); + + return $this->applyBulkLoaders($set, $modelConfig); + } + + /** + * @param BulkLoaderSet $loaders + * @param array $modelConfig + * @return $this + * @throws SchemaBuilderException + */ + public function applyBulkLoaders(BulkLoaderSet $loaders, array $modelConfig): self + { + $collection = $loaders->process(); + $count = 0; + foreach ($collection->getClasses() as $includedClass) { + $modelType = $this->createModel($includedClass, $modelConfig); + if (!$modelType) { + continue; + } + $this->logger->debug("Bulk loaded $includedClass"); + $count++; + $this->addModel($modelType); + } + + $this->logger->info("Bulk loaded $count classes"); + + return $this; + } + + /** + * @return BulkLoaderSet + * @throws SchemaBuilderException + */ + private function getDefaultBulkLoaderSet(): BulkLoaderSet + { + $loaders = []; + $default = $this->getConfig()->get('defaultBulkLoad'); + if ($default && is_array($default)) { + foreach ($default as $id => $config) { + /* @var AbstractBulkLoader $defaultLoader */ + $defaultLoader = Registry::inst()->getByID($id); + static::invariant($defaultLoader, 'Default loader %s not found', $id); + $loaders[] = $defaultLoader->applyConfig($config); + } + } + + return BulkLoaderSet::create($loaders); + } + /** * @return array */ diff --git a/src/Schema/SchemaBuilder.php b/src/Schema/SchemaBuilder.php index 2f28745d..16045596 100644 --- a/src/Schema/SchemaBuilder.php +++ b/src/Schema/SchemaBuilder.php @@ -241,6 +241,7 @@ private static function getSchemaConfigFromSource(string $schemaKey, string $dir $config = [ Schema::SCHEMA_CONFIG => [], Schema::TYPES => [], + Schema::BULK_LOAD => [], Schema::MODELS => [], Schema::QUERIES => [], Schema::MUTATIONS => [], diff --git a/tests/Fake/Extensions/FakeExtension1.php b/tests/Fake/Extensions/FakeExtension1.php new file mode 100644 index 00000000..61bb7d74 --- /dev/null +++ b/tests/Fake/Extensions/FakeExtension1.php @@ -0,0 +1,11 @@ +shouldReturn = $shouldReturn; + } + + public static function getIdentifier(): string + { + return 'fake'; + } + + /** + * @param Collection $collection + * @return Collection + * @throws \Exception + */ + public function collect(Collection $collection): Collection + { + if ($this->shouldReturn) { + return new Collection($this->shouldReturn); + } + + return $collection; + } +} diff --git a/tests/Schema/BulkLoader/BulkLoaderSetTest.php b/tests/Schema/BulkLoader/BulkLoaderSetTest.php new file mode 100644 index 00000000..ed0658f6 --- /dev/null +++ b/tests/Schema/BulkLoader/BulkLoaderSetTest.php @@ -0,0 +1,58 @@ +applyConfig([ + 'fake' => [ + 'include' => [], + ] + ]); + + foreach ($set->getLoaders() as $loader) { + $this->assertInstanceOf(AbstractBulkLoader::class, $loader); + } + } + + public function testInvalidLoader() + { + $this->expectExceptionMessage('Loader "fail" does not exist'); + $set = new BulkLoaderSet(); + $set->applyConfig([ + 'fail' => [ + 'include' => [], + ] + ]); + } + + public function testProcess() + { + $set = new BulkLoaderSet([ + new FakeBulkLoader(['one' => 'one', 'two' => 'two']), + new FakeBulkLoader(['three' => 'three', 'four' => 'four']), + ], new Collection()); + $result = $set->process(); + $this->assertEquals(['three', 'four'], $result->getClasses()); + } + + public function testInitialCollection() + { + $set = new BulkLoaderSet([ + new FakeBulkLoader(), + new FakeBulkLoader(), + ], new Collection(['foo' => 'foo', 'bar' => 'bar'])); + $result = $set->process(); + $this->assertEquals(['foo', 'bar'], $result->getClasses()); + } +} diff --git a/tests/Schema/BulkLoader/CollectionTest.php b/tests/Schema/BulkLoader/CollectionTest.php new file mode 100644 index 00000000..c0ad513c --- /dev/null +++ b/tests/Schema/BulkLoader/CollectionTest.php @@ -0,0 +1,61 @@ + 'file1', + 'class2' => 'file2', + 'class3' => 'file3', + 'class4' => 'file4', + ]); + + $collection->removeClass('class2') + ->removeFile('file3'); + + $this->assertEquals( + ['class1', 'class4'], + $collection->getClasses() + ); + + $this->assertEquals( + ['file1', 'file4'], + $collection->getFiles() + ); + + $this->assertEquals( + [ + 'class1' => 'file1', + 'class4' => 'file4', + ], + $collection->getManifest() + ); + } + + public function testCreateFromClassList() + { + $collection = Collection::createFromClassList([ + A1::class, + B1::class, + ]); + $mod = ModuleLoader::inst()->getManifest()->getModule('silverstripe/graphql'); + $path = $mod->getPath(); + $this->assertEquals( + [ + A1::class => $path . '/tests/Fake/Inheritance/A1.php', + B1::class => $path . '/tests/Fake/Inheritance/B1.php', + ], + $collection->getManifest() + ); + } +} diff --git a/tests/Schema/BulkLoader/ExtensionLoaderTest.php b/tests/Schema/BulkLoader/ExtensionLoaderTest.php new file mode 100644 index 00000000..fccc9102 --- /dev/null +++ b/tests/Schema/BulkLoader/ExtensionLoaderTest.php @@ -0,0 +1,76 @@ +collect($collection)->getClasses(); + $this->assertEquals([ + A1::class, + A2::class, + A1a::class, + ], $result); + + $loader = new ExtensionLoader( + [FakeExtension2::class] + ); + + $result = $loader->collect($collection)->getClasses(); + $this->assertEquals([ + B1::class, + B2::class, + B1a::class, + C1::class, + ], $result); + } +} diff --git a/tests/Schema/BulkLoader/FilepathLoaderTest.php b/tests/Schema/BulkLoader/FilepathLoaderTest.php new file mode 100644 index 00000000..04e8280e --- /dev/null +++ b/tests/Schema/BulkLoader/FilepathLoaderTest.php @@ -0,0 +1,56 @@ +collect($collection)->getClasses(); + + $this->assertEquals([ + B::class, + B1::class, + B2::class, + ], $result); + } +} diff --git a/tests/Schema/BulkLoader/InheritanceLoaderTest.php b/tests/Schema/BulkLoader/InheritanceLoaderTest.php new file mode 100644 index 00000000..7debc7e4 --- /dev/null +++ b/tests/Schema/BulkLoader/InheritanceLoaderTest.php @@ -0,0 +1,57 @@ +collect($collection)->getClasses(); + + $this->assertEquals([ + A1::class, + A1a::class, + B1::class, + B1a::class, + C::class, + C1::class, + ], $result); + } +} diff --git a/tests/Schema/BulkLoader/NamespaceLoaderTest.php b/tests/Schema/BulkLoader/NamespaceLoaderTest.php new file mode 100644 index 00000000..dad5819b --- /dev/null +++ b/tests/Schema/BulkLoader/NamespaceLoaderTest.php @@ -0,0 +1,41 @@ +collect($collection)->getClasses(); + + $this->assertEquals([ + A::class, + A1::class, + A2::class, + A1a::class, + ], $result); + } +} diff --git a/tests/Schema/BulkLoader/RegistryBackendTest.php b/tests/Schema/BulkLoader/RegistryBackendTest.php new file mode 100644 index 00000000..e1aec344 --- /dev/null +++ b/tests/Schema/BulkLoader/RegistryBackendTest.php @@ -0,0 +1,33 @@ +assertInstanceOf(FakeBulkLoader::class, $backend->getByID('fake')); + $this->assertInstanceOf(NamespaceLoader::class, $backend->getByID('namespaceLoader')); + $this->assertNull($backend->getByID('fail')); + } + + public function testExeception() + { + $this->expectException('InvalidArgumentException'); + new RegistryBackend( + new A() + ); + } +} diff --git a/tests/Schema/BulkLoader/RegistryTest.php b/tests/Schema/BulkLoader/RegistryTest.php new file mode 100644 index 00000000..07912cf7 --- /dev/null +++ b/tests/Schema/BulkLoader/RegistryTest.php @@ -0,0 +1,20 @@ +assertSame($inst, $inst2); + + $this->assertInstanceOf(FakeBulkLoader::class, $inst->getByID('fake')); + } +} diff --git a/tests/Schema/IntegrationTest.php b/tests/Schema/IntegrationTest.php index af08b1f1..37b70933 100644 --- a/tests/Schema/IntegrationTest.php +++ b/tests/Schema/IntegrationTest.php @@ -5,6 +5,7 @@ use GraphQL\Type\Definition\ObjectType; use SilverStripe\Assets\File; +use SilverStripe\Core\ClassInfo; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Path; @@ -29,6 +30,7 @@ use SilverStripe\GraphQL\Tests\Fake\FakeRedirectorPage; use SilverStripe\GraphQL\Tests\Fake\FakeReview; use SilverStripe\GraphQL\Tests\Fake\FakeSiteTree; +use SilverStripe\GraphQL\Tests\Fake\Inheritance\A; use SilverStripe\GraphQL\Tests\Fake\IntegrationTestResolver; use SilverStripe\Security\Member; use Symfony\Component\Filesystem\Filesystem; @@ -1058,6 +1060,65 @@ public function testDBFieldArgs() $this->assertEquals('This is a really long text field. It has a few sentences.', $node['myText']); } + public function testBulkLoadInheritance() + { + $schema = $this->createSchema(new TestSchemaBuilder(['_' . __FUNCTION__])); + $this->assertSchemaHasType($schema, 'A1'); + $this->assertSchemaHasType($schema, 'A1a'); + $this->assertSchemaHasType($schema, 'A1b'); + $this->assertSchemaHasType($schema, 'C'); + $this->assertSchemaHasType($schema, 'C1'); + $this->assertSchemaHasType($schema, 'C2'); + $this->assertSchemaNotHasType($schema, 'C2a'); + $this->assertSchemaNotHasType($schema, 'B'); + $this->assertSchemaNotHasType($schema, 'A2'); + + $query = $schema->getQueryType(); + $this->assertNotNull($query->getFieldByName('readA1s')); + $this->assertNotNull($query->getFieldByName('readA1as')); + $this->assertNotNull($query->getFieldByName('readA1bs')); + $this->assertNotNull($query->getFieldByName('readCs')); + $this->assertNotNull($query->getFieldByName('readC1s')); + $this->assertNotNull($query->getFieldByName('readC2s')); + $this->assertNull($query->getFieldByName('readC2as')); + + $a1 = $schema->getType('A1'); + $this->assertNotNull($a1->getFieldByName('A1Field')); + $this->assertNull($a1->getFieldByName('created')); + + $c = $schema->getType('C'); + $this->assertNotNull($c->getFieldByName('cField')); + $this->assertNull($c->getFieldByName('created')); + } + + public function testBulkLoadNamespaceAndFilepath() + { + $schema = $this->createSchema(new TestSchemaBuilder(['_' . __FUNCTION__])); + $this->assertSchemaHasType($schema, 'A1'); + $this->assertSchemaHasType($schema, 'A2'); + $this->assertSchemaHasType($schema, 'A1a'); + $this->assertSchemaHasType($schema, 'A1b'); + $this->assertSchemaHasType($schema, 'A2a'); + + $this->assertSchemaHasType($schema, 'B'); + $this->assertSchemaHasType($schema, 'B1'); + $this->assertSchemaHasType($schema, 'B2'); + $this->assertSchemaHasType($schema, 'B1a'); + $this->assertSchemaHasType($schema, 'B1b'); + + $this->assertSchemaHasType($schema, 'C'); + $this->assertSchemaHasType($schema, 'C1'); + $this->assertSchemaHasType($schema, 'C2'); + + $this->assertSchemaNotHasType($schema, 'C2a'); + + $this->assertSchemaHasType($schema, 'SubFakePage'); + $this->assertSchemaNotHasType($schema, 'FakePage'); + + $this->assertSchemaHasType($schema, 'FakeProductPage'); + $this->assertSchemaHasType($schema, 'FakeRedirectorPage'); + } + /** * @return array */ diff --git a/tests/Schema/_testBulkLoadInheritance/bulkLoad.yml b/tests/Schema/_testBulkLoadInheritance/bulkLoad.yml new file mode 100644 index 00000000..f8473c59 --- /dev/null +++ b/tests/Schema/_testBulkLoadInheritance/bulkLoad.yml @@ -0,0 +1,14 @@ +test: + load: + inheritanceLoader: + include: + - SilverStripe\GraphQL\Tests\Fake\Inheritance\A1 + - SilverStripe\GraphQL\Tests\Fake\Inheritance\C + exclude: + - SilverStripe\GraphQL\Tests\Fake\Inheritance\C2a + apply: + fields: + '*': true + created: false + operations: + read: true diff --git a/tests/Schema/_testBulkLoadNamespaceAndFilepath/bulkLoad.yml b/tests/Schema/_testBulkLoadNamespaceAndFilepath/bulkLoad.yml new file mode 100644 index 00000000..19db1c1a --- /dev/null +++ b/tests/Schema/_testBulkLoadNamespaceAndFilepath/bulkLoad.yml @@ -0,0 +1,24 @@ +test1: + load: + namespaceLoader: + include: + - SilverStripe\GraphQL\Tests\Fake\Inheritance\* + - SilverStripe\GraphQL\Tests\Fake\SubFake\* + exclude: + - SilverStripe\GraphQL\Tests\Fake\Inheritance\C2a + apply: + fields: + '*': true + created: false + operations: + read: true +test2: + load: + filepathLoader: + include: + - 'silverstripe/graphql: tests/Fake/*Page.php' + exclude: + - 'silverstripe/graphql: tests/Fake/FakePage.php' + apply: + fields: + '*': true diff --git a/tests/Schema/_testBulkLoadNamespaceAndFilepath/config.yml b/tests/Schema/_testBulkLoadNamespaceAndFilepath/config.yml new file mode 100644 index 00000000..05661dd8 --- /dev/null +++ b/tests/Schema/_testBulkLoadNamespaceAndFilepath/config.yml @@ -0,0 +1,2 @@ +typeMapping: + SilverStripe\GraphQL\Tests\Fake\SubFake\FakePage: SubFakePage