Skip to content

Commit

Permalink
Merge branch 't3n:master' into task/closure-callback-support-only
Browse files Browse the repository at this point in the history
  • Loading branch information
simstern authored Jan 13, 2022
2 parents 7999cdb + cee6ec9 commit b687086
Show file tree
Hide file tree
Showing 17 changed files with 342 additions and 88 deletions.
35 changes: 27 additions & 8 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: 2.1

aliases:
- &ci-build-image quay.io/yeebase/ci-build:7.2
- &ci-build-image quay.io/yeebase/ci-build:7.3
- &workspace_root ~/workspace

- &save_composer_cache
Expand All @@ -16,11 +16,11 @@ aliases:
- composer-cache-v1-

- &attach_workspace
at: *workspace_root
at: *workspace_root

- &persist_to_workspace
root: .
paths:
paths:
- .

jobs:
Expand All @@ -32,7 +32,7 @@ jobs:
steps:
- checkout
- restore_cache: *restore_composer_cache

- run: |
mkdir graphql
shopt -s extglob dotglob
Expand Down Expand Up @@ -61,17 +61,36 @@ jobs:
FLOW_CONTEXT: Testing
steps:
- attach_workspace: *attach_workspace
- run:
bin/phpunit -c Build/BuildEssentials/PhpUnit/FunctionalTests.xml graphql/Tests/Functional
- run: bin/phpunit -c Build/BuildEssentials/PhpUnit/FunctionalTests.xml graphql/Tests/Functional

workflows:
version: 2
build_and_test:
jobs:
- checkout
- checkout:
filters:
branches:
ignore: /dependabot.*/
- lint:
requires:
- checkout
- tests:
requires:
- checkout
- checkout

build_and_test_dependabot:
jobs:
- hold:
type: approval
filters:
branches:
only: /dependabot.*/
- checkout:
requires:
- hold
- lint:
requires:
- checkout
- tests:
requires:
- checkout
6 changes: 6 additions & 0 deletions .dependabot/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 1
update_configs:
- package_manager: "php:composer"
directory: "/"
update_schedule: "weekly"
target_branch: "master"
21 changes: 15 additions & 6 deletions Classes/Controller/GraphQLController.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,23 @@ class GraphQLController extends ActionController
protected $requestLogger;

/**
* phpcs:disable SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingTraversableParameterTypeHintSpecification
* A list of IANA media types which are supported by this controller
*
* @see http://www.iana.org/assignments/media-types/index.html
*
* @var string[]
*/
protected $supportedMediaTypes = ['application/json'];

/**
* @Flow\SkipCsrfProtection
*
* @param string $endpoint
* @param string $query
* @param array|null $variables
* @param string|null $operationName
* @param mixed[]|null $variables
*
* @throws \Neos\Flow\Mvc\Exception\NoSuchArgumentException
* @throws InvalidContextException
*
* @phpcsSuppress PEAR.Commenting.FunctionComment.MissingParamTag
*/
public function queryAction(string $endpoint, string $query, ?array $variables = null, ?string $operationName = null): string
{
Expand Down Expand Up @@ -100,7 +109,7 @@ public function queryAction(string $endpoint, string $query, ?array $variables =
$validationRules
);

$this->response->setHeader('Content-Type', 'application/json');
$this->response->setContentType('application/json');
return json_encode($result->toArray());
}
}
11 changes: 11 additions & 0 deletions Classes/Exception/InvalidResolverException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace t3n\GraphQL\Exception;

use Neos\Flow\Exception;

class InvalidResolverException extends Exception
{
}
43 changes: 0 additions & 43 deletions Classes/Http/HttpOptionsComponent.php

This file was deleted.

40 changes: 40 additions & 0 deletions Classes/Http/HttpOptionsMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace t3n\GraphQL\Http;

use GuzzleHttp\Psr7\Response;
use Neos\Flow\Annotations as Flow;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class HttpOptionsMiddleware implements MiddlewareInterface
{
/**
* @Flow\InjectConfiguration("endpoints")
*
* @var mixed[]
*/
protected $endpoints;

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if ($request->getMethod() !== 'OPTIONS') {
return $handler->handle($request);
}

// We explode the request target because custom routes like /some/custom/route/<endpoint> are
// are common. So we double check here if the last part in the route matches a configured
// endpoint
$endpoint = explode('/', ltrim($request->getRequestTarget(), '\/'));

if (! isset($this->endpoints[end($endpoint)])) {
return $handler->handle($request);
}

return new Response(200, ['Content-Type' => 'application/json', 'Allow' => 'GET, POST'], json_encode(['success' => true]));
}
}
12 changes: 6 additions & 6 deletions Classes/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use Neos\Flow\Core\Bootstrap;
use Neos\Flow\Monitor\FileMonitor;
use Neos\Flow\Package\Package as BasePackage;
use Neos\Flow\Package\PackageManagerInterface;
use Neos\Flow\Package\PackageManager;
use Neos\Utility\Files;
use Neos\Utility\Unicode\Functions;

Expand All @@ -22,7 +22,7 @@ class Package extends BasePackage
/**
* @param mixed[] $configuration
*/
protected function monitorTypeDefResources(array $configuration, PackageManagerInterface $packageManager, FileMonitor $fileMonitor): void
protected function monitorTypeDefResources(array $configuration, PackageManager $packageManager, FileMonitor $fileMonitor): void
{
$schemas = $configuration['schemas'] ?? null;
if (is_array($schemas)) {
Expand All @@ -44,7 +44,7 @@ protected function monitorTypeDefResources(array $configuration, PackageManagerI

$uriParts = Functions::parse_url($typeDefs);
/** @var BasePackage $package */
$package = $packageManager->getPackage($uriParts['host']);
$package = $packageManager->getPackage($uriParts['host']);
$absolutePath = Files::concatenatePaths([$package->getResourcesPath(), $uriParts['path']]);
$fileMonitor->monitorFile($absolutePath);
}
Expand All @@ -61,9 +61,9 @@ public function boot(Bootstrap $bootstrap): void
return;
}

$graphQLFileMonitor = FileMonitor::createFileMonitorAtBoot(static::FILE_MONITOR_IDENTIFIER, $bootstrap);
$configurationManager = $bootstrap->getEarlyInstance(ConfigurationManager::class);
$packageManager = $bootstrap->getEarlyInstance(PackageManagerInterface::class);
$graphQLFileMonitor = FileMonitor::createFileMonitorAtBoot(static::FILE_MONITOR_IDENTIFIER, $bootstrap);
$configurationManager = $bootstrap->getEarlyInstance(ConfigurationManager::class);
$packageManager = $bootstrap->getEarlyInstance(PackageManager::class);
$endpointsConfiguration = $configurationManager->getConfiguration(
ConfigurationManager::CONFIGURATION_TYPE_SETTINGS,
't3n.GraphQL.endpoints'
Expand Down
19 changes: 19 additions & 0 deletions Classes/ResolverGeneratorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace t3n\GraphQL;

interface ResolverGeneratorInterface
{
/**
* Should return a map with this structure:
*
* return [
* ['typeName' => \Resolver\Class\Name]
* ];
*
* @return mixed[]
*/
public function generate(): array;
}
25 changes: 22 additions & 3 deletions Classes/Resolvers.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Neos\Flow\Reflection\ReflectionService;
use ReflectionClass;
use ReflectionMethod;
use t3n\GraphQL\Exception\InvalidResolverException;

class Resolvers implements ArrayAccess, IteratorAggregate
{
Expand All @@ -33,6 +34,9 @@ class Resolvers implements ArrayAccess, IteratorAggregate
/** @var string */
protected $pathPattern;

/** @var string */
protected $generator;

/** @var mixed[] */
protected $types = [];

Expand All @@ -52,7 +56,7 @@ public static function create(): self
public static function aggregateTypes(ObjectManagerInterface $objectManager): array
{
$reflectionService = $objectManager->get(ReflectionService::class);
$classNames = $reflectionService->getAllImplementationClassNamesForInterface(ResolverInterface::class);
$classNames = $reflectionService->getAllImplementationClassNamesForInterface(ResolverInterface::class);

$types = [];
foreach ($classNames as $className) {
Expand Down Expand Up @@ -89,9 +93,18 @@ protected function initialize(): void
return;
}

$typeMap = static::aggregateTypes($this->objectManager);
$typeMap = static::aggregateTypes($this->objectManager);
$resolverClasses = [];

if ($this->generator) {
$generator = $this->objectManager->get($this->generator);

if (! $generator instanceof ResolverGeneratorInterface) {
throw new InvalidResolverException(sprintf('The configured resolver %s generator must implement ResolverGeneratorInterface', $this->generator));
}
$resolverClasses = $generator->generate();
}

if ($this->pathPattern) {
foreach ($typeMap as $className => $info) {
$possibleMatch = str_replace('{Type}', $info['typeName'], $this->pathPattern);
Expand All @@ -113,7 +126,7 @@ protected function initialize(): void
}

foreach ($resolverClasses as $typeName => $className) {
$fields = $typeMap[$className]['fields'];
$fields = $typeMap[$className]['fields'];
$this->resolvers[$typeName] = [];
foreach ($fields as $fieldName) {
$this->resolvers[$typeName][$fieldName] = function (...$args) use ($className, $fieldName) {
Expand All @@ -123,6 +136,12 @@ protected function initialize(): void
}
}

public function withGenerator(string $generatorClassname): self
{
$this->generator = $generatorClassname;
return $this;
}

public function withPathPattern(string $pathPattern): self
{
$this->pathPattern = trim($pathPattern, '/');
Expand Down
19 changes: 17 additions & 2 deletions Classes/Service/SchemaService.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ protected function getSchemaFromConfiguration(array $configuration): array
}

$resolvers = Resolvers::create();

if (isset($configuration['resolverGenerator'])) {
if ($this->objectManager->isRegistered($configuration['resolverGenerator'])) {
$resolvers->withGenerator($configuration['resolverGenerator']);
}
}

if (isset($configuration['resolverPathPattern'])) {
$resolvers->withPathPattern($configuration['resolverPathPattern']);
}
Expand Down Expand Up @@ -153,13 +160,21 @@ protected function getMergedSchemaFromConfigurations(array $configuration): Sche

$executableSchemas = [];

$transforms = [new FlowErrorTransform()];
// Determine default error transformer
if (array_key_exists('errorTransform', $configuration) && ! empty($configuration['errorTransform']) && class_exists($configuration['errorTransform'])) {
$transforms = [new $configuration['errorTransform']()];
} else {
$transforms = [new FlowErrorTransform()];
}

$options = [
'typeDefs' => [],
'resolvers' => [],
'schemaDirectives' => [],
'resolverValidationOptions' => ['allowResolversNotInSchema' => true],
'resolverValidationOptions' => [
'allowResolversNotInSchema' => $configuration['resolverValidationOptions']['allowResolversNotInSchema'] ?? true,
'requireResolversForResolveType' => $configuration['resolverValidationOptions']['requireResolversForResolveType'] ?? null,
],
];

foreach ($schemaConfigurations as $schemaConfiguration) {
Expand Down
2 changes: 1 addition & 1 deletion Classes/Transform/FlowErrorTransform.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function transformResult(ExecutionResult $result): ExecutionResult
$message = $this->throwableStorage->logThrowable($previousError);

if (! $this->includeExceptionMessageInOutput) {
$message = preg_replace('/.* - See also: (.+)\.txt$/', 'Internal error ($1)', $message);
$message = preg_replace('/.* - See also: (.+)\.txt$/s', 'Internal error ($1)', $message);
}

return new Error(
Expand Down
Loading

0 comments on commit b687086

Please sign in to comment.