new Deps
This commit is contained in:
parent
e7a49138bb
commit
aae17f10a6
7
vendor/haydenpierce/class-finder/.gitattributes
vendored
Normal file
7
vendor/haydenpierce/class-finder/.gitattributes
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/ci/ export-ignore
|
||||
/docs/ export-ignore
|
||||
/test/ export-ignore
|
||||
/.gitignore export-ignore
|
||||
/.gitlab-ci.yml export-ignore
|
||||
/composer.lock export-ignore
|
||||
/phpunit.xml export-ignore
|
||||
13
vendor/haydenpierce/class-finder/LICENSE
vendored
Normal file
13
vendor/haydenpierce/class-finder/LICENSE
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
Copyright (c) 2018 Hayden Pierce
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
126
vendor/haydenpierce/class-finder/README.md
vendored
Normal file
126
vendor/haydenpierce/class-finder/README.md
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
ClassFinder
|
||||
===========
|
||||
|
||||
A dead simple utility to identify classes in a given namespace.
|
||||
|
||||
This package is an improved implementation of an [answer on Stack Overflow](https://stackoverflow.com/a/40229665/3000068)
|
||||
and provides additional features with less configuration required.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
* Application is using Composer.
|
||||
* Classes can be autoloaded with Composer.
|
||||
* PHP >= 5.3.0
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Installing is done by requiring it with Composer.
|
||||
|
||||
```
|
||||
$ composer require haydenpierce/class-finder
|
||||
```
|
||||
|
||||
No other installation methods are currently supported.
|
||||
|
||||
Supported Autoloading Methods
|
||||
--------------------------------
|
||||
|
||||
| Method | Supported | with `ClassFinder::RECURSIVE_MODE` |
|
||||
| ---------- | --------- | ---------------------------------- |
|
||||
| PSR-4 | ✔️ | ✔️ |
|
||||
| PSR-0 | ❌️* | ❌️* |
|
||||
| Classmap | ✔️ | ✔️ |
|
||||
| Files | ✔️^ | ❌️** |
|
||||
|
||||
\^ Experimental.
|
||||
|
||||
\* Planned.
|
||||
|
||||
\** Not planned. Open an issue if you need this feature.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
**Standard Mode**
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$classes = ClassFinder::getClassesInNamespace('TestApp1\Foo');
|
||||
|
||||
/**
|
||||
* array(
|
||||
* 'TestApp1\Foo\Bar',
|
||||
* 'TestApp1\Foo\Baz',
|
||||
* 'TestApp1\Foo\Foo'
|
||||
* )
|
||||
*/
|
||||
var_dump($classes);
|
||||
```
|
||||
|
||||
**Recursive Mode** *(in v0.3-beta)*
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$classes = ClassFinder::getClassesInNamespace('TestApp1\Foo', ClassFinder::RECURSIVE_MODE);
|
||||
|
||||
/**
|
||||
* array(
|
||||
* 'TestApp1\Foo\Bar',
|
||||
* 'TestApp1\Foo\Baz',
|
||||
* 'TestApp1\Foo\Foo',
|
||||
* 'TestApp1\Foo\Box\Bar',
|
||||
* 'TestApp1\Foo\Box\Baz',
|
||||
* 'TestApp1\Foo\Box\Foo',
|
||||
* 'TestApp1\Foo\Box\Lon\Bar',
|
||||
* 'TestApp1\Foo\Box\Lon\Baz',
|
||||
* 'TestApp1\Foo\Box\Lon\Foo',
|
||||
* )
|
||||
*/
|
||||
var_dump($classes);
|
||||
```
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
[Changelog](docs/changelog.md)
|
||||
|
||||
**Exceptions**:
|
||||
|
||||
* [Files could not locate PHP](docs/exceptions/filesCouldNotLocatePHP.md)
|
||||
* [Files exec not available](docs/exceptions/filesExecNotAvailable.md)
|
||||
* [Missing composer.json](docs/exceptions/missingComposerConfig.md)
|
||||
|
||||
**Internals**
|
||||
|
||||
* [How Testing Works](docs/testing.md)
|
||||
* [Continuous Integration Notes](docs/ci.md)
|
||||
|
||||
Future Work
|
||||
-----------
|
||||
|
||||
> **WARNING**: Before 1.0.0, expect that bug fixes _will not_ be backported to older versions. Backwards incompatible changes
|
||||
may be introduced in minor 0.X.Y versions, where X changes.
|
||||
|
||||
* `psr0` support
|
||||
|
||||
* Additional features:
|
||||
|
||||
Various ideas:
|
||||
|
||||
* ~~`ClassFinder::getClassesInNamespace('TestApp1\Foo', ClassFinder::RECURSIVE_MODE)`.
|
||||
Providing classes multiple namespaces deep.~~ (included v0.3-beta)
|
||||
|
||||
* `ClassFinder::getClassesImplementingInterface('TestApp1\Foo', 'TestApp1\FooInterface', ClassFinder::RECURSIVE_MODE)`.
|
||||
Filtering classes to only classes that implement a namespace.
|
||||
|
||||
* `ClassFinder::debugRenderReport('TestApp1\Foo\Baz')`
|
||||
Guidance for solving "class not found" errors resulting from typos in namespaces, missing directories, etc. Would print
|
||||
an HTML report. Not intended for production use, but debugging.
|
||||
27
vendor/haydenpierce/class-finder/composer.json
vendored
Normal file
27
vendor/haydenpierce/class-finder/composer.json
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "haydenpierce/class-finder",
|
||||
"description" : "A library that can provide of a list of classes in a given namespace",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"version": "0.4.3",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Hayden Pierce",
|
||||
"email": "hayden@haydenpierce.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~9.0",
|
||||
"mikey179/vfsstream": "^1.6"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"HaydenPierce\\ClassFinder\\": "src/",
|
||||
"HaydenPierce\\ClassFinder\\UnitTest\\": "test/unit"
|
||||
}
|
||||
}
|
||||
}
|
||||
82
vendor/haydenpierce/class-finder/src/AppConfig.php
vendored
Normal file
82
vendor/haydenpierce/class-finder/src/AppConfig.php
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder;
|
||||
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
|
||||
class AppConfig
|
||||
{
|
||||
/** @var string */
|
||||
private $appRoot;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->appRoot = $this->findAppRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function findAppRoot()
|
||||
{
|
||||
if ($this->appRoot) {
|
||||
$appRoot = $this->appRoot;
|
||||
} else {
|
||||
$workingDirectory = str_replace('\\', '/', __DIR__);
|
||||
$workingDirectory = str_replace('/vendor/haydenpierce/class-finder/src', '', $workingDirectory);
|
||||
$directoryPathPieces = explode('/', $workingDirectory);
|
||||
|
||||
$appRoot = null;
|
||||
do {
|
||||
$path = implode('/', $directoryPathPieces) . '/composer.json';
|
||||
if (file_exists($path)) {
|
||||
$appRoot = implode('/', $directoryPathPieces) . '/';
|
||||
} else {
|
||||
array_pop($directoryPathPieces);
|
||||
}
|
||||
} while (is_null($appRoot) && count($directoryPathPieces) > 0);
|
||||
}
|
||||
|
||||
$this->throwIfInvalidAppRoot($appRoot);
|
||||
|
||||
$this->appRoot= $appRoot;
|
||||
return $this->appRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $appRoot
|
||||
* @return void
|
||||
* @throws ClassFinderException
|
||||
*/
|
||||
private function throwIfInvalidAppRoot($appRoot)
|
||||
{
|
||||
if (!file_exists($appRoot . '/composer.json')) {
|
||||
throw new ClassFinderException(sprintf("Could not locate composer.json. You can get around this by setting ClassFinder::\$appRoot manually. See '%s' for details.",
|
||||
'https://gitlab.com/hpierce1102/ClassFinder/blob/master/docs/exceptions/missingComposerConfig.md'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAppRoot()
|
||||
{
|
||||
if ($this->appRoot === null) {
|
||||
$this->appRoot = $this->findAppRoot();
|
||||
}
|
||||
|
||||
$this->throwIfInvalidAppRoot($this->appRoot);
|
||||
|
||||
return $this->appRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $appRoot
|
||||
* @return void
|
||||
*/
|
||||
public function setAppRoot($appRoot)
|
||||
{
|
||||
$this->appRoot = $appRoot;
|
||||
}
|
||||
}
|
||||
208
vendor/haydenpierce/class-finder/src/ClassFinder.php
vendored
Normal file
208
vendor/haydenpierce/class-finder/src/ClassFinder.php
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder;
|
||||
|
||||
use HaydenPierce\ClassFinder\Classmap\ClassmapEntryFactory;
|
||||
use HaydenPierce\ClassFinder\Classmap\ClassmapFinder;
|
||||
use HaydenPierce\ClassFinder\Files\FilesEntryFactory;
|
||||
use HaydenPierce\ClassFinder\Files\FilesFinder;
|
||||
use HaydenPierce\ClassFinder\PSR4\PSR4Finder;
|
||||
use HaydenPierce\ClassFinder\PSR4\PSR4NamespaceFactory;
|
||||
|
||||
class ClassFinder
|
||||
{
|
||||
const STANDARD_MODE = 1;
|
||||
const RECURSIVE_MODE = 2;
|
||||
|
||||
/** @var AppConfig */
|
||||
private static $config;
|
||||
|
||||
/** @var PSR4Finder */
|
||||
private static $psr4;
|
||||
|
||||
/** @var ClassmapFinder */
|
||||
private static $classmap;
|
||||
|
||||
/** @var FilesFinder */
|
||||
private static $files;
|
||||
|
||||
/** @var boolean */
|
||||
private static $useFilesSupport = false;
|
||||
|
||||
/** @var boolean */
|
||||
private static $usePSR4Support = true;
|
||||
|
||||
/** @var boolean */
|
||||
private static $useClassmapSupport = true;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private static function initialize()
|
||||
{
|
||||
if (!(self::$config instanceof AppConfig)) {
|
||||
self::$config = new AppConfig();
|
||||
}
|
||||
|
||||
if (!(self::$psr4 instanceof PSR4Finder)) {
|
||||
$PSR4Factory = new PSR4NamespaceFactory(self::$config);
|
||||
self::$psr4 = new PSR4Finder($PSR4Factory);
|
||||
}
|
||||
|
||||
if (!(self::$classmap instanceof ClassmapFinder)) {
|
||||
$classmapFactory = new ClassmapEntryFactory(self::$config);
|
||||
self::$classmap = new ClassmapFinder($classmapFactory);
|
||||
}
|
||||
|
||||
if (!(self::$files instanceof FilesFinder) && self::$useFilesSupport) {
|
||||
$filesFactory = new FilesEntryFactory(self::$config);
|
||||
self::$files = new FilesFinder($filesFactory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify classes in a given namespace.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param int $options
|
||||
* @return string[]
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getClassesInNamespace($namespace, $options = self::STANDARD_MODE)
|
||||
{
|
||||
self::initialize();
|
||||
|
||||
$findersWithNamespace = self::findersWithNamespace($namespace);
|
||||
|
||||
$classes = array_reduce($findersWithNamespace, function($carry, FinderInterface $finder) use ($namespace, $options){
|
||||
return array_merge($carry, $finder->findClasses($namespace, $options));
|
||||
}, array());
|
||||
|
||||
return array_unique($classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given namespace contains any classes.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public static function namespaceHasClasses($namespace)
|
||||
{
|
||||
self::initialize();
|
||||
|
||||
return count(self::findersWithNamespace($namespace)) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $appRoot
|
||||
* @return void
|
||||
*/
|
||||
public static function setAppRoot($appRoot)
|
||||
{
|
||||
self::initialize();
|
||||
self::$config->setAppRoot($appRoot);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function enableExperimentalFilesSupport()
|
||||
{
|
||||
self::$useFilesSupport = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function disableExperimentalFilesSupport()
|
||||
{
|
||||
self::$useFilesSupport = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function enablePSR4Support()
|
||||
{
|
||||
self::$usePSR4Support = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function disablePSR4Support()
|
||||
{
|
||||
self::$usePSR4Support = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function enableClassmapSupport()
|
||||
{
|
||||
self::$useClassmapSupport = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function disableClassmapSupport()
|
||||
{
|
||||
self::$useClassmapSupport = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FinderInterface[]
|
||||
*/
|
||||
private static function getSupportedFinders()
|
||||
{
|
||||
$supportedFinders = array();
|
||||
|
||||
/*
|
||||
* This is done for testing. For some tests, allowing PSR4 classes contaminates the test results. This could also be
|
||||
* disabled for performance reasons (less finders in use means less work), but most people probably won't do that.
|
||||
*/
|
||||
if (self::$usePSR4Support) {
|
||||
$supportedFinders[] = self::$psr4;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is done for testing. For some tests, allowing classmap classes contaminates the test results. This could also be
|
||||
* disabled for performance reasons (less finders in use means less work), but most people probably won't do that.
|
||||
*/
|
||||
if (self::$useClassmapSupport) {
|
||||
$supportedFinders[] = self::$classmap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Files support is tucked away behind a flag because it will need to use some kind of shell access via exec, or
|
||||
* system.
|
||||
*
|
||||
* #1 Many environments (such as shared space hosts) may not allow these functions, and attempting to call
|
||||
* these functions will blow up.
|
||||
* #2 I've heard of performance issues with calling these functions.
|
||||
* #3 Files support probably doesn't benefit most projects.
|
||||
* #4 Using exec() or system() is against many PHP developers' religions.
|
||||
*/
|
||||
if (self::$useFilesSupport) {
|
||||
$supportedFinders[] = self::$files;
|
||||
}
|
||||
|
||||
return $supportedFinders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return FinderInterface[]
|
||||
*/
|
||||
private static function findersWithNamespace($namespace)
|
||||
{
|
||||
$findersWithNamespace = array_filter(self::getSupportedFinders(), function (FinderInterface $finder) use ($namespace) {
|
||||
return $finder->isNamespaceKnown($namespace);
|
||||
});
|
||||
|
||||
return $findersWithNamespace;
|
||||
}
|
||||
}
|
||||
77
vendor/haydenpierce/class-finder/src/Classmap/ClassmapEntry.php
vendored
Normal file
77
vendor/haydenpierce/class-finder/src/Classmap/ClassmapEntry.php
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Classmap;
|
||||
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
|
||||
class ClassmapEntry
|
||||
{
|
||||
/** @var string */
|
||||
private $className;
|
||||
|
||||
/**
|
||||
* @param string $fullyQualifiedClassName
|
||||
*/
|
||||
public function __construct($fullyQualifiedClassName)
|
||||
{
|
||||
$this->className = $fullyQualifiedClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function knowsNamespace($namespace)
|
||||
{
|
||||
return strpos($this->className, $namespace) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function matches($namespace, $options)
|
||||
{
|
||||
if ($options === ClassFinder::RECURSIVE_MODE) {
|
||||
return $this->doesMatchAnyNamespace($namespace);
|
||||
} else {
|
||||
return $this->doesMatchDirectNamespace($namespace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getClassName()
|
||||
{
|
||||
return $this->className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the class is a child or subchild of the given namespace.
|
||||
*
|
||||
* @param $namespace
|
||||
* @return bool
|
||||
*/
|
||||
private function doesMatchAnyNamespace($namespace)
|
||||
{
|
||||
return strpos($this->getClassName(),$namespace) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the class is a DIRECT child of the given namespace.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
private function doesMatchDirectNamespace($namespace)
|
||||
{
|
||||
$classNameFragments = explode('\\', $this->getClassName());
|
||||
array_pop($classNameFragments);
|
||||
$classNamespace = implode('\\', $classNameFragments);
|
||||
|
||||
$namespace = trim($namespace, '\\');
|
||||
|
||||
return $namespace === $classNamespace;
|
||||
}
|
||||
}
|
||||
36
vendor/haydenpierce/class-finder/src/Classmap/ClassmapEntryFactory.php
vendored
Normal file
36
vendor/haydenpierce/class-finder/src/Classmap/ClassmapEntryFactory.php
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Classmap;
|
||||
|
||||
use HaydenPierce\ClassFinder\AppConfig;
|
||||
|
||||
class ClassmapEntryFactory
|
||||
{
|
||||
/** @var AppConfig */
|
||||
private $appConfig;
|
||||
|
||||
public function __construct(AppConfig $appConfig)
|
||||
{
|
||||
$this->appConfig = $appConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassmapEntry[]
|
||||
*/
|
||||
public function getClassmapEntries()
|
||||
{
|
||||
// Composer will compile user declared mappings to autoload_classmap.php. So no additional work is needed
|
||||
// to fetch user provided entries.
|
||||
$classmap = require($this->appConfig->getAppRoot() . 'vendor/composer/autoload_classmap.php');
|
||||
|
||||
// if classmap has no entries return empty array
|
||||
if(count($classmap) == 0) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$classmapKeys = array_keys($classmap);
|
||||
return array_map(function($index) use ($classmapKeys){
|
||||
return new ClassmapEntry($classmapKeys[$index]);
|
||||
}, range(0, count($classmap) - 1));
|
||||
}
|
||||
}
|
||||
51
vendor/haydenpierce/class-finder/src/Classmap/ClassmapFinder.php
vendored
Normal file
51
vendor/haydenpierce/class-finder/src/Classmap/ClassmapFinder.php
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Classmap;
|
||||
|
||||
use HaydenPierce\ClassFinder\FinderInterface;
|
||||
|
||||
class ClassmapFinder implements FinderInterface
|
||||
{
|
||||
/** @var ClassmapEntryFactory */
|
||||
private $factory;
|
||||
|
||||
public function __construct(ClassmapEntryFactory $factory)
|
||||
{
|
||||
$this->factory = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function isNamespaceKnown($namespace)
|
||||
{
|
||||
$classmapEntries = $this->factory->getClassmapEntries();
|
||||
|
||||
foreach($classmapEntries as $classmapEntry) {
|
||||
if ($classmapEntry->knowsNamespace($namespace)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @param int $options
|
||||
* @return string[]
|
||||
*/
|
||||
public function findClasses($namespace, $options)
|
||||
{
|
||||
$classmapEntries = $this->factory->getClassmapEntries();
|
||||
|
||||
$matchingEntries = array_filter($classmapEntries, function(ClassmapEntry $entry) use ($namespace, $options) {
|
||||
return $entry->matches($namespace, $options);
|
||||
});
|
||||
|
||||
return array_map(function(ClassmapEntry $entry) {
|
||||
return $entry->getClassName();
|
||||
}, $matchingEntries);
|
||||
}
|
||||
}
|
||||
8
vendor/haydenpierce/class-finder/src/Exception/ClassFinderException.php
vendored
Normal file
8
vendor/haydenpierce/class-finder/src/Exception/ClassFinderException.php
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Exception;
|
||||
|
||||
class ClassFinderException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
101
vendor/haydenpierce/class-finder/src/Files/FilesEntry.php
vendored
Normal file
101
vendor/haydenpierce/class-finder/src/Files/FilesEntry.php
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Files;
|
||||
|
||||
class FilesEntry
|
||||
{
|
||||
/** @var string */
|
||||
private $file;
|
||||
|
||||
/** @var string */
|
||||
private $php;
|
||||
|
||||
/**
|
||||
* @param string $fileToInclude
|
||||
* @param string $php
|
||||
*/
|
||||
public function __construct($fileToInclude, $php)
|
||||
{
|
||||
$this->file = $this->normalizePath($fileToInclude);
|
||||
$this->php = $php;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function knowsNamespace($namespace)
|
||||
{
|
||||
$classes = $this->getClassesInFile();
|
||||
|
||||
foreach($classes as $class) {
|
||||
if (strpos($class, $namespace) !== false) {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of classes that belong to the given namespace.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return string[]
|
||||
*/
|
||||
public function getClasses($namespace)
|
||||
{
|
||||
$classes = $this->getClassesInFile();
|
||||
|
||||
return array_values(array_filter($classes, function($class) use ($namespace) {
|
||||
$classNameFragments = explode('\\', $class);
|
||||
array_pop($classNameFragments);
|
||||
$classNamespace = implode('\\', $classNameFragments);
|
||||
|
||||
$namespace = trim($namespace, '\\');
|
||||
|
||||
return $namespace === $classNamespace;
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically execute files and check for defined classes.
|
||||
*
|
||||
* This is where the real magic happens. Since classes in a randomly included file could contain classes in any namespace,
|
||||
* (or even multiple namespaces!) we must execute the file and check for newly defined classes. This has a potential
|
||||
* downside that files being executed will execute their side effects - which may be undesirable. However, Composer
|
||||
* will require these files anyway - so hopefully causing those side effects isn't that big of a deal.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getClassesInFile()
|
||||
{
|
||||
// get_declared_classes() returns a bunch of classes that are built into PHP. So we need a control here.
|
||||
$script = "var_export(get_declared_classes());";
|
||||
exec($this->php . " -r \"$script\"", $output);
|
||||
$classes = 'return ' . implode('', $output) . ';';
|
||||
$initialClasses = eval($classes);
|
||||
|
||||
// clear the exec() buffer.
|
||||
unset($output);
|
||||
|
||||
// This brings in the new classes. so $classes here will include the PHP defaults and the newly defined classes
|
||||
$script = "require_once '{$this->file}'; var_export(get_declared_classes());";
|
||||
exec($this->php . ' -r "' . $script . '"', $output);
|
||||
$classes = 'return ' . implode('', $output) . ';';
|
||||
$allClasses = eval($classes);
|
||||
|
||||
return array_diff($allClasses, $initialClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Similar to PSR4Namespace::normalizePath. Maybe we refactor?
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
private function normalizePath($path)
|
||||
{
|
||||
$path = str_replace('\\', '/', $path);
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
66
vendor/haydenpierce/class-finder/src/Files/FilesEntryFactory.php
vendored
Normal file
66
vendor/haydenpierce/class-finder/src/Files/FilesEntryFactory.php
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Files;
|
||||
|
||||
use HaydenPierce\ClassFinder\AppConfig;
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
|
||||
class FilesEntryFactory
|
||||
{
|
||||
/** @var AppConfig */
|
||||
private $appConfig;
|
||||
|
||||
public function __construct(AppConfig $appConfig)
|
||||
{
|
||||
$this->appConfig = $appConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FilesEntry[]
|
||||
*/
|
||||
public function getFilesEntries()
|
||||
{
|
||||
$files = require($this->appConfig->getAppRoot() . 'vendor/composer/autoload_files.php');
|
||||
$phpPath = $this->findPHP();
|
||||
|
||||
$filesKeys = array_values($files);
|
||||
return array_map(function($index) use ($filesKeys, $phpPath){
|
||||
return new FilesEntry($filesKeys[$index], $phpPath);
|
||||
}, range(0, count($files) - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the PHP interrupter.
|
||||
*
|
||||
* If PHP 5.4 or newer is used, the PHP_BINARY value is used.
|
||||
* Otherwise we attempt to find it from shell commands.
|
||||
*
|
||||
* @return string
|
||||
* @throws ClassFinderException
|
||||
*/
|
||||
private function findPHP()
|
||||
{
|
||||
if (defined("PHP_BINARY")) {
|
||||
// PHP_BINARY was made available in PHP 5.4
|
||||
$php = PHP_BINARY;
|
||||
} else {
|
||||
$isHostWindows = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
|
||||
if ($isHostWindows) {
|
||||
exec('where php', $output);
|
||||
$php = $output[0];
|
||||
} else {
|
||||
exec('which php', $output);
|
||||
$php = $output[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($php)) {
|
||||
throw new ClassFinderException(sprintf(
|
||||
'Could not locate PHP interrupter. See "%s" for details.',
|
||||
'https://gitlab.com/hpierce1102/ClassFinder/blob/master/docs/exceptions/filesCouldNotLocatePHP.md'
|
||||
));
|
||||
}
|
||||
|
||||
return $php;
|
||||
}
|
||||
}
|
||||
59
vendor/haydenpierce/class-finder/src/Files/FilesFinder.php
vendored
Normal file
59
vendor/haydenpierce/class-finder/src/Files/FilesFinder.php
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Files;
|
||||
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
use HaydenPierce\ClassFinder\FinderInterface;
|
||||
|
||||
class FilesFinder implements FinderInterface
|
||||
{
|
||||
/** @var FilesEntryFactory */
|
||||
private $factory;
|
||||
|
||||
/**
|
||||
* @param FilesEntryFactory $factory
|
||||
* @throws ClassFinderException
|
||||
*/
|
||||
public function __construct(FilesEntryFactory $factory)
|
||||
{
|
||||
$this->factory = $factory;
|
||||
|
||||
if (!function_exists('exec')) {
|
||||
throw new ClassFinderException(sprintf(
|
||||
'FilesFinder requires that exec() is available. Check your php.ini to see if it is disabled. See "%s" for details.',
|
||||
'https://gitlab.com/hpierce1102/ClassFinder/blob/master/docs/exceptions/filesExecNotAvailable.md'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function isNamespaceKnown($namespace)
|
||||
{
|
||||
$filesEntries = $this->factory->getFilesEntries();
|
||||
|
||||
foreach($filesEntries as $filesEntry) {
|
||||
if ($filesEntry->knowsNamespace($namespace)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @param int $options
|
||||
* @return string[]
|
||||
*/
|
||||
public function findClasses($namespace, $options)
|
||||
{
|
||||
$filesEntries = $this->factory->getFilesEntries();
|
||||
|
||||
return array_reduce($filesEntries, function($carry, FilesEntry $entry) use ($namespace){
|
||||
return array_merge($carry, $entry->getClasses($namespace));
|
||||
}, array());
|
||||
}
|
||||
}
|
||||
29
vendor/haydenpierce/class-finder/src/FinderInterface.php
vendored
Normal file
29
vendor/haydenpierce/class-finder/src/FinderInterface.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder;
|
||||
|
||||
interface FinderInterface
|
||||
{
|
||||
/**
|
||||
* Find classes in a given namespace.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param int $options
|
||||
* @return string[]
|
||||
*/
|
||||
public function findClasses($namespace, $options);
|
||||
|
||||
/**
|
||||
* Check if a given namespace is known.
|
||||
*
|
||||
* A namespace is "known" if a Finder can determine that the autoloader can create classes from that namespace.
|
||||
*
|
||||
* For instance:
|
||||
* If given a classmap for "TestApp1\Foo\Bar\Baz", the namespace "TestApp1\Foo" is known, even if nothing loads
|
||||
* from that namespace directly. It is known because classes that include that namespace are known.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function isNamespaceKnown($namespace);
|
||||
}
|
||||
106
vendor/haydenpierce/class-finder/src/PSR4/PSR4Finder.php
vendored
Normal file
106
vendor/haydenpierce/class-finder/src/PSR4/PSR4Finder.php
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\PSR4;
|
||||
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
use HaydenPierce\ClassFinder\FinderInterface;
|
||||
|
||||
class PSR4Finder implements FinderInterface
|
||||
{
|
||||
/** @var PSR4NamespaceFactory */
|
||||
private $factory;
|
||||
|
||||
public function __construct(PSR4NamespaceFactory $factory)
|
||||
{
|
||||
$this->factory = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @param int $options
|
||||
* @return string[]
|
||||
*/
|
||||
public function findClasses($namespace, $options)
|
||||
{
|
||||
if ($options === ClassFinder::RECURSIVE_MODE) {
|
||||
$applicableNamespaces = $this->findAllApplicableNamespaces($namespace);
|
||||
}
|
||||
|
||||
if (empty($applicableNamespaces)) {
|
||||
$bestNamespace = $this->findBestPSR4Namespace($namespace);
|
||||
$applicableNamespaces = array($bestNamespace);
|
||||
}
|
||||
|
||||
return array_reduce($applicableNamespaces, function($carry, $psr4NamespaceOrNull) use ($namespace, $options) {
|
||||
if ($psr4NamespaceOrNull instanceof PSR4Namespace) {
|
||||
$classes = $psr4NamespaceOrNull->findClasses($namespace, $options);
|
||||
} else {
|
||||
$classes = array();
|
||||
}
|
||||
|
||||
return array_merge($carry, $classes);
|
||||
}, array());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function isNamespaceKnown($namespace)
|
||||
{
|
||||
$composerNamespaces = $this->factory->getPSR4Namespaces();
|
||||
|
||||
foreach($composerNamespaces as $psr4Namespace) {
|
||||
if ($psr4Namespace->knowsNamespace($namespace)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return PSR4Namespace[]
|
||||
*/
|
||||
private function findAllApplicableNamespaces($namespace)
|
||||
{
|
||||
$composerNamespaces = $this->factory->getPSR4Namespaces();
|
||||
|
||||
return array_filter($composerNamespaces, function(PSR4Namespace $potentialNamespace) use ($namespace){
|
||||
return $potentialNamespace->isAcceptableNamespaceRecursiveMode($namespace);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return PSR4Namespace
|
||||
*/
|
||||
private function findBestPSR4Namespace($namespace)
|
||||
{
|
||||
$composerNamespaces = $this->factory->getPSR4Namespaces();
|
||||
|
||||
$acceptableNamespaces = array_filter($composerNamespaces, function(PSR4Namespace $potentialNamespace) use ($namespace){
|
||||
return $potentialNamespace->isAcceptableNamespace($namespace);
|
||||
});
|
||||
|
||||
$carry = new \stdClass();
|
||||
$carry->highestMatchingSegments = 0;
|
||||
$carry->bestNamespace = null;
|
||||
|
||||
/** @var PSR4Namespace $bestNamespace */
|
||||
$bestNamespace = array_reduce($acceptableNamespaces, function ($carry, PSR4Namespace $potentialNamespace) use ($namespace) {
|
||||
$matchingSegments = $potentialNamespace->countMatchingNamespaceSegments($namespace);
|
||||
|
||||
if ($matchingSegments > $carry->highestMatchingSegments) {
|
||||
$carry->highestMatchingSegments = $matchingSegments;
|
||||
$carry->bestNamespace = $potentialNamespace;
|
||||
}
|
||||
|
||||
return $carry;
|
||||
}, $carry);
|
||||
|
||||
return $bestNamespace->bestNamespace;
|
||||
}
|
||||
}
|
||||
302
vendor/haydenpierce/class-finder/src/PSR4/PSR4Namespace.php
vendored
Normal file
302
vendor/haydenpierce/class-finder/src/PSR4/PSR4Namespace.php
vendored
Normal file
@ -0,0 +1,302 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\PSR4;
|
||||
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
|
||||
class PSR4Namespace
|
||||
{
|
||||
/** @var string */
|
||||
private $namespace;
|
||||
|
||||
/** @var string[] */
|
||||
private $directories;
|
||||
|
||||
/** @var PSR4Namespace[] */
|
||||
private $directSubnamespaces;
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @param string[] $directories
|
||||
*/
|
||||
public function __construct($namespace, $directories)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
$this->directories = $directories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function knowsNamespace($namespace)
|
||||
{
|
||||
$numberOfSegments = count(explode('\\', $namespace));
|
||||
$matchingSegments = $this->countMatchingNamespaceSegments($namespace);
|
||||
|
||||
if ($matchingSegments === 0) {
|
||||
// Provided namespace doesn't map to anything registered.
|
||||
return false;
|
||||
} elseif ($numberOfSegments <= $matchingSegments) {
|
||||
// This namespace is a superset of the provided namespace. Namespace is known.
|
||||
return true;
|
||||
} else {
|
||||
// This namespace is a subset of the provided namespace. We must resolve the remaining segments to a directory.
|
||||
$relativePath = substr($namespace, strlen($this->namespace));
|
||||
foreach ($this->directories as $directory) {
|
||||
$path = $this->normalizePath($directory, $relativePath);
|
||||
if (is_dir($path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines how many namespace segments match the internal namespace. This is useful because multiple namespaces
|
||||
* may technically match a registered namespace root, but one of the matches may be a better match. Namespaces that
|
||||
* match, but are not _the best_ match are incorrect matches. TestApp1\\ is **not** the best match when searching for
|
||||
* namespace TestApp1\\Multi\\Foo if TestApp1\\Multi was explicitly registered.
|
||||
*
|
||||
* PSR4Namespace $a;
|
||||
* $a->namespace = "TestApp1\\";
|
||||
* $a->countMatchingNamespaceSegments("TestApp1\\Multi") -> 1, TestApp1 matches.
|
||||
*
|
||||
* PSR4Namespace $b;
|
||||
* $b->namespace = "TestApp1\\Multi";
|
||||
* $b->countMatchingNamespaceSegments("TestApp1\\Multi") -> 2, TestApp1\\Multi matches
|
||||
*
|
||||
* PSR4Namespace $c;
|
||||
* $c->namespace = "HaydenPierce\\Foo\\Bar";
|
||||
* $c->countMatchingNamespaceSegments("TestApp1\\Multi") -> 0, No matches.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return int
|
||||
*/
|
||||
public function countMatchingNamespaceSegments($namespace)
|
||||
{
|
||||
$namespaceFragments = explode('\\', $namespace);
|
||||
$undefinedNamespaceFragments = array();
|
||||
|
||||
while($namespaceFragments) {
|
||||
$possibleNamespace = implode('\\', $namespaceFragments) . '\\';
|
||||
|
||||
if(strpos($this->namespace, $possibleNamespace) !== false){
|
||||
return count(explode('\\', $possibleNamespace)) - 1;
|
||||
}
|
||||
|
||||
array_unshift($undefinedNamespaceFragments, array_pop($namespaceFragments));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function isAcceptableNamespace($namespace)
|
||||
{
|
||||
$namespaceSegments = count(explode('\\', $this->namespace)) - 1;
|
||||
$matchingSegments = $this->countMatchingNamespaceSegments($namespace);
|
||||
return $namespaceSegments === $matchingSegments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function isAcceptableNamespaceRecursiveMode($namespace)
|
||||
{
|
||||
// Remove prefix backslash (TODO: review if we do this eariler).
|
||||
$namespace = ltrim($namespace, '\\');
|
||||
|
||||
return strpos($this->namespace, $namespace) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to identify subnamespaces.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function findDirectories()
|
||||
{
|
||||
$self = $this;
|
||||
$directories = array_reduce($this->directories, function($carry, $directory) use ($self){
|
||||
$path = $self->normalizePath($directory, '');
|
||||
$realDirectory = realpath($path);
|
||||
if ($realDirectory !== false) {
|
||||
return array_merge($carry, array($realDirectory));
|
||||
} else {
|
||||
return $carry;
|
||||
}
|
||||
}, array());
|
||||
|
||||
$arraysOfClasses = array_map(function($directory) use ($self) {
|
||||
$files = scandir($directory);
|
||||
return array_map(function($file) use ($directory, $self) {
|
||||
return $self->normalizePath($directory, $file);
|
||||
}, $files);
|
||||
}, $directories);
|
||||
|
||||
$potentialDirectories = array_reduce($arraysOfClasses, function($carry, $arrayOfClasses) {
|
||||
return array_merge($carry, $arrayOfClasses);
|
||||
}, array());
|
||||
|
||||
// Remove '.' and '..' directories
|
||||
$potentialDirectories = array_filter($potentialDirectories, function($potentialDirectory) {
|
||||
$segments = explode('/', $potentialDirectory);
|
||||
$lastSegment = array_pop($segments);
|
||||
|
||||
return $lastSegment !== '.' && $lastSegment !== '..';
|
||||
});
|
||||
|
||||
$confirmedDirectories = array_filter($potentialDirectories, function($potentialDirectory) {
|
||||
return is_dir($potentialDirectory);
|
||||
});
|
||||
|
||||
return $confirmedDirectories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @param int $options
|
||||
* @return string[]
|
||||
*/
|
||||
public function findClasses($namespace, $options = ClassFinder::STANDARD_MODE)
|
||||
{
|
||||
$relativePath = substr($namespace, strlen($this->namespace));
|
||||
|
||||
$self = $this;
|
||||
$directories = array_reduce($this->directories, function($carry, $directory) use ($relativePath, $namespace, $self){
|
||||
$path = $self->normalizePath($directory, $relativePath);
|
||||
$realDirectory = realpath($path);
|
||||
if ($realDirectory !== false) {
|
||||
return array_merge($carry, array($realDirectory));
|
||||
} else {
|
||||
return $carry;
|
||||
}
|
||||
}, array());
|
||||
|
||||
$arraysOfClasses = array_map(function($directory) {
|
||||
return scandir($directory);
|
||||
}, $directories);
|
||||
|
||||
$potentialClassFiles = array_reduce($arraysOfClasses, function($carry, $arrayOfClasses) {
|
||||
return array_merge($carry, $arrayOfClasses);
|
||||
}, array());
|
||||
|
||||
$potentialClasses = array_map(function($file) use ($namespace){
|
||||
return $namespace . '\\' . str_replace('.php', '', $file);
|
||||
}, $potentialClassFiles);
|
||||
|
||||
if ($options == ClassFinder::RECURSIVE_MODE) {
|
||||
return $this->getClassesFromListRecursively($namespace);
|
||||
} else {
|
||||
return array_filter($potentialClasses, function($potentialClass) {
|
||||
if (function_exists($potentialClass)) {
|
||||
// For some reason calling class_exists() on a namespace'd function raises a Fatal Error (tested PHP 7.0.8)
|
||||
// Example: DeepCopy\deep_copy
|
||||
return false;
|
||||
} else {
|
||||
return class_exists($potentialClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getDirectClassesOnly()
|
||||
{
|
||||
$self = $this;
|
||||
$directories = array_reduce($this->directories, function($carry, $directory) use ($self){
|
||||
$path = $self->normalizePath($directory, '');
|
||||
$realDirectory = realpath($path);
|
||||
if ($realDirectory !== false) {
|
||||
return array_merge($carry, array($realDirectory));
|
||||
} else {
|
||||
return $carry;
|
||||
}
|
||||
}, array());
|
||||
|
||||
$arraysOfClasses = array_map(function($directory) {
|
||||
return scandir($directory);
|
||||
}, $directories);
|
||||
|
||||
$potentialClassFiles = array_reduce($arraysOfClasses, function($carry, $arrayOfClasses) {
|
||||
return array_merge($carry, $arrayOfClasses);
|
||||
}, array());
|
||||
|
||||
$selfNamespace = $this->namespace; // PHP 5.3 BC
|
||||
$potentialClasses = array_map(function($file) use ($self, $selfNamespace) {
|
||||
return $selfNamespace . str_replace('.php', '', $file);
|
||||
}, $potentialClassFiles);
|
||||
|
||||
return array_filter($potentialClasses, function($potentialClass) {
|
||||
if (function_exists($potentialClass)) {
|
||||
// For some reason calling class_exists() on a namespace'd function raises a Fatal Error (tested PHP 7.0.8)
|
||||
// Example: DeepCopy\deep_copy
|
||||
return false;
|
||||
} else {
|
||||
return class_exists($potentialClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return string[]
|
||||
*/
|
||||
public function getClassesFromListRecursively($namespace)
|
||||
{
|
||||
$initialClasses = strpos( $this->namespace, $namespace) !== false ? $this->getDirectClassesOnly() : array();
|
||||
|
||||
return array_reduce($this->getDirectSubnamespaces(), function($carry, PSR4Namespace $subNamespace) use ($namespace) {
|
||||
return array_merge($carry, $subNamespace->getClassesFromListRecursively($namespace));
|
||||
}, $initialClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join an absolute path and a relative path in a platform agnostic way.
|
||||
*
|
||||
* This method is also extracted so that it can be turned into a vfs:// stream URL for unit testing.
|
||||
*
|
||||
* @param string $directory
|
||||
* @param string $relativePath
|
||||
* @return mixed
|
||||
*/
|
||||
public function normalizePath($directory, $relativePath)
|
||||
{
|
||||
$path = str_replace('\\', '/', $directory . '/' . $relativePath);
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PSR4Namespace[]
|
||||
*/
|
||||
public function getDirectSubnamespaces()
|
||||
{
|
||||
return $this->directSubnamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PSR4Namespace[] $directSubnamespaces
|
||||
*/
|
||||
public function setDirectSubnamespaces($directSubnamespaces)
|
||||
{
|
||||
$this->directSubnamespaces = $directSubnamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return trim($this->namespace, '\\');
|
||||
}
|
||||
}
|
||||
153
vendor/haydenpierce/class-finder/src/PSR4/PSR4NamespaceFactory.php
vendored
Normal file
153
vendor/haydenpierce/class-finder/src/PSR4/PSR4NamespaceFactory.php
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\PSR4;
|
||||
|
||||
use HaydenPierce\ClassFinder\AppConfig;
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
|
||||
class PSR4NamespaceFactory
|
||||
{
|
||||
/** @var AppConfig */
|
||||
private $appConfig;
|
||||
|
||||
public function __construct(AppConfig $appConfig)
|
||||
{
|
||||
$this->appConfig = $appConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPSR4Namespaces()
|
||||
{
|
||||
$namespaces = $this->getUserDefinedPSR4Namespaces();
|
||||
$vendorNamespaces = require($this->appConfig->getAppRoot() . 'vendor/composer/autoload_psr4.php');
|
||||
|
||||
$namespaces = array_merge($vendorNamespaces, $namespaces);
|
||||
|
||||
// There's some wackiness going on here for PHP 5.3 compatibility.
|
||||
$names = array_keys($namespaces);
|
||||
$directories = array_values($namespaces);
|
||||
$self = $this;
|
||||
$namespaces = array_map(function($index) use ($self, $names, $directories) {
|
||||
return $self->createNamespace($names[$index], $directories[$index]);
|
||||
},range(0, count($namespaces) - 1));
|
||||
|
||||
return $namespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getUserDefinedPSR4Namespaces()
|
||||
{
|
||||
$appRoot = $this->appConfig->getAppRoot();
|
||||
|
||||
$composerJsonPath = $appRoot . 'composer.json';
|
||||
$composerConfig = json_decode(file_get_contents($composerJsonPath));
|
||||
|
||||
if (!isset($composerConfig->autoload)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
//Apparently PHP doesn't like hyphens, so we use variable variables instead.
|
||||
$psr4 = "psr-4";
|
||||
return (array)$composerConfig->autoload->$psr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a namespace from composer_psr4.php and composer.json autoload.psr4 items.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param string[] $directories
|
||||
* @return PSR4Namespace
|
||||
* @throws ClassFinderException
|
||||
*/
|
||||
public function createNamespace($namespace, $directories)
|
||||
{
|
||||
if (is_string($directories)) {
|
||||
// This is an acceptable format according to composer.json
|
||||
$directories = array($directories);
|
||||
} elseif (is_array($directories)) {
|
||||
// composer_psr4.php seems to put everything in this format
|
||||
} else {
|
||||
throw new ClassFinderException('Unknown PSR4 definition.');
|
||||
}
|
||||
|
||||
$self = $this;
|
||||
$appConfig = $this->appConfig;
|
||||
$directories = array_map(function($directory) use ($self, $appConfig) {
|
||||
if ($self->isAbsolutePath($directory)) {
|
||||
return $directory;
|
||||
} else {
|
||||
return $appConfig->getAppRoot() . $directory;
|
||||
}
|
||||
}, $directories);
|
||||
|
||||
$directories = array_filter(array_map(function($directory) {
|
||||
return realpath($directory);
|
||||
}, $directories));
|
||||
|
||||
$psr4Namespace = new PSR4Namespace($namespace, $directories);
|
||||
|
||||
$subNamespaces = $this->getSubnamespaces($psr4Namespace);
|
||||
$psr4Namespace->setDirectSubnamespaces($subNamespaces);
|
||||
|
||||
return $psr4Namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PSR4Namespace $psr4Namespace
|
||||
* @return PSR4Namespace[]
|
||||
*/
|
||||
private function getSubnamespaces(PSR4Namespace $psr4Namespace)
|
||||
{
|
||||
// Scan it's own directories.
|
||||
$directories = $psr4Namespace->findDirectories();
|
||||
|
||||
$self = $this;
|
||||
$subnamespaces = array_map(function($directory) use ($self, $psr4Namespace){
|
||||
$segments = explode('/', $directory);
|
||||
$subnamespaceSegment = array_pop($segments);
|
||||
|
||||
$namespace = $psr4Namespace->getNamespace() . "\\" . $subnamespaceSegment . "\\";
|
||||
return $self->createNamespace($namespace, $directory);
|
||||
}, $directories);
|
||||
|
||||
return $subnamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a path is absolute.
|
||||
*
|
||||
* Mostly this answer https://stackoverflow.com/a/38022806/3000068
|
||||
* A few changes: Changed exceptions to be ClassFinderExceptions, removed some ctype dependencies,
|
||||
* updated the root prefix regex to handle Window paths better.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
* @throws ClassFinderException
|
||||
*/
|
||||
public function isAbsolutePath($path) {
|
||||
if (!is_string($path)) {
|
||||
$mess = sprintf('String expected but was given %s', gettype($path));
|
||||
throw new ClassFinderException($mess);
|
||||
}
|
||||
|
||||
// Optional wrapper(s).
|
||||
$regExp = '%^(?<wrappers>(?:[[:print:]]{2,}://)*)';
|
||||
// Optional root prefix.
|
||||
$regExp .= '(?<root>(?:[[:alpha:]]:[/\\\\]|/)?)';
|
||||
// Actual path.
|
||||
$regExp .= '(?<path>(?:[[:print:]]*))$%';
|
||||
$parts = array();
|
||||
if (!preg_match($regExp, $path, $parts)) {
|
||||
$mess = sprintf('Path is NOT valid, was given %s', $path);
|
||||
throw new ClassFinderException($mess);
|
||||
}
|
||||
if ('' !== $parts['root']) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
5
vendor/laragraph/utils/.styleci.yml
vendored
Normal file
5
vendor/laragraph/utils/.styleci.yml
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
preset: laravel
|
||||
risky: true
|
||||
enabled:
|
||||
- declare_strict_types
|
||||
- unalign_double_arrow
|
||||
13
vendor/laragraph/utils/CHANGELOG.md
vendored
Normal file
13
vendor/laragraph/utils/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 1.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- Add `RequestParser` to convert an incoming HTTP request to one or more `OperationParams`
|
||||
16
vendor/laragraph/utils/LICENSE
vendored
Normal file
16
vendor/laragraph/utils/LICENSE
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Benedikt Franke
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
39
vendor/laragraph/utils/README.md
vendored
Normal file
39
vendor/laragraph/utils/README.md
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
# laragraph/utils
|
||||
|
||||
[](https://github.com/laragraph/utils/actions)
|
||||
[](https://codecov.io/gh/laragraph/utils)
|
||||
[](https://github.styleci.io/repos/228471198)
|
||||
|
||||
[](https://packagist.org/packages/laragraph/utils)
|
||||
[](https://packagist.org/packages/laragraph/utils)
|
||||
|
||||
Utilities for using GraphQL with Laravel
|
||||
|
||||
## Installation
|
||||
|
||||
Install through composer
|
||||
|
||||
```bash
|
||||
composer require laragraph/utils
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
This package holds basic utilities that are useful for building a GraphQL server with Laravel.
|
||||
If you want to build an application, we recommend using a full framework that integrates the
|
||||
primitives within this package:
|
||||
|
||||
- SDL-first: [Lighthouse](https://github.com/nuwave/lighthouse)
|
||||
- Code-first: [graphql-laravel](https://github.com/rebing/graphql-laravel)
|
||||
|
||||
## Changelog
|
||||
|
||||
See [`CHANGELOG.md`](CHANGELOG.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
See [`CONTRIBUTING.md`](.github/CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
This package is licensed using the MIT License.
|
||||
55
vendor/laragraph/utils/composer.json
vendored
Normal file
55
vendor/laragraph/utils/composer.json
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "laragraph/utils",
|
||||
"type": "library",
|
||||
"description": "Utilities for using GraphQL with Laravel",
|
||||
"homepage": "https://github.com/laragraph/utils",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Benedikt Franke",
|
||||
"email": "benedikt@franke.tech"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0",
|
||||
"illuminate/contracts": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"illuminate/http": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"thecodingmachine/safe": "^1.1",
|
||||
"webonyx/graphql-php": "^0.13.2 || ^14"
|
||||
},
|
||||
"require-dev": {
|
||||
"ergebnis/composer-normalize": "^2.11",
|
||||
"infection/infection": "~0.20",
|
||||
"jangregor/phpstan-prophecy": "^0.8.1",
|
||||
"orchestra/testbench": "3.6.* || 3.7.* || 3.8.* || 3.9.* || ^4 || ^5 || ^6",
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "^0.12.57",
|
||||
"phpstan/phpstan-deprecation-rules": "^0.12.5",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.5",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5",
|
||||
"thecodingmachine/phpstan-safe-rule": "^1.0"
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laragraph\\Utils\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Laragraph\\Utils\\Tests\\": "tests/"
|
||||
},
|
||||
"files": [
|
||||
"vendor/symfony/var-dumper/Resources/functions/dump.php"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"support": {
|
||||
"issues": "https://github.com/laragraph/utils/issues",
|
||||
"source": "https://github.com/laragraph/utils"
|
||||
}
|
||||
}
|
||||
118
vendor/laragraph/utils/src/RequestParser.php
vendored
Normal file
118
vendor/laragraph/utils/src/RequestParser.php
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laragraph\Utils;
|
||||
|
||||
use GraphQL\Server\Helper;
|
||||
use GraphQL\Server\OperationParams;
|
||||
use GraphQL\Server\RequestError;
|
||||
use GraphQL\Utils\Utils;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class RequestParser
|
||||
{
|
||||
/**
|
||||
* @var \GraphQL\Server\Helper
|
||||
*/
|
||||
protected $helper;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->helper = new Helper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an incoming HTTP request to one or more OperationParams.
|
||||
*
|
||||
* @return \GraphQL\Server\OperationParams|array<int, \GraphQL\Server\OperationParams>
|
||||
*
|
||||
* @throws \GraphQL\Server\RequestError
|
||||
*/
|
||||
public function parseRequest(Request $request)
|
||||
{
|
||||
$method = $request->getMethod();
|
||||
$bodyParams = [];
|
||||
/** @var array<string, mixed> $queryParams */
|
||||
$queryParams = $request->query();
|
||||
|
||||
if ($method === 'POST') {
|
||||
/**
|
||||
* Never null, since Symfony defaults to application/x-www-form-urlencoded.
|
||||
*
|
||||
* @var string $contentType
|
||||
*/
|
||||
$contentType = $request->header('Content-Type');
|
||||
|
||||
if (stripos($contentType, 'application/json') !== false) {
|
||||
/** @var string $content */
|
||||
$content = $request->getContent();
|
||||
$bodyParams = \Safe\json_decode($content, true);
|
||||
|
||||
if (! is_array($bodyParams)) {
|
||||
throw new RequestError(
|
||||
'GraphQL Server expects JSON object or array, but got '.
|
||||
Utils::printSafeJson($bodyParams)
|
||||
);
|
||||
}
|
||||
} elseif (stripos($contentType, 'application/graphql') !== false) {
|
||||
/** @var string $content */
|
||||
$content = $request->getContent();
|
||||
$bodyParams = ['query' => $content];
|
||||
} elseif (stripos($contentType, 'application/x-www-form-urlencoded') !== false) {
|
||||
/** @var array<string, mixed> $bodyParams */
|
||||
$bodyParams = $request->post();
|
||||
} elseif (stripos($contentType, 'multipart/form-data') !== false) {
|
||||
$bodyParams = $this->inlineFiles($request);
|
||||
} else {
|
||||
throw new RequestError('Unexpected content type: '.Utils::printSafeJson($contentType));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->helper->parseRequestParams($method, $bodyParams, $queryParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline file uploads given through a multipart request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array<mixed>
|
||||
*/
|
||||
protected function inlineFiles(Request $request): array
|
||||
{
|
||||
/** @var string|null $mapParam */
|
||||
$mapParam = $request->post('map');
|
||||
if ($mapParam === null) {
|
||||
throw new RequestError(
|
||||
'Could not find a valid map, be sure to conform to GraphQL multipart request specification: https://github.com/jaydenseric/graphql-multipart-request-spec'
|
||||
);
|
||||
}
|
||||
|
||||
/** @var string|null $operationsParam */
|
||||
$operationsParam = $request->post('operations');
|
||||
if ($operationsParam === null) {
|
||||
throw new RequestError(
|
||||
'Could not find valid operations, be sure to conform to GraphQL multipart request specification: https://github.com/jaydenseric/graphql-multipart-request-spec'
|
||||
);
|
||||
}
|
||||
|
||||
/** @var array<string, mixed>|array<int, array<string, mixed>> $operations */
|
||||
$operations = \Safe\json_decode($operationsParam, true);
|
||||
|
||||
/** @var array<int|string, array<int, string>> $map */
|
||||
$map = \Safe\json_decode($mapParam, true);
|
||||
|
||||
foreach ($map as $fileKey => $operationsPaths) {
|
||||
/** @var array<string> $operationsPaths */
|
||||
$file = $request->file((string) $fileKey);
|
||||
|
||||
/** @var string $operationsPath */
|
||||
foreach ($operationsPaths as $operationsPath) {
|
||||
Arr::set($operations, $operationsPath, $file);
|
||||
}
|
||||
}
|
||||
|
||||
return $operations;
|
||||
}
|
||||
}
|
||||
253
vendor/laragraph/utils/tests/Unit/RequestParserTest.php
vendored
Normal file
253
vendor/laragraph/utils/tests/Unit/RequestParserTest.php
vendored
Normal file
@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laragraph\Utils\Tests\Unit;
|
||||
|
||||
use GraphQL\Server\RequestError;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Laragraph\Utils\RequestParser;
|
||||
use Orchestra\Testbench\TestCase;
|
||||
use Safe\Exceptions\JsonException;
|
||||
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
|
||||
|
||||
class RequestParserTest extends TestCase
|
||||
{
|
||||
public function testGetWithQuery(): void
|
||||
{
|
||||
$query = /** @lang GraphQL */ '{ foo }';
|
||||
$request = $this->makeRequest('GET', ['query' => $query]);
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame($query, $params->query);
|
||||
}
|
||||
|
||||
public function testPostWithJson(): void
|
||||
{
|
||||
$query = /** @lang GraphQL */ '{ foo }';
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[],
|
||||
[],
|
||||
['Content-Type' => 'application/json'],
|
||||
\Safe\json_encode(['query' => $query])
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame($query, $params->query);
|
||||
}
|
||||
|
||||
public function testPostWithQueryApplicationGraphQL(): void
|
||||
{
|
||||
$query = /** @lang GraphQL */ '{ foo }';
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[],
|
||||
[],
|
||||
['Content-Type' => 'application/graphql'],
|
||||
$query
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame($query, $params->query);
|
||||
}
|
||||
|
||||
public function testPostWithRegularForm(): void
|
||||
{
|
||||
$query = /** @lang GraphQL */ '{ foo }';
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
['query' => $query],
|
||||
[],
|
||||
['Content-Type' => 'application/x-www-form-urlencoded']
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame($query, $params->query);
|
||||
}
|
||||
|
||||
public function testPostDefaultsToRegularForm(): void
|
||||
{
|
||||
$query = /** @lang GraphQL */ '{ foo }';
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
['query' => $query]
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame($query, $params->query);
|
||||
}
|
||||
|
||||
public function testNonSensicalContentType(): void
|
||||
{
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[],
|
||||
[],
|
||||
['Content-Type' => 'foobar']
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
$this->expectException(RequestError::class);
|
||||
$parser->parseRequest($request);
|
||||
}
|
||||
|
||||
public function testNoQuery(): void
|
||||
{
|
||||
$request = $this->makeRequest('GET');
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame(null, $params->query);
|
||||
}
|
||||
|
||||
public function testInvalidJson(): void
|
||||
{
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[],
|
||||
[],
|
||||
['Content-Type' => 'application/json'],
|
||||
'this is not valid json'
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
$this->expectException(JsonException::class);
|
||||
$parser->parseRequest($request);
|
||||
}
|
||||
|
||||
public function testNonArrayJson(): void
|
||||
{
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[],
|
||||
[],
|
||||
['Content-Type' => 'application/json'],
|
||||
'"this should be a map with query, variables, etc."'
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
$this->expectException(RequestError::class);
|
||||
$parser->parseRequest($request);
|
||||
}
|
||||
|
||||
public function testMultipartFormRequest(): void
|
||||
{
|
||||
$file = UploadedFile::fake()->create('image.jpg', 500);
|
||||
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[
|
||||
'operations' => /** @lang JSON */ '
|
||||
{
|
||||
"query": "mutation Upload($file: Upload!) { upload(file: $file) }",
|
||||
"variables": {
|
||||
"file": null
|
||||
}
|
||||
}
|
||||
',
|
||||
'map' => /** @lang JSON */ '
|
||||
{
|
||||
"0": ["variables.file"]
|
||||
}
|
||||
',
|
||||
],
|
||||
[
|
||||
'0' => $file,
|
||||
],
|
||||
[
|
||||
'Content-Type' => 'multipart/form-data',
|
||||
]
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame('mutation Upload($file: Upload!) { upload(file: $file) }', $params->query);
|
||||
|
||||
$variables = $params->variables;
|
||||
self::assertNotNull($variables);
|
||||
/** @var array<string, mixed> $variables */
|
||||
self::assertSame($file, $variables['file']);
|
||||
}
|
||||
|
||||
public function testMultipartFormWithoutMap(): void
|
||||
{
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[],
|
||||
[],
|
||||
[
|
||||
'Content-Type' => 'multipart/form-data',
|
||||
]
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
$this->expectException(RequestError::class);
|
||||
$parser->parseRequest($request);
|
||||
}
|
||||
|
||||
public function testMultipartFormWithoutOperations(): void
|
||||
{
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[
|
||||
'map' => /** @lang JSON */ '
|
||||
{
|
||||
"0": ["variables.file"]
|
||||
}
|
||||
',
|
||||
],
|
||||
[],
|
||||
[
|
||||
'Content-Type' => 'multipart/form-data',
|
||||
]
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
$this->expectException(RequestError::class);
|
||||
$parser->parseRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param array<mixed> $parameters
|
||||
* @param array<mixed> $files
|
||||
* @param array<mixed> $headers
|
||||
* @param string|resource|null $content
|
||||
* @return \Illuminate\Http\Request
|
||||
*/
|
||||
public function makeRequest(string $method, array $parameters = [], array $files = [], array $headers = [], $content = null): Request
|
||||
{
|
||||
$symfonyRequest = SymfonyRequest::create(
|
||||
'http://foo.bar/graphql',
|
||||
$method,
|
||||
$parameters,
|
||||
[],
|
||||
$files,
|
||||
$this->transformHeadersToServerVars($headers),
|
||||
$content
|
||||
);
|
||||
|
||||
return Request::createFromBase($symfonyRequest);
|
||||
}
|
||||
}
|
||||
71
vendor/nuwave/lighthouse/README.md
vendored
71
vendor/nuwave/lighthouse/README.md
vendored
@ -1,6 +1,6 @@
|
||||
<div align="center">
|
||||
<a href="https://www.lighthouse-php.com">
|
||||
<img src="logo.png" alt=lighthouse-logo" width="150" height="150">
|
||||
<img src="./logo.png" alt=lighthouse-logo" width="150" height="150">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -8,50 +8,59 @@
|
||||
|
||||
# Lighthouse
|
||||
|
||||
[](https://travis-ci.org/nuwave/lighthouse)
|
||||
[](https://codecov.io/gh/nuwave/lighthouse)
|
||||
[](https://github.com/nuwave/lighthouse/actions)
|
||||
[](https://codecov.io/gh/nuwave/lighthouse)
|
||||
[](https://github.com/phpstan/phpstan)
|
||||
[](https://github.styleci.io/repos/59965104)
|
||||
[](https://packagist.org/packages/nuwave/lighthouse)
|
||||
[](https://github.com/nuwave/lighthouse/blob/master/LICENSE)
|
||||
[](https://join.slack.com/t/lighthouse-php/shared_invite/enQtMzc1NzQwNTUxMjk3LWI1ZDQ1YWM1NmM2MmQ0NTU0NGNjZWFkMTJhY2VjMDAwZmMyZDFlZTc1Mjc3ZGY0MWM1Y2Q5MWNjYmJmYWJkYmU)
|
||||
[](https://github.styleci.io/repos/59965104)
|
||||
|
||||
[](https://packagist.org/packages/nuwave/lighthouse)
|
||||
[](https://packagist.org/packages/nuwave/lighthouse)
|
||||
[](https://github.com/nuwave/lighthouse/blob/master/LICENSE)
|
||||
|
||||
[](https://stackoverflow.com/questions/tagged/laravel-lighthouse)
|
||||
[](https://join.slack.com/t/lighthouse-php/shared_invite/zt-4sm280w1-wu21r94f3kLRRtBXRbXVfw)
|
||||
|
||||
**A framework for serving GraphQL from Laravel**
|
||||
|
||||
**GraphQL Server for Laravel**
|
||||
</div>
|
||||
|
||||
Lighthouse is a PHP package that allows you to serve a GraphQL endpoint from your
|
||||
Laravel application. It greatly reduces the boilerplate required to create a schema,
|
||||
it integrates well with any Laravel project, and it's highly customizable
|
||||
giving you full control over your data.
|
||||
Lighthouse is a GraphQL framework that integrates with your Laravel application.
|
||||
It takes the best ideas of both and combines them to solve common tasks with ease
|
||||
and offer flexibility when you need it.
|
||||
|
||||
## [Documentation](https://lighthouse-php.com/)
|
||||
## Documentation
|
||||
|
||||
The documentation lives at [lighthouse-php.com](https://lighthouse-php.com/).
|
||||
|
||||
If you like reading plain markdown, you can also find the source files in the [docs folder](/docs).
|
||||
The site includes the latest docs for each major version of Lighthouse.
|
||||
You can find docs for specific versions by looking at the contents of [/docs/master](/docs/master)
|
||||
at that point in the git history: `https://github.com/nuwave/lighthouse/tree/<SPECIFIC-TAG>/docs/master`.
|
||||
|
||||
## Get started
|
||||
|
||||
If you have an existing Laravel project, all you really need
|
||||
to get up and running is a few steps:
|
||||
|
||||
1. Install via `composer require nuwave/lighthouse`
|
||||
2. Publish the default schema `php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=schema`
|
||||
3. Use something like [GraphQL Playground](https://github.com/mll-lab/laravel-graphql-playground) to explore your GraphQL endpoint
|
||||
|
||||
Check out [the docs](https://lighthouse-php.com/) to learn more.
|
||||
A chinese translation is available at [lighthouse-php.cn](http://lighthouse-php.cn/) and is maintained
|
||||
over at https://github.com/haxibiao/lighthouse.
|
||||
|
||||
## Get involved
|
||||
|
||||
We welcome contributions of any kind.
|
||||
|
||||
- Have a question? [Use the laravel-lighthouse tag on Stackoverflow](https://stackoverflow.com/questions/tagged/laravel-lighthouse)
|
||||
- Talk to other users? [Hop into Slack](https://join.slack.com/t/lighthouse-php/shared_invite/enQtMzc1NzQwNTUxMjk3LWI1ZDQ1YWM1NmM2MmQ0NTU0NGNjZWFkMTJhY2VjMDAwZmMyZDFlZTc1Mjc3ZGY0MWM1Y2Q5MWNjYmJmYWJkYmU)
|
||||
- Have a question? [Use the laravel-lighthouse tag on Stack Overflow](https://stackoverflow.com/questions/tagged/laravel-lighthouse)
|
||||
- Talk to other users? [Hop into Slack](https://join.slack.com/t/lighthouse-php/shared_invite/zt-4sm280w1-wu21r94f3kLRRtBXRbXVfw)
|
||||
- Found a bug? [Report a bug](https://github.com/nuwave/lighthouse/issues/new?template=bug_report.md)
|
||||
- Need a feature? [Open a feature request](https://github.com/nuwave/lighthouse/issues/new?template=feature_request.md)
|
||||
- Want to improve Lighthouse? [Read our contribution guidelines](https://github.com/nuwave/lighthouse/blob/master/.github/CONTRIBUTING.md)
|
||||
- Have an idea? [Propose a feature](https://github.com/nuwave/lighthouse/issues/new?template=feature_proposal.md)
|
||||
- Want to improve Lighthouse? [Read our contribution guidelines](https://github.com/nuwave/lighthouse/blob/master/CONTRIBUTING.md)
|
||||
|
||||
## Changelog
|
||||
|
||||
All notable changes to this project are documented in [`CHANGELOG.md`](CHANGELOG.md).
|
||||
|
||||
## Upgrade Guide
|
||||
|
||||
When upgrading between major versions of Lighthouse, consider [`UPGRADE.md`](UPGRADE.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions of any kind, see how in [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Lighthouse,
|
||||
please email Benedikt Franke via [benedikt@franke.tech](mailto:benedikt@franke.tech).
|
||||
please email Benedikt Franke via [benedikt@franke.tech](mailto:benedikt@franke.tech)
|
||||
or visit https://tidelift.com/security.
|
||||
|
||||
122
vendor/nuwave/lighthouse/_ide_helper.php
vendored
Normal file
122
vendor/nuwave/lighthouse/_ide_helper.php
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Foundation\Testing {
|
||||
class TestResponse
|
||||
{
|
||||
/**
|
||||
* Asserts that the response contains an error from a given category.
|
||||
*
|
||||
* @param string $category The name of the expected error category.
|
||||
* @return $this
|
||||
*/
|
||||
public function assertGraphQLErrorCategory(string $category): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the returned result contains exactly the given validation keys.
|
||||
*
|
||||
* @param array $keys The validation keys the result should have.
|
||||
* @return $this
|
||||
*/
|
||||
public function assertGraphQLValidationKeys(array $keys): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a given validation error is present in the response.
|
||||
*
|
||||
* @param string $key The validation key that should be present.
|
||||
* @param string $message The expected validation message.
|
||||
* @return $this
|
||||
*/
|
||||
public function assertGraphQLValidationError(string $key, string $message): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that no validation errors are present in the response.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function assertGraphQLValidationPasses(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Illuminate\Testing {
|
||||
class TestResponse
|
||||
{
|
||||
/**
|
||||
* Assert the response contains an error with the given message.
|
||||
*
|
||||
* @param string $message The expected error message.
|
||||
* @return $this
|
||||
*/
|
||||
public function assertGraphQLErrorMessage(string $message): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the response contains an error from the given category.
|
||||
*
|
||||
* @param string $category The name of the expected error category.
|
||||
* @return $this
|
||||
*/
|
||||
public function assertGraphQLErrorCategory(string $category): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the returned result contains exactly the given validation keys.
|
||||
*
|
||||
* @param array $keys The validation keys the result should have.
|
||||
* @return $this
|
||||
*/
|
||||
public function assertGraphQLValidationKeys(array $keys): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert a given validation error is present in the response.
|
||||
*
|
||||
* @param string $key The validation key that should be present.
|
||||
* @param string $message The expected validation message.
|
||||
* @return $this
|
||||
*/
|
||||
public function assertGraphQLValidationError(string $key, string $message): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert no validation errors are present in the response.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function assertGraphQLValidationPasses(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace GraphQL\Type\Definition {
|
||||
class ResolveInfo
|
||||
{
|
||||
/**
|
||||
* We monkey patch this onto here to pass it down the resolver chain.
|
||||
*
|
||||
* @var \Nuwave\Lighthouse\Execution\Arguments\ArgumentSet
|
||||
*/
|
||||
public $argumentSet;
|
||||
}
|
||||
}
|
||||
111
vendor/nuwave/lighthouse/composer.json
vendored
111
vendor/nuwave/lighthouse/composer.json
vendored
@ -1,15 +1,14 @@
|
||||
{
|
||||
"name": "nuwave/lighthouse",
|
||||
"description": "Lighthouse is a schema first GraphQL package for Laravel applications.",
|
||||
"type": "library",
|
||||
"description": "A framework for serving GraphQL from Laravel",
|
||||
"keywords": [
|
||||
"api",
|
||||
"graphql",
|
||||
"laravel",
|
||||
"laravel-graphql"
|
||||
],
|
||||
"license": "MIT",
|
||||
"homepage": "https://lighthouse-php.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christopher Moore",
|
||||
@ -22,38 +21,71 @@
|
||||
"homepage": "https://franke.tech"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nuwave/lighthouse/issues",
|
||||
"source": "https://github.com/nuwave/lighthouse"
|
||||
},
|
||||
"require": {
|
||||
"php": ">= 7.1",
|
||||
"php": ">= 7.2",
|
||||
"ext-json": "*",
|
||||
"illuminate/contracts": "5.5.*|5.6.*|5.7.*|5.8.*|^6.0",
|
||||
"illuminate/http": "5.5.*|5.6.*|5.7.*|5.8.*|^6.0",
|
||||
"illuminate/pagination": "5.5.*|5.6.*|5.7.*|5.8.*|^6.0",
|
||||
"illuminate/routing": "5.5.*|5.6.*|5.7.*|5.8.*|^6.0",
|
||||
"illuminate/support": "5.5.*|5.6.*|5.7.*|5.8.*|^6.0",
|
||||
"illuminate/validation": "5.5.*|5.6.*|5.7.*|5.8.*|^6.0",
|
||||
"webonyx/graphql-php": "^0.13.2"
|
||||
"haydenpierce/class-finder": "^0.4",
|
||||
"illuminate/auth": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"illuminate/bus": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"illuminate/contracts": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"illuminate/http": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"illuminate/pagination": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"illuminate/queue": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"illuminate/routing": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"illuminate/support": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"illuminate/validation": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"laragraph/utils": "^1",
|
||||
"thecodingmachine/safe": "^1",
|
||||
"webonyx/graphql-php": "^14.7"
|
||||
},
|
||||
"require-dev": {
|
||||
"bensampo/laravel-enum": "^1.22",
|
||||
"laravel/lumen-framework": "5.5.*|5.6.*|5.7.*|5.8.*|^6.0",
|
||||
"laravel/scout": "^4.0",
|
||||
"mll-lab/graphql-php-scalars": "^2.1",
|
||||
"mockery/mockery": "^1.0",
|
||||
"orchestra/database": "3.5.*|3.6.*|3.7.*|3.8.*|3.9.*",
|
||||
"orchestra/testbench": "3.5.*|3.6.*|3.7.*|3.8.*|3.9.*",
|
||||
"phpbench/phpbench": "@dev",
|
||||
"pusher/pusher-php-server": "^3.2",
|
||||
"haydenpierce/class-finder": "^0.3.3"
|
||||
"bensampo/laravel-enum": "^1.28.3 || ^2 || ^3",
|
||||
"ergebnis/composer-normalize": "^2.2.2",
|
||||
"finwe/phpstan-faker": "^0.1.0",
|
||||
"laravel/framework": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"laravel/legacy-factories": "^1",
|
||||
"laravel/lumen-framework": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"laravel/scout": "^7 || ^8",
|
||||
"mll-lab/graphql-php-scalars": "^4",
|
||||
"mockery/mockery": "^1",
|
||||
"nunomaduro/larastan": "^0.6 || ^0.7",
|
||||
"orchestra/testbench": "3.6.* || 3.7.* || 3.8.* || 3.9.* || ^4 || ^5 || ^6",
|
||||
"phpbench/phpbench": "1.0.0-alpha4",
|
||||
"phpstan/phpstan": "0.12.89",
|
||||
"phpstan/phpstan-mockery": "^0.12.5",
|
||||
"phpstan/phpstan-phpunit": "^0.12.17",
|
||||
"phpunit/phpunit": "^7.5 || ^8.4 || ^9",
|
||||
"predis/predis": "^1.1",
|
||||
"pusher/pusher-php-server": "^4 || ^5",
|
||||
"rector/rector": "^0.9",
|
||||
"thecodingmachine/phpstan-safe-rule": "^1",
|
||||
"vimeo/psalm": "^4.7"
|
||||
},
|
||||
"suggest": {
|
||||
"bensampo/laravel-enum": "Convenient enum definitions that can easily be registered in your Schema",
|
||||
"laravel/scout": "Required for the @search directive",
|
||||
"mll-lab/graphql-php-scalars": "Useful scalar types, required for @whereConstraints",
|
||||
"mll-lab/graphql-php-scalars": "Useful scalar types, required for @whereConditions",
|
||||
"mll-lab/laravel-graphql-playground": "GraphQL IDE for better development workflow - integrated with Laravel",
|
||||
"bensampo/laravel-enum": "Convenient enum definitions that can easily be registered in your Schema"
|
||||
"pusher/pusher-php-server": "Required when using the Pusher Subscriptions driver"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"aliases": {
|
||||
"graphql": "Nuwave\\Lighthouse\\GraphQL"
|
||||
},
|
||||
"providers": [
|
||||
"Nuwave\\Lighthouse\\LighthouseServiceProvider",
|
||||
"Nuwave\\Lighthouse\\GlobalId\\GlobalIdServiceProvider",
|
||||
"Nuwave\\Lighthouse\\OrderBy\\OrderByServiceProvider",
|
||||
"Nuwave\\Lighthouse\\Pagination\\PaginationServiceProvider",
|
||||
"Nuwave\\Lighthouse\\Scout\\ScoutServiceProvider",
|
||||
"Nuwave\\Lighthouse\\SoftDeletes\\SoftDeletesServiceProvider",
|
||||
"Nuwave\\Lighthouse\\Validation\\ValidationServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -67,24 +99,15 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit --colors=always",
|
||||
"test:unit": "phpunit --colors=always --testsuite Unit",
|
||||
"test:integration": "phpunit --colors=always --testsuite Integration",
|
||||
"bench": "phpbench run",
|
||||
"rector": "rector process -v src/ tests/",
|
||||
"stan": "phpstan analyse --memory-limit 2048M",
|
||||
"bench": "phpbench run"
|
||||
"test": "phpunit --colors=always",
|
||||
"test:integration": "phpunit --colors=always --testsuite Integration",
|
||||
"test:unit": "phpunit --colors=always --testsuite Unit"
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Nuwave\\Lighthouse\\LighthouseServiceProvider",
|
||||
"Nuwave\\Lighthouse\\SoftDeletes\\SoftDeletesServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"graphql": "Nuwave\\Lighthouse\\GraphQL"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
"support": {
|
||||
"issues": "https://github.com/nuwave/lighthouse/issues",
|
||||
"source": "https://github.com/nuwave/lighthouse"
|
||||
}
|
||||
}
|
||||
|
||||
240
vendor/nuwave/lighthouse/config/config.php
vendored
240
vendor/nuwave/lighthouse/config/config.php
vendored
@ -1,240 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Route Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Controls the HTTP route that your GraphQL server responds to.
|
||||
| You may set `route` => false, to disable the default route
|
||||
| registration and take full control.
|
||||
|
|
||||
*/
|
||||
|
||||
'route' => [
|
||||
/*
|
||||
* The URI the endpoint responds to, e.g. mydomain.com/graphql.
|
||||
*/
|
||||
'uri' => 'graphql',
|
||||
|
||||
/*
|
||||
* Lighthouse creates a named route for convenient URL generation and redirects.
|
||||
*/
|
||||
'name' => 'graphql',
|
||||
|
||||
/*
|
||||
*
|
||||
* Beware that middleware defined here runs before the GraphQL execution phase,
|
||||
* so you have to take extra care to return spec-compliant error responses.
|
||||
* To apply middleware on a field level, use the @middleware directive.
|
||||
*/
|
||||
'middleware' => [
|
||||
\Nuwave\Lighthouse\Support\Http\Middleware\AcceptJson::class,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Schema Declaration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is a path that points to where your GraphQL schema is located
|
||||
| relative to the app path. You should define your entire GraphQL
|
||||
| schema in this file (additional files may be imported).
|
||||
|
|
||||
*/
|
||||
|
||||
'schema' => [
|
||||
'register' => base_path('graphql/schema.graphql'),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Schema Cache
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| A large part of schema generation consists of parsing and AST manipulation.
|
||||
| This operation is very expensive, so it is highly recommended to enable
|
||||
| caching of the final schema to optimize performance of large schemas.
|
||||
|
|
||||
*/
|
||||
|
||||
'cache' => [
|
||||
'enable' => env('LIGHTHOUSE_CACHE_ENABLE', true),
|
||||
'key' => env('LIGHTHOUSE_CACHE_KEY', 'lighthouse-schema'),
|
||||
'ttl' => env('LIGHTHOUSE_CACHE_TTL', null),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Namespaces
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These are the default namespaces where Lighthouse looks for classes
|
||||
| that extend functionality of the schema. You may pass either a string
|
||||
| or an array, they are tried in order and the first match is used.
|
||||
|
|
||||
*/
|
||||
|
||||
'namespaces' => [
|
||||
'models' => ['App', 'App\\Models'],
|
||||
'queries' => 'App\\GraphQL\\Queries',
|
||||
'mutations' => 'App\\GraphQL\\Mutations',
|
||||
'subscriptions' => 'App\\GraphQL\\Subscriptions',
|
||||
'interfaces' => 'App\\GraphQL\\Interfaces',
|
||||
'unions' => 'App\\GraphQL\\Unions',
|
||||
'scalars' => 'App\\GraphQL\\Scalars',
|
||||
'directives' => ['App\\GraphQL\\Directives'],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Security
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Control how Lighthouse handles security related query validation.
|
||||
| This configures the options from http://webonyx.github.io/graphql-php/security/
|
||||
|
|
||||
*/
|
||||
|
||||
'security' => [
|
||||
'max_query_complexity' => \GraphQL\Validator\Rules\QueryComplexity::DISABLED,
|
||||
'max_query_depth' => \GraphQL\Validator\Rules\QueryDepth::DISABLED,
|
||||
'disable_introspection' => \GraphQL\Validator\Rules\DisableIntrospection::DISABLED,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pagination
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Limits the maximum "count" that users may pass as an argument
|
||||
| to fields that are paginated with the @paginate directive.
|
||||
| A setting of "null" means the count is unrestricted.
|
||||
|
|
||||
*/
|
||||
|
||||
'paginate_max_count' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pagination Amount Argument
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Set the name to use for the generated argument on paginated fields
|
||||
| that controls how many results are returned.
|
||||
| This setting will be removed in v5.
|
||||
|
|
||||
*/
|
||||
|
||||
'pagination_amount_argument' => 'first',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Debug
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Control the debug level as described in http://webonyx.github.io/graphql-php/error-handling/
|
||||
| Debugging is only applied if the global Laravel debug config is set to true.
|
||||
|
|
||||
*/
|
||||
|
||||
'debug' => \GraphQL\Error\Debug::INCLUDE_DEBUG_MESSAGE | \GraphQL\Error\Debug::INCLUDE_TRACE,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Error Handlers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Register error handlers that receive the Errors that occur during execution
|
||||
| and handle them. You may use this to log, filter or format the errors.
|
||||
| The classes must implement \Nuwave\Lighthouse\Execution\ErrorHandler
|
||||
|
|
||||
*/
|
||||
|
||||
'error_handlers' => [
|
||||
\Nuwave\Lighthouse\Execution\ExtensionErrorHandler::class,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global ID
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The name that is used for the global id field on the Node interface.
|
||||
| When creating a Relay compliant server, this must be named "id".
|
||||
|
|
||||
*/
|
||||
|
||||
'global_id_field' => 'id',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Batched Queries
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| GraphQL query batching means sending multiple queries to the server in one request,
|
||||
| You may set this flag to either process or deny batched queries.
|
||||
|
|
||||
*/
|
||||
|
||||
'batched_queries' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Transactional Mutations
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Sets default setting for transactional mutations.
|
||||
| You may set this flag to have @create|@update mutations transactional or not.
|
||||
|
|
||||
*/
|
||||
|
||||
'transactional_mutations' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| GraphQL Subscriptions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you can define GraphQL subscription "broadcasters" and "storage" drivers
|
||||
| as well their required configuration options.
|
||||
|
|
||||
*/
|
||||
|
||||
'subscriptions' => [
|
||||
/*
|
||||
* Determines if broadcasts should be queued by default.
|
||||
*/
|
||||
'queue_broadcasts' => env('LIGHTHOUSE_QUEUE_BROADCASTS', true),
|
||||
|
||||
/*
|
||||
* Default subscription storage.
|
||||
*
|
||||
* Any Laravel supported cache driver options are available here.
|
||||
*/
|
||||
'storage' => env('LIGHTHOUSE_SUBSCRIPTION_STORAGE', 'redis'),
|
||||
|
||||
/*
|
||||
* Default subscription broadcaster.
|
||||
*/
|
||||
'broadcaster' => env('LIGHTHOUSE_BROADCASTER', 'pusher'),
|
||||
|
||||
/*
|
||||
* Subscription broadcasting drivers with config options.
|
||||
*/
|
||||
'broadcasters' => [
|
||||
'log' => [
|
||||
'driver' => 'log',
|
||||
],
|
||||
'pusher' => [
|
||||
'driver' => 'pusher',
|
||||
'routes' => \Nuwave\Lighthouse\Subscriptions\SubscriptionRouter::class.'@pusher',
|
||||
'connection' => 'pusher',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
29
vendor/nuwave/lighthouse/rector.php
vendored
Normal file
29
vendor/nuwave/lighthouse/rector.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedParameterRector;
|
||||
use Rector\Set\ValueObject\SetList;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
|
||||
$parameters->set(Option::SETS, [
|
||||
SetList::CODE_QUALITY,
|
||||
SetList::DEAD_CODE,
|
||||
SetList::PHPUNIT_EXCEPTION,
|
||||
SetList::PHPUNIT_SPECIFIC_METHOD,
|
||||
SetList::PHPUNIT_YIELD_DATA_PROVIDER,
|
||||
]);
|
||||
|
||||
$parameters->set(Option::SKIP, [
|
||||
// Does not fit autoloading standards
|
||||
__DIR__.'/tests/database/migrations',
|
||||
|
||||
// Gets stuck on WhereConditionsBaseDirective for some reason
|
||||
__DIR__.'/src/WhereConditions',
|
||||
|
||||
// Having unused parameters can increase clarity, e.g. in event handlers
|
||||
RemoveUnusedParameterRector::class,
|
||||
]);
|
||||
};
|
||||
79
vendor/nuwave/lighthouse/src/ClientDirectives/ClientDirective.php
vendored
Normal file
79
vendor/nuwave/lighthouse/src/ClientDirectives/ClientDirective.php
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\ClientDirectives;
|
||||
|
||||
use GraphQL\Executor\Values;
|
||||
use GraphQL\Type\Definition\Directive;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use Nuwave\Lighthouse\Exceptions\DefinitionException;
|
||||
use Nuwave\Lighthouse\GraphQL;
|
||||
use Nuwave\Lighthouse\Schema\SchemaBuilder;
|
||||
|
||||
/**
|
||||
* Provides information about where client directives
|
||||
* were placed in the query and what arguments were given to them.
|
||||
*
|
||||
* TODO implement accessors for other locations https://spec.graphql.org/draft/#ExecutableDirectiveLocation
|
||||
*/
|
||||
class ClientDirective
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var \GraphQL\Type\Definition\Directive|null
|
||||
*/
|
||||
protected $definition;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the given values for a client directive.
|
||||
*
|
||||
* This returns an array of the given arguments for all field nodes.
|
||||
* The number of items in the returned result will always be equivalent
|
||||
* to the number of field nodes, each having one of the following values:
|
||||
* - When a field node does not have the directive on it: null
|
||||
* - When the directive is present but has no arguments: []
|
||||
* - When the directive is present with arguments: an associative array
|
||||
*
|
||||
* @return array<array<string, mixed>|null>
|
||||
*/
|
||||
public function forField(ResolveInfo $resolveInfo): array
|
||||
{
|
||||
$directive = $this->definition();
|
||||
|
||||
$arguments = [];
|
||||
foreach ($resolveInfo->fieldNodes as $fieldNode) {
|
||||
$arguments [] = Values::getDirectiveValues($directive, $fieldNode, $resolveInfo->variableValues);
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Nuwave\Lighthouse\Exceptions\DefinitionException
|
||||
*/
|
||||
protected function definition(): Directive
|
||||
{
|
||||
if ($this->definition !== null) {
|
||||
return $this->definition;
|
||||
}
|
||||
|
||||
/** @var \Nuwave\Lighthouse\Schema\SchemaBuilder $schemaBuilder */
|
||||
$schemaBuilder = app(SchemaBuilder::class);
|
||||
$schema = $schemaBuilder->schema();
|
||||
|
||||
$definition = $schema->getDirective($this->name);
|
||||
if ($definition === null) {
|
||||
throw new DefinitionException("Missing a schema definition for the client directive $this->name");
|
||||
}
|
||||
|
||||
return $this->definition = $definition;
|
||||
}
|
||||
}
|
||||
20
vendor/nuwave/lighthouse/src/Console/CacheCommand.php
vendored
Normal file
20
vendor/nuwave/lighthouse/src/Console/CacheCommand.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Nuwave\Lighthouse\Schema\AST\ASTBuilder;
|
||||
|
||||
class CacheCommand extends Command
|
||||
{
|
||||
protected $name = 'lighthouse:cache';
|
||||
|
||||
protected $description = 'Compile the GraphQL schema and cache it.';
|
||||
|
||||
public function handle(ASTBuilder $builder): void
|
||||
{
|
||||
$builder->documentAST();
|
||||
|
||||
$this->info('GraphQL schema cache created.');
|
||||
}
|
||||
}
|
||||
@ -3,33 +3,45 @@
|
||||
namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Cache\Repository;
|
||||
use Illuminate\Contracts\Cache\Factory as CacheFactory;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Nuwave\Lighthouse\Exceptions\UnknownCacheVersionException;
|
||||
|
||||
class ClearCacheCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
* TODO remove once we require Laravel 6 which allows $this->call(ClearCacheCommand::class).
|
||||
*/
|
||||
protected $signature = 'lighthouse:clear-cache';
|
||||
const NAME = 'lighthouse:clear-cache';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Clear the cache for the GraphQL AST.';
|
||||
protected $name = self::NAME;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Repository $cache): void
|
||||
protected $description = 'Clear the GraphQL schema cache.';
|
||||
|
||||
public function handle(ConfigRepository $config): void
|
||||
{
|
||||
$cache->forget(config('lighthouse.cache.key'));
|
||||
$version = $config->get('lighthouse.cache.version', 1);
|
||||
switch ($version) {
|
||||
case 1:
|
||||
/** @var \Illuminate\Contracts\Cache\Factory $cacheFactory */
|
||||
$cacheFactory = app(CacheFactory::class);
|
||||
|
||||
$cacheFactory
|
||||
->store($config->get('lighthouse.cache.store'))
|
||||
->forget($config->get('lighthouse.cache.key'));
|
||||
break;
|
||||
case 2:
|
||||
/** @var \Illuminate\Filesystem\Filesystem $filesystem */
|
||||
$filesystem = app(Filesystem::class);
|
||||
|
||||
$path = $config->get('lighthouse.cache.path')
|
||||
?? base_path('bootstrap/cache/lighthouse-schema.php');
|
||||
$filesystem->delete($path);
|
||||
break;
|
||||
default:
|
||||
throw new UnknownCacheVersionException($version);
|
||||
}
|
||||
|
||||
$this->info('GraphQL AST schema cache deleted.');
|
||||
}
|
||||
|
||||
228
vendor/nuwave/lighthouse/src/Console/DirectiveCommand.php
vendored
Normal file
228
vendor/nuwave/lighthouse/src/Console/DirectiveCommand.php
vendored
Normal file
@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Nuwave\Lighthouse\Support\Contracts\ArgBuilderDirective;
|
||||
use Nuwave\Lighthouse\Support\Contracts\ArgDirective;
|
||||
use Nuwave\Lighthouse\Support\Contracts\ArgDirectiveForArray;
|
||||
use Nuwave\Lighthouse\Support\Contracts\ArgManipulator;
|
||||
use Nuwave\Lighthouse\Support\Contracts\ArgResolver;
|
||||
use Nuwave\Lighthouse\Support\Contracts\ArgTransformerDirective;
|
||||
use Nuwave\Lighthouse\Support\Contracts\FieldManipulator;
|
||||
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
|
||||
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
|
||||
use Nuwave\Lighthouse\Support\Contracts\TypeExtensionManipulator;
|
||||
use Nuwave\Lighthouse\Support\Contracts\TypeManipulator;
|
||||
use Nuwave\Lighthouse\Support\Contracts\TypeMiddleware;
|
||||
use Nuwave\Lighthouse\Support\Contracts\TypeResolver;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
class DirectiveCommand extends LighthouseGeneratorCommand
|
||||
{
|
||||
const ARGUMENT_INTERFACES = [
|
||||
ArgTransformerDirective::class,
|
||||
ArgBuilderDirective::class,
|
||||
ArgResolver::class,
|
||||
ArgManipulator::class,
|
||||
];
|
||||
|
||||
const FIELD_INTERFACES = [
|
||||
FieldResolver::class,
|
||||
FieldMiddleware::class,
|
||||
FieldManipulator::class,
|
||||
];
|
||||
|
||||
const TYPE_INTERFACES = [
|
||||
TypeManipulator::class,
|
||||
TypeMiddleware::class,
|
||||
TypeResolver::class,
|
||||
TypeExtensionManipulator::class,
|
||||
];
|
||||
|
||||
protected $name = 'lighthouse:directive';
|
||||
|
||||
protected $description = 'Create a class for a custom schema directive.';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Directive';
|
||||
|
||||
/**
|
||||
* The required imports.
|
||||
*
|
||||
* @var \Illuminate\Support\Collection<string>
|
||||
*/
|
||||
protected $imports;
|
||||
|
||||
/**
|
||||
* The implemented interfaces.
|
||||
*
|
||||
* @var \Illuminate\Support\Collection<string>
|
||||
*/
|
||||
protected $implements;
|
||||
|
||||
/**
|
||||
* The method stubs.
|
||||
*
|
||||
* @var \Illuminate\Support\Collection<string>
|
||||
*/
|
||||
protected $methods;
|
||||
|
||||
protected function getNameInput(): string
|
||||
{
|
||||
return parent::getNameInput().'Directive';
|
||||
}
|
||||
|
||||
protected function namespaceConfigKey(): string
|
||||
{
|
||||
return 'directives';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
protected function buildClass($name): string
|
||||
{
|
||||
$this->imports = new Collection();
|
||||
$this->implements = new Collection();
|
||||
$this->methods = new Collection();
|
||||
|
||||
$stub = parent::buildClass($name);
|
||||
|
||||
$forType = $this->option('type');
|
||||
$forField = $this->option('field');
|
||||
$forArgument = $this->option('argument');
|
||||
|
||||
if (! $forType && ! $forField && ! $forArgument) {
|
||||
throw new \Exception('Must specify at least one of: --type, --field or --argument');
|
||||
}
|
||||
|
||||
if ($forType) {
|
||||
$this->askForInterfaces(self::TYPE_INTERFACES);
|
||||
}
|
||||
|
||||
if ($forField) {
|
||||
$this->askForInterfaces(self::FIELD_INTERFACES);
|
||||
}
|
||||
|
||||
if ($forArgument) {
|
||||
// Arg directives always either implement ArgDirective or ArgDirectiveForArray.
|
||||
if ($this->confirm('Will your argument directive apply to a list of items?')) {
|
||||
$this->implementInterface(ArgDirectiveForArray::class);
|
||||
} else {
|
||||
$this->implementInterface(ArgDirective::class);
|
||||
}
|
||||
|
||||
$this->askForInterfaces(self::ARGUMENT_INTERFACES);
|
||||
}
|
||||
|
||||
$stub = str_replace(
|
||||
'{{ imports }}',
|
||||
$this->imports
|
||||
->filter()
|
||||
->unique()
|
||||
->implode("\n"),
|
||||
$stub
|
||||
);
|
||||
|
||||
$stub = str_replace(
|
||||
'{{ methods }}',
|
||||
$this->methods->implode("\n"),
|
||||
$stub
|
||||
);
|
||||
|
||||
return str_replace(
|
||||
'{{ implements }}',
|
||||
$this->implements->implode(', '),
|
||||
$stub
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the user if the directive should implement any of the given interfaces.
|
||||
*
|
||||
* @param array<class-string> $interfaces
|
||||
*/
|
||||
protected function askForInterfaces(array $interfaces): void
|
||||
{
|
||||
foreach ($interfaces as $interface) {
|
||||
if ($this->confirm("Should the directive implement the {$this->shortName($interface)} middleware?")) {
|
||||
$this->implementInterface($interface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string $interface
|
||||
*/
|
||||
protected function shortName(string $interface): string
|
||||
{
|
||||
return Str::afterLast($interface, '\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string $interface
|
||||
*/
|
||||
protected function implementInterface(string $interface): void
|
||||
{
|
||||
$shortName = $this->shortName($interface);
|
||||
$this->implements->push($shortName);
|
||||
|
||||
$this->imports->push("use {$interface};");
|
||||
if ($imports = $this->interfaceImports($shortName)) {
|
||||
$imports = explode("\n", $imports);
|
||||
$this->imports->push(...$imports);
|
||||
}
|
||||
|
||||
if ($methods = $this->interfaceMethods($shortName)) {
|
||||
$this->methods->push($methods);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getStub(): string
|
||||
{
|
||||
return __DIR__.'/stubs/directive.stub';
|
||||
}
|
||||
|
||||
protected function interfaceMethods(string $interface): ?string
|
||||
{
|
||||
return $this->getFileIfExists(
|
||||
__DIR__.'/stubs/directives/'.Str::snake($interface).'_methods.stub'
|
||||
);
|
||||
}
|
||||
|
||||
protected function interfaceImports(string $interface): ?string
|
||||
{
|
||||
return $this->getFileIfExists(
|
||||
__DIR__.'/stubs/directives/'.Str::snake($interface).'_imports.stub'
|
||||
);
|
||||
}
|
||||
|
||||
protected function getFileIfExists(string $path): ?string
|
||||
{
|
||||
if (! $this->files->exists($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->files->get($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, mixed>>
|
||||
*/
|
||||
protected function getOptions(): array
|
||||
{
|
||||
return [
|
||||
['type', null, InputOption::VALUE_NONE, 'Create a directive that can be applied to types.'],
|
||||
['field', null, InputOption::VALUE_NONE, 'Create a directive that can be applied to fields.'],
|
||||
['argument', null, InputOption::VALUE_NONE, 'Create a directive that can be applied to arguments.'],
|
||||
];
|
||||
}
|
||||
}
|
||||
27
vendor/nuwave/lighthouse/src/Console/FieldGeneratorCommand.php
vendored
Normal file
27
vendor/nuwave/lighthouse/src/Console/FieldGeneratorCommand.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
abstract class FieldGeneratorCommand extends LighthouseGeneratorCommand
|
||||
{
|
||||
protected function getStub(): string
|
||||
{
|
||||
$stub = $this->option('full')
|
||||
? 'field_full'
|
||||
: 'field_simple';
|
||||
|
||||
return __DIR__."/stubs/{$stub}.stub";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, mixed>>
|
||||
*/
|
||||
protected function getOptions(): array
|
||||
{
|
||||
return [
|
||||
['full', 'F', InputOption::VALUE_NONE, 'Include the seldom needed resolver arguments $context and $resolveInfo'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -2,88 +2,91 @@
|
||||
|
||||
namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Utils\SchemaPrinter;
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
use Nuwave\Lighthouse\Schema\AST\PartialParser;
|
||||
use Nuwave\Lighthouse\Schema\DirectiveNamespacer;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Nuwave\Lighthouse\Schema\AST\ASTHelper;
|
||||
use Nuwave\Lighthouse\Schema\DirectiveLocator;
|
||||
use Nuwave\Lighthouse\Schema\TypeRegistry;
|
||||
use Nuwave\Lighthouse\Support\Contracts\Directive;
|
||||
use Nuwave\Lighthouse\Support\Contracts\DefinedDirective;
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
|
||||
class IdeHelperCommand extends Command
|
||||
{
|
||||
const GENERATED_NOTICE = <<<'SDL'
|
||||
public const OPENING_PHP_TAG = /** @lang GraphQL */ "<?php\n";
|
||||
|
||||
public const GENERATED_NOTICE = /** @lang GraphQL */ <<<'GRAPHQL'
|
||||
# File generated by "php artisan lighthouse:ide-helper".
|
||||
# Do not edit this file directly.
|
||||
# This file should be ignored by git.
|
||||
# This file should be ignored by git as it can be autogenerated.
|
||||
|
||||
SDL;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'lighthouse:ide-helper';
|
||||
GRAPHQL;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Gather all schema directive definitions and write them to a file.';
|
||||
protected $name = 'lighthouse:ide-helper';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\DirectiveNamespacer $directiveNamespaces
|
||||
* @return int
|
||||
*/
|
||||
public function handle(DirectiveNamespacer $directiveNamespaces): int
|
||||
protected $description = 'Create IDE helper files to improve type checking and autocompletion.';
|
||||
|
||||
public function handle(DirectiveLocator $directiveLocator, TypeRegistry $typeRegistry): int
|
||||
{
|
||||
if (! class_exists('HaydenPierce\ClassFinder\ClassFinder')) {
|
||||
$this->error(
|
||||
"This command requires haydenpierce/class-finder. Install it by running:\n"
|
||||
."\n"
|
||||
." composer require --dev haydenpierce/class-finder\n"
|
||||
);
|
||||
$this->schemaDirectiveDefinitions($directiveLocator);
|
||||
$this->programmaticTypes($typeRegistry);
|
||||
$this->phpIdeHelper();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$directiveClasses = $this->scanForDirectives(
|
||||
$directiveNamespaces->gather()
|
||||
);
|
||||
|
||||
$schema = $this->buildSchemaString($directiveClasses);
|
||||
|
||||
$filePath = static::filePath();
|
||||
file_put_contents($filePath, $schema);
|
||||
|
||||
$this->info("Wrote schema directive definitions to $filePath.");
|
||||
$this->info("\nIt is recommended to add them to your .gitignore file.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and write schema directive definitions to a file.
|
||||
*/
|
||||
protected function schemaDirectiveDefinitions(DirectiveLocator $directiveLocator): void
|
||||
{
|
||||
$schema = /** @lang GraphQL */ <<<'GRAPHQL'
|
||||
"""
|
||||
Placeholder type for various directives such as `@orderBy`.
|
||||
Will be replaced by a generated type during schema manipulation.
|
||||
"""
|
||||
scalar _
|
||||
|
||||
GRAPHQL;
|
||||
|
||||
$directiveClasses = $this->scanForDirectives(
|
||||
$directiveLocator->namespaces()
|
||||
);
|
||||
|
||||
foreach ($directiveClasses as $directiveClass) {
|
||||
$definition = $this->define($directiveClass);
|
||||
|
||||
$schema .= /** @lang GraphQL */ <<<GRAPHQL
|
||||
|
||||
# Directive class: $directiveClass
|
||||
$definition
|
||||
|
||||
GRAPHQL;
|
||||
}
|
||||
|
||||
$filePath = static::schemaDirectivesPath();
|
||||
\Safe\file_put_contents($filePath, self::GENERATED_NOTICE.$schema);
|
||||
|
||||
$this->info("Wrote schema directive definitions to $filePath.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the given namespaces for directive classes.
|
||||
*
|
||||
* @param string[] $directiveNamespaces
|
||||
* @return string[]
|
||||
* @param array<string> $directiveNamespaces
|
||||
* @return array<string, class-string<\Nuwave\Lighthouse\Support\Contracts\Directive>>
|
||||
*/
|
||||
protected function scanForDirectives(array $directiveNamespaces): array
|
||||
{
|
||||
$directives = [];
|
||||
|
||||
foreach ($directiveNamespaces as $directiveNamespace) {
|
||||
try {
|
||||
$classesInNamespace = ClassFinder::getClassesInNamespace($directiveNamespace);
|
||||
} catch (ClassFinderException $classFinderException) {
|
||||
// TODO remove if https://gitlab.com/hpierce1102/ClassFinder/merge_requests/16 is merged
|
||||
// The ClassFinder throws if no classes are found. Since we can not know
|
||||
// in advance if the user has defined custom directives, this behaviour is problematic.
|
||||
continue;
|
||||
}
|
||||
/** @var array<class-string> $classesInNamespace */
|
||||
$classesInNamespace = ClassFinder::getClassesInNamespace($directiveNamespace);
|
||||
|
||||
foreach ($classesInNamespace as $class) {
|
||||
$reflection = new \ReflectionClass($class);
|
||||
@ -94,10 +97,7 @@ SDL;
|
||||
if (! is_a($class, Directive::class, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var \Nuwave\Lighthouse\Support\Contracts\Directive $instance */
|
||||
$instance = app($class);
|
||||
$name = $instance->name();
|
||||
$name = DirectiveLocator::directiveName($class);
|
||||
|
||||
// The directive was already found, so we do not add it twice
|
||||
if (isset($directives[$name])) {
|
||||
@ -112,42 +112,77 @@ SDL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $directiveClasses
|
||||
* @return string
|
||||
* @param class-string<\Nuwave\Lighthouse\Support\Contracts\Directive> $directiveClass
|
||||
* @throws \Nuwave\Lighthouse\Exceptions\DefinitionException
|
||||
*/
|
||||
protected function buildSchemaString(array $directiveClasses): string
|
||||
protected function define(string $directiveClass): string
|
||||
{
|
||||
$schema = self::GENERATED_NOTICE;
|
||||
$definition = $directiveClass::definition();
|
||||
|
||||
foreach ($directiveClasses as $name => $directiveClass) {
|
||||
$definition = $this->define($name, $directiveClass);
|
||||
// Throws if the definition is invalid
|
||||
ASTHelper::extractDirectiveDefinition($definition);
|
||||
|
||||
$schema .= "\n"
|
||||
."# Directive class: $directiveClass\n"
|
||||
.$definition."\n";
|
||||
}
|
||||
|
||||
return $schema;
|
||||
return trim($definition);
|
||||
}
|
||||
|
||||
protected function define(string $name, string $directiveClass): string
|
||||
{
|
||||
if (is_a($directiveClass, DefinedDirective::class, true)) {
|
||||
/** @var DefinedDirective $directiveClass */
|
||||
$definition = $directiveClass::definition();
|
||||
|
||||
// This operation throws if the schema definition is invalid
|
||||
PartialParser::directiveDefinition($definition);
|
||||
|
||||
return trim($definition);
|
||||
} else {
|
||||
return '# Add a proper definition by implementing '.DefinedDirective::class."\n"
|
||||
."directive @{$name}";
|
||||
}
|
||||
}
|
||||
|
||||
public static function filePath(): string
|
||||
public static function schemaDirectivesPath(): string
|
||||
{
|
||||
return base_path().'/schema-directives.graphql';
|
||||
}
|
||||
|
||||
protected function programmaticTypes(TypeRegistry $typeRegistry): void
|
||||
{
|
||||
// Users may register types programmatically, e.g. in service providers
|
||||
// In order to allow referencing those in the schema, it is useful to print
|
||||
// those types to a helper schema, excluding types the user defined in the schema
|
||||
$types = new Collection($typeRegistry->resolvedTypes());
|
||||
|
||||
$filePath = static::programmaticTypesPath();
|
||||
|
||||
if ($types->isEmpty() && file_exists($filePath)) {
|
||||
\Safe\unlink($filePath);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$schema = $types
|
||||
->map(function (Type $type): string {
|
||||
return SchemaPrinter::printType($type);
|
||||
})
|
||||
->implode("\n");
|
||||
|
||||
\Safe\file_put_contents($filePath, self::GENERATED_NOTICE.$schema);
|
||||
|
||||
$this->info("Wrote definitions for programmatically registered types to $filePath.");
|
||||
}
|
||||
|
||||
public static function programmaticTypesPath(): string
|
||||
{
|
||||
return base_path().'/programmatic-types.graphql';
|
||||
}
|
||||
|
||||
protected function phpIdeHelper(): void
|
||||
{
|
||||
$filePath = static::phpIdeHelperPath();
|
||||
$contents = \Safe\file_get_contents(__DIR__.'/../../_ide_helper.php');
|
||||
|
||||
\Safe\file_put_contents($filePath, $this->withGeneratedNotice($contents));
|
||||
|
||||
$this->info("Wrote PHP definitions to $filePath.");
|
||||
}
|
||||
|
||||
public static function phpIdeHelperPath(): string
|
||||
{
|
||||
return base_path().'/_lighthouse_ide_helper.php';
|
||||
}
|
||||
|
||||
protected function withGeneratedNotice(string $phpContents): string
|
||||
{
|
||||
return substr_replace(
|
||||
$phpContents,
|
||||
self::OPENING_PHP_TAG.self::GENERATED_NOTICE,
|
||||
0,
|
||||
strlen(self::OPENING_PHP_TAG)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,43 +4,17 @@ namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
class InterfaceCommand extends LighthouseGeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The name of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'lighthouse:interface';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a class for a GraphQL interface type.';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Interface';
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace): string
|
||||
protected function namespaceConfigKey(): string
|
||||
{
|
||||
return config('lighthouse.namespaces.interfaces');
|
||||
return 'interfaces';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub(): string
|
||||
{
|
||||
return __DIR__.'/stubs/typeResolver.stub';
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use InvalidArgumentException;
|
||||
|
||||
abstract class LighthouseGeneratorCommand extends GeneratorCommand
|
||||
{
|
||||
@ -12,11 +13,83 @@ abstract class LighthouseGeneratorCommand extends GeneratorCommand
|
||||
* As a typical workflow would be to write the schema first and then copy-paste
|
||||
* a field name to generate a class for it, we uppercase it so the user does
|
||||
* not run into unnecessary errors. You're welcome.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getNameInput(): string
|
||||
{
|
||||
return ucfirst(trim($this->argument('name')));
|
||||
$name = $this->argument('name');
|
||||
if (! is_string($name)) {
|
||||
throw new InvalidArgumentException('You must the name for the class to generate.');
|
||||
}
|
||||
|
||||
return ucfirst(trim($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $rootNamespace
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace): string
|
||||
{
|
||||
$namespaces = config('lighthouse.namespaces.'.$this->namespaceConfigKey());
|
||||
|
||||
return static::commonNamespace((array) $namespaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the config key that holds the default namespaces for the class.
|
||||
*/
|
||||
abstract protected function namespaceConfigKey(): string;
|
||||
|
||||
/**
|
||||
* Find the common namespace of a list of namespaces.
|
||||
*
|
||||
* For example, ['App\\Foo\\A', 'App\\Foo\\B'] would return 'App\\Foo'.
|
||||
*
|
||||
* @param array<string> $namespaces
|
||||
*/
|
||||
public static function commonNamespace(array $namespaces): string
|
||||
{
|
||||
if ($namespaces === []) {
|
||||
throw new InvalidArgumentException(
|
||||
'A default namespace is required for code generation.'
|
||||
);
|
||||
}
|
||||
|
||||
if (count($namespaces) === 1) {
|
||||
return reset($namespaces);
|
||||
}
|
||||
|
||||
// Save the first namespace
|
||||
$preferredNamespaceFallback = reset($namespaces);
|
||||
|
||||
// If the strings are sorted, any prefix common to all strings
|
||||
// will be common to the sorted first and last strings.
|
||||
// All the strings in the middle can be ignored.
|
||||
\Safe\sort($namespaces);
|
||||
|
||||
$firstParts = explode('\\', reset($namespaces));
|
||||
$lastParts = explode('\\', end($namespaces));
|
||||
|
||||
$matching = [];
|
||||
foreach ($firstParts as $i => $part) {
|
||||
// We ran out of elements to compare, so we reached the maximum common length
|
||||
if (! isset($lastParts[$i])) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We found an element that differs
|
||||
if ($lastParts[$i] !== $part) {
|
||||
break;
|
||||
}
|
||||
|
||||
$matching [] = $part;
|
||||
}
|
||||
|
||||
// We could not determine a common part of the configured namespaces,
|
||||
// so we just assume the user will prefer the first one in the list.
|
||||
if ($matching === []) {
|
||||
return $preferredNamespaceFallback;
|
||||
}
|
||||
|
||||
return implode('\\', $matching);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,47 +2,16 @@
|
||||
|
||||
namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
class MutationCommand extends LighthouseGeneratorCommand
|
||||
class MutationCommand extends FieldGeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The name of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'lighthouse:mutation';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a class for a single field on the root Mutation type.';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Mutation';
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace): string
|
||||
protected function namespaceConfigKey(): string
|
||||
{
|
||||
return config('lighthouse.namespaces.mutations');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub(): string
|
||||
{
|
||||
return __DIR__.'/stubs/field.stub';
|
||||
return 'mutations';
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,53 +2,62 @@
|
||||
|
||||
namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
use Nuwave\Lighthouse\GraphQL;
|
||||
use Illuminate\Console\Command;
|
||||
use GraphQL\Type\Introspection;
|
||||
use GraphQL\Type\Schema;
|
||||
use GraphQL\Utils\SchemaPrinter;
|
||||
use Illuminate\Cache\Repository;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||
use Nuwave\Lighthouse\Schema\SchemaBuilder;
|
||||
|
||||
class PrintSchemaCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = '
|
||||
lighthouse:print-schema
|
||||
{--W|write : Write the output to a file}
|
||||
';
|
||||
public const GRAPHQL_FILENAME = 'lighthouse-schema.graphql';
|
||||
public const JSON_FILENAME = 'lighthouse-schema.json';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Compile the final GraphQL schema and print the result.';
|
||||
protected $signature = <<<'SIGNATURE'
|
||||
lighthouse:print-schema
|
||||
{--W|write : Write the output to a file}
|
||||
{--json : Output JSON instead of GraphQL SDL}
|
||||
SIGNATURE;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @param \Illuminate\Cache\Repository $cache
|
||||
* @param \Illuminate\Contracts\Filesystem\Filesystem $storage
|
||||
* @param \Nuwave\Lighthouse\GraphQL $graphQL
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Repository $cache, Filesystem $storage, GraphQL $graphQL): void
|
||||
protected $description = 'Compile the GraphQL schema and print the result.';
|
||||
|
||||
public function handle(Filesystem $storage, SchemaBuilder $schemaBuilder): void
|
||||
{
|
||||
// Clear the cache so this always gets the current schema
|
||||
$cache->forget(config('lighthouse.cache.key'));
|
||||
$this->callSilent(ClearCacheCommand::NAME);
|
||||
|
||||
$schema = SchemaPrinter::doPrint(
|
||||
$graphQL->prepSchema()
|
||||
);
|
||||
$schema = $schemaBuilder->schema();
|
||||
if ($this->option('json')) {
|
||||
$filename = self::JSON_FILENAME;
|
||||
$schemaString = $this->toJson($schema);
|
||||
} else {
|
||||
$filename = self::GRAPHQL_FILENAME;
|
||||
$schemaString = SchemaPrinter::doPrint($schema);
|
||||
}
|
||||
|
||||
if ($this->option('write')) {
|
||||
$storage->put('lighthouse-schema.graphql', $schema);
|
||||
$this->info('Wrote schema to the default file storage (usually storage/app) as "lighthouse-schema.graphql".');
|
||||
$storage->put($filename, $schemaString);
|
||||
$this->info('Wrote schema to the default file storage (usually storage/app) as "'.$filename.'".');
|
||||
} else {
|
||||
$this->info($schema);
|
||||
$this->info($schemaString);
|
||||
}
|
||||
}
|
||||
|
||||
protected function toJson(Schema $schema): string
|
||||
{
|
||||
$introspectionResult = Introspection::fromSchema($schema);
|
||||
if ($introspectionResult === null) {
|
||||
throw new \Exception(<<<'MESSAGE'
|
||||
Did not receive a valid introspection result.
|
||||
Check if your schema is correct with:
|
||||
|
||||
php artisan lighthouse:validate-schema
|
||||
|
||||
MESSAGE
|
||||
);
|
||||
}
|
||||
|
||||
return \Safe\json_encode($introspectionResult);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,47 +2,16 @@
|
||||
|
||||
namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
class QueryCommand extends LighthouseGeneratorCommand
|
||||
class QueryCommand extends FieldGeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The name of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'lighthouse:query';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a class for a single field on the root Query type.';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Query';
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace): string
|
||||
protected function namespaceConfigKey(): string
|
||||
{
|
||||
return config('lighthouse.namespaces.queries');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub(): string
|
||||
{
|
||||
return __DIR__.'/stubs/field.stub';
|
||||
return 'queries';
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,43 +4,17 @@ namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
class ScalarCommand extends LighthouseGeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The name of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'lighthouse:scalar';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a class for a GraphQL scalar type.';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Scalar';
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace): string
|
||||
protected function namespaceConfigKey(): string
|
||||
{
|
||||
return config('lighthouse.namespaces.scalars');
|
||||
return 'scalars';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub(): string
|
||||
{
|
||||
return __DIR__.'/stubs/scalar.stub';
|
||||
|
||||
@ -4,43 +4,17 @@ namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
class SubscriptionCommand extends LighthouseGeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The name of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'lighthouse:subscription';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a class for a single field on the root Subscription type.';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Subscription';
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace): string
|
||||
protected function namespaceConfigKey(): string
|
||||
{
|
||||
return config('lighthouse.namespaces.subscriptions');
|
||||
return 'subscriptions';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub(): string
|
||||
{
|
||||
return __DIR__.'/stubs/subscription.stub';
|
||||
|
||||
@ -4,43 +4,17 @@ namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
class UnionCommand extends LighthouseGeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The name of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'lighthouse:union';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a class for a GraphQL union type.';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Union';
|
||||
|
||||
/**
|
||||
* Get the default namespace for the class.
|
||||
*
|
||||
* @param string $rootNamespace
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace): string
|
||||
protected function namespaceConfigKey(): string
|
||||
{
|
||||
return config('lighthouse.namespaces.unions');
|
||||
return 'unions';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub(): string
|
||||
{
|
||||
return __DIR__.'/stubs/typeResolver.stub';
|
||||
|
||||
@ -2,38 +2,52 @@
|
||||
|
||||
namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
use Nuwave\Lighthouse\GraphQL;
|
||||
use GraphQL\Type\Schema;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Cache\Repository;
|
||||
use Illuminate\Contracts\Events\Dispatcher as EventsDispatcher;
|
||||
use Nuwave\Lighthouse\Events\ValidateSchema;
|
||||
use Nuwave\Lighthouse\Schema\DirectiveLocator;
|
||||
use Nuwave\Lighthouse\Schema\Factories\DirectiveFactory;
|
||||
use Nuwave\Lighthouse\Schema\FallbackTypeNodeConverter;
|
||||
use Nuwave\Lighthouse\Schema\SchemaBuilder;
|
||||
use Nuwave\Lighthouse\Schema\TypeRegistry;
|
||||
|
||||
class ValidateSchemaCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'lighthouse:validate-schema';
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'lighthouse:validate-schema';
|
||||
|
||||
protected $description = 'Validate the GraphQL schema definition.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||
* @param \Nuwave\Lighthouse\GraphQL $graphQL
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Repository $cache, GraphQL $graphQL): void
|
||||
{
|
||||
public function handle(
|
||||
EventsDispatcher $eventsDispatcher,
|
||||
SchemaBuilder $schemaBuilder,
|
||||
DirectiveLocator $directiveLocator,
|
||||
TypeRegistry $typeRegistry
|
||||
): void {
|
||||
// Clear the cache so this always validates the current schema
|
||||
$cache->forget(config('lighthouse.cache.key'));
|
||||
$this->call(ClearCacheCommand::NAME);
|
||||
|
||||
$graphQL->prepSchema()->assertValid();
|
||||
$originalSchema = $schemaBuilder->schema();
|
||||
$schemaConfig = $originalSchema->getConfig();
|
||||
|
||||
// We add schema directive definitions only here, since it is very slow
|
||||
$directiveFactory = new DirectiveFactory(
|
||||
new FallbackTypeNodeConverter($typeRegistry)
|
||||
);
|
||||
foreach ($directiveLocator->definitions() as $directiveDefinition) {
|
||||
// TODO consider a solution that feels less hacky
|
||||
if ($directiveDefinition->name->value !== 'deprecated') {
|
||||
$schemaConfig->directives [] = $directiveFactory->handle($directiveDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
$schema = new Schema($schemaConfig);
|
||||
$schema->assertValid();
|
||||
|
||||
// Allow plugins to do their own schema validations
|
||||
$eventsDispatcher->dispatch(
|
||||
new ValidateSchema($schema)
|
||||
);
|
||||
|
||||
$this->info('The defined schema is valid.');
|
||||
}
|
||||
|
||||
40
vendor/nuwave/lighthouse/src/Console/ValidatorCommand.php
vendored
Normal file
40
vendor/nuwave/lighthouse/src/Console/ValidatorCommand.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Console;
|
||||
|
||||
class ValidatorCommand extends LighthouseGeneratorCommand
|
||||
{
|
||||
/**
|
||||
* The name of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'lighthouse:validator';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a validator class';
|
||||
|
||||
/**
|
||||
* The type of class being generated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Validator';
|
||||
|
||||
protected function namespaceConfigKey(): string
|
||||
{
|
||||
return 'validators';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stub file for the generator.
|
||||
*/
|
||||
protected function getStub(): string
|
||||
{
|
||||
return __DIR__.'/stubs/validator.stub';
|
||||
}
|
||||
}
|
||||
12
vendor/nuwave/lighthouse/src/Console/stubs/directive.stub
vendored
Normal file
12
vendor/nuwave/lighthouse/src/Console/stubs/directive.stub
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace DummyNamespace;
|
||||
|
||||
use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
|
||||
{{ imports }}
|
||||
|
||||
class DummyClass extends BaseDirective implements {{ implements }}
|
||||
{
|
||||
// TODO implement the directive https://lighthouse-php.com/master/custom-directives/getting-started.html
|
||||
|
||||
{{ methods }}}
|
||||
11
vendor/nuwave/lighthouse/src/Console/stubs/directives/arg_builder_directive_methods.stub
vendored
Normal file
11
vendor/nuwave/lighthouse/src/Console/stubs/directives/arg_builder_directive_methods.stub
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Add additional constraints to the builder based on the given argument value.
|
||||
*
|
||||
* @param \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder $builder
|
||||
* @param mixed $value
|
||||
* @return \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function handleBuilder($builder, $value)
|
||||
{
|
||||
// TODO implement the arg builder
|
||||
}
|
||||
4
vendor/nuwave/lighthouse/src/Console/stubs/directives/arg_manipulator_imports.stub
vendored
Normal file
4
vendor/nuwave/lighthouse/src/Console/stubs/directives/arg_manipulator_imports.stub
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
use GraphQL\Language\AST\FieldDefinitionNode;
|
||||
use GraphQL\Language\AST\InputValueDefinitionNode;
|
||||
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
|
||||
use Nuwave\Lighthouse\Schema\AST\DocumentAST;
|
||||
17
vendor/nuwave/lighthouse/src/Console/stubs/directives/arg_manipulator_methods.stub
vendored
Normal file
17
vendor/nuwave/lighthouse/src/Console/stubs/directives/arg_manipulator_methods.stub
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Manipulate the AST.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\AST\DocumentAST $documentAST
|
||||
* @param \GraphQL\Language\AST\InputValueDefinitionNode $argDefinition
|
||||
* @param \GraphQL\Language\AST\FieldDefinitionNode $parentField
|
||||
* @param \GraphQL\Language\AST\ObjectTypeDefinitionNode $parentType
|
||||
* @return void
|
||||
*/
|
||||
public function manipulateArgDefinition(
|
||||
DocumentAST &$documentAST,
|
||||
InputValueDefinitionNode &$argDefinition,
|
||||
FieldDefinitionNode &$parentField,
|
||||
ObjectTypeDefinitionNode &$parentType
|
||||
) {
|
||||
// TODO implement the arg manipulator
|
||||
}
|
||||
0
vendor/nuwave/lighthouse/src/Console/stubs/directives/arg_resolver_imports.stub
vendored
Normal file
0
vendor/nuwave/lighthouse/src/Console/stubs/directives/arg_resolver_imports.stub
vendored
Normal file
9
vendor/nuwave/lighthouse/src/Console/stubs/directives/arg_resolver_methods.stub
vendored
Normal file
9
vendor/nuwave/lighthouse/src/Console/stubs/directives/arg_resolver_methods.stub
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* @param mixed $root The result of the parent resolver.
|
||||
* @param mixed|\Nuwave\Lighthouse\Execution\Arguments\ArgumentSet|array<\Nuwave\Lighthouse\Execution\Arguments\ArgumentSet> $value The slice of arguments that belongs to this nested resolver.
|
||||
* @return mixed
|
||||
*/
|
||||
public function __invoke($root, $value)
|
||||
{
|
||||
// TODO implement the arg resolver
|
||||
}
|
||||
10
vendor/nuwave/lighthouse/src/Console/stubs/directives/arg_transformer_directive_methods.stub
vendored
Normal file
10
vendor/nuwave/lighthouse/src/Console/stubs/directives/arg_transformer_directive_methods.stub
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Apply transformations on the value of an argument given to a field.
|
||||
*
|
||||
* @param mixed $argumentValue
|
||||
* @return mixed
|
||||
*/
|
||||
public function transform($argumentValue)
|
||||
{
|
||||
// TODO implement the arg transformer
|
||||
}
|
||||
3
vendor/nuwave/lighthouse/src/Console/stubs/directives/field_manipulator_imports.stub
vendored
Normal file
3
vendor/nuwave/lighthouse/src/Console/stubs/directives/field_manipulator_imports.stub
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
use GraphQL\Language\AST\FieldDefinitionNode;
|
||||
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
|
||||
use Nuwave\Lighthouse\Schema\AST\DocumentAST;
|
||||
15
vendor/nuwave/lighthouse/src/Console/stubs/directives/field_manipulator_methods.stub
vendored
Normal file
15
vendor/nuwave/lighthouse/src/Console/stubs/directives/field_manipulator_methods.stub
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Manipulate the AST based on a field definition.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\AST\DocumentAST $documentAST
|
||||
* @param \GraphQL\Language\AST\FieldDefinitionNode $fieldDefinition
|
||||
* @param \GraphQL\Language\AST\ObjectTypeDefinitionNode $parentType
|
||||
* @return void
|
||||
*/
|
||||
public function manipulateFieldDefinition(
|
||||
DocumentAST &$documentAST,
|
||||
FieldDefinitionNode &$fieldDefinition,
|
||||
ObjectTypeDefinitionNode &$parentType
|
||||
) {
|
||||
// TODO implement the field manipulator
|
||||
}
|
||||
2
vendor/nuwave/lighthouse/src/Console/stubs/directives/field_middleware_imports.stub
vendored
Normal file
2
vendor/nuwave/lighthouse/src/Console/stubs/directives/field_middleware_imports.stub
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
use Closure;
|
||||
use Nuwave\Lighthouse\Schema\Values\FieldValue;
|
||||
11
vendor/nuwave/lighthouse/src/Console/stubs/directives/field_middleware_methods.stub
vendored
Normal file
11
vendor/nuwave/lighthouse/src/Console/stubs/directives/field_middleware_methods.stub
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Wrap around the final field resolver.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\Values\FieldValue $fieldValue
|
||||
* @param \Closure $next
|
||||
* @return \Nuwave\Lighthouse\Schema\Values\FieldValue
|
||||
*/
|
||||
public function handleField(FieldValue $fieldValue, Closure $next)
|
||||
{
|
||||
// TODO implement the field middleware
|
||||
}
|
||||
1
vendor/nuwave/lighthouse/src/Console/stubs/directives/field_resolver_imports.stub
vendored
Normal file
1
vendor/nuwave/lighthouse/src/Console/stubs/directives/field_resolver_imports.stub
vendored
Normal file
@ -0,0 +1 @@
|
||||
use Nuwave\Lighthouse\Schema\Values\FieldValue;
|
||||
13
vendor/nuwave/lighthouse/src/Console/stubs/directives/field_resolver_methods.stub
vendored
Normal file
13
vendor/nuwave/lighthouse/src/Console/stubs/directives/field_resolver_methods.stub
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Set a field resolver on the FieldValue.
|
||||
*
|
||||
* This must call $fieldValue->setResolver() before returning
|
||||
* the FieldValue.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\Values\FieldValue $fieldValue
|
||||
* @return \Nuwave\Lighthouse\Schema\Values\FieldValue
|
||||
*/
|
||||
public function resolveField(FieldValue $fieldValue)
|
||||
{
|
||||
// TODO implement the field resolver
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
use GraphQL\Language\AST\TypeExtensionNode;
|
||||
use Nuwave\Lighthouse\Schema\AST\DocumentAST;
|
||||
11
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_extension_manipulator_methods.stub
vendored
Normal file
11
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_extension_manipulator_methods.stub
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Apply manipulations from a type extension node.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\AST\DocumentAST $documentAST
|
||||
* @param \GraphQL\Language\AST\TypeExtensionNode $typeExtension
|
||||
* @return void
|
||||
*/
|
||||
public function manipulateTypeExtension(DocumentAST &$documentAST, TypeExtensionNode &$typeExtension)
|
||||
{
|
||||
// TODO implement the type extension manipulator
|
||||
}
|
||||
2
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_manipulator_imports.stub
vendored
Normal file
2
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_manipulator_imports.stub
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
use GraphQL\Language\AST\TypeDefinitionNode;
|
||||
use Nuwave\Lighthouse\Schema\AST\DocumentAST;
|
||||
11
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_manipulator_methods.stub
vendored
Normal file
11
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_manipulator_methods.stub
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Apply manipulations from a type definition node.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\AST\DocumentAST $documentAST
|
||||
* @param \GraphQL\Language\AST\TypeDefinitionNode $typeDefinition
|
||||
* @return void
|
||||
*/
|
||||
public function manipulateTypeDefinition(DocumentAST &$documentAST, TypeDefinitionNode &$typeDefinition)
|
||||
{
|
||||
// TODO implement the type manipulator
|
||||
}
|
||||
2
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_middleware_imports.stub
vendored
Normal file
2
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_middleware_imports.stub
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
use Closure;
|
||||
use Nuwave\Lighthouse\Schema\Values\TypeValue;
|
||||
11
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_middleware_methods.stub
vendored
Normal file
11
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_middleware_methods.stub
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Handle a type AST as it is converted to an executable type.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\Values\TypeValue $value
|
||||
* @param \Closure $next
|
||||
* @return \GraphQL\Type\Definition\Type
|
||||
*/
|
||||
public function handleNode(TypeValue $value, Closure $next)
|
||||
{
|
||||
// TODO implement the type middleware
|
||||
}
|
||||
1
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_resolver_imports.stub
vendored
Normal file
1
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_resolver_imports.stub
vendored
Normal file
@ -0,0 +1 @@
|
||||
use Nuwave\Lighthouse\Schema\Values\TypeValue;
|
||||
10
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_resolver_methods.stub
vendored
Normal file
10
vendor/nuwave/lighthouse/src/Console/stubs/directives/type_resolver_methods.stub
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Resolve a type AST to a GraphQL Type.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\Values\TypeValue $value
|
||||
* @return \GraphQL\Type\Definition\Type
|
||||
*/
|
||||
public function resolveNode(TypeValue $value)
|
||||
{
|
||||
// TODO implement the type resolver
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DummyNamespace;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
|
||||
|
||||
class DummyClass
|
||||
{
|
||||
/**
|
||||
* Return a value for the field.
|
||||
*
|
||||
* @param null $rootValue Usually contains the result returned from the parent field. In this case, it is always `null`.
|
||||
* @param mixed[] $args The arguments that were passed into the field.
|
||||
* @param \Nuwave\Lighthouse\Support\Contracts\GraphQLContext $context Arbitrary data that is shared between all fields of a single query.
|
||||
* @param \GraphQL\Type\Definition\ResolveInfo $resolveInfo Information about the query itself, such as the execution state, the field name, path to the field from the root, and more.
|
||||
* @return mixed
|
||||
*/
|
||||
public function __invoke($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
|
||||
{
|
||||
// TODO implement the resolver
|
||||
}
|
||||
}
|
||||
23
vendor/nuwave/lighthouse/src/Console/stubs/field_full.stub
vendored
Normal file
23
vendor/nuwave/lighthouse/src/Console/stubs/field_full.stub
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace DummyNamespace;
|
||||
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
|
||||
|
||||
class DummyClass
|
||||
{
|
||||
/**
|
||||
* Return a value for the field.
|
||||
*
|
||||
* @param @param null $root Always null, since this field has no parent.
|
||||
* @param array<string, mixed> $args The field arguments passed by the client.
|
||||
* @param \Nuwave\Lighthouse\Support\Contracts\GraphQLContext $context Shared between all fields.
|
||||
* @param \GraphQL\Type\Definition\ResolveInfo $resolveInfo Metadata for advanced query resolution.
|
||||
* @return mixed
|
||||
*/
|
||||
public function __invoke($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
|
||||
{
|
||||
// TODO implement the resolver
|
||||
}
|
||||
}
|
||||
15
vendor/nuwave/lighthouse/src/Console/stubs/field_simple.stub
vendored
Normal file
15
vendor/nuwave/lighthouse/src/Console/stubs/field_simple.stub
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace DummyNamespace;
|
||||
|
||||
class DummyClass
|
||||
{
|
||||
/**
|
||||
* @param null $_
|
||||
* @param array<string, mixed> $args
|
||||
*/
|
||||
public function __invoke($_, array $args)
|
||||
{
|
||||
// TODO implement the resolver
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ namespace DummyNamespace;
|
||||
use GraphQL\Type\Definition\ScalarType;
|
||||
|
||||
/**
|
||||
* Read more about scalars here http://webonyx.github.io/graphql-php/type-system/scalar-types/
|
||||
* Read more about scalars here https://webonyx.github.io/graphql-php/type-definitions/scalars
|
||||
*/
|
||||
class DummyClass extends ScalarType
|
||||
{
|
||||
@ -45,7 +45,7 @@ class DummyClass extends ScalarType
|
||||
* }
|
||||
*
|
||||
* @param \GraphQL\Language\AST\Node $valueNode
|
||||
* @param mixed[]|null $variables
|
||||
* @param array<string, mixed>|null $variables
|
||||
* @return mixed
|
||||
*/
|
||||
public function parseLiteral($valueNode, ?array $variables = null)
|
||||
|
||||
@ -5,6 +5,7 @@ namespace DummyNamespace;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use Nuwave\Lighthouse\Schema\TypeRegistry;
|
||||
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
|
||||
|
||||
class DummyClass
|
||||
{
|
||||
@ -15,12 +16,6 @@ class DummyClass
|
||||
*/
|
||||
protected $typeRegistry;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\TypeRegistry $typeRegistry
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(TypeRegistry $typeRegistry)
|
||||
{
|
||||
$this->typeRegistry = $typeRegistry;
|
||||
|
||||
20
vendor/nuwave/lighthouse/src/Console/stubs/validator.stub
vendored
Normal file
20
vendor/nuwave/lighthouse/src/Console/stubs/validator.stub
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace DummyNamespace;
|
||||
|
||||
use Nuwave\Lighthouse\Validation\Validator;
|
||||
|
||||
class DummyClass extends Validator
|
||||
{
|
||||
/**
|
||||
* Return the validation rules.
|
||||
*
|
||||
* @return array<string, array<mixed>>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// TODO Add your validation rules
|
||||
];
|
||||
}
|
||||
}
|
||||
257
vendor/nuwave/lighthouse/src/Defer/Defer.php
vendored
257
vendor/nuwave/lighthouse/src/Defer/Defer.php
vendored
@ -3,14 +3,13 @@
|
||||
namespace Nuwave\Lighthouse\Defer;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
use Illuminate\Support\Arr;
|
||||
use Nuwave\Lighthouse\Events\StartExecution;
|
||||
use Nuwave\Lighthouse\GraphQL;
|
||||
use Nuwave\Lighthouse\Events\ManipulateAST;
|
||||
use Nuwave\Lighthouse\Schema\AST\ASTHelper;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Nuwave\Lighthouse\Schema\AST\PartialParser;
|
||||
use Nuwave\Lighthouse\Support\Contracts\CreatesResponse;
|
||||
use Nuwave\Lighthouse\Support\Contracts\CanStreamResponse;
|
||||
use Nuwave\Lighthouse\Support\Contracts\CreatesResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class Defer implements CreatesResponse
|
||||
{
|
||||
@ -25,161 +24,134 @@ class Defer implements CreatesResponse
|
||||
protected $graphQL;
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
* @var \Nuwave\Lighthouse\Events\StartExecution
|
||||
*/
|
||||
protected $result = [];
|
||||
protected $startExecution;
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
* A map from paths to deferred resolvers.
|
||||
*
|
||||
* @var array<string, \Closure(): mixed>
|
||||
*/
|
||||
protected $deferred = [];
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
* Paths resolved during the current nesting of defers.
|
||||
*
|
||||
* @var array<int, mixed>
|
||||
*/
|
||||
protected $resolved = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* The entire result of resolving the query up until the current nesting.
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected $acceptFurtherDeferring = true;
|
||||
protected $result = [];
|
||||
|
||||
/**
|
||||
* Should further deferring happen?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $shouldDeferFurther = true;
|
||||
|
||||
/**
|
||||
* Are we currently streaming deferred results?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $isStreaming = false;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @var float|int
|
||||
*/
|
||||
protected $maxExecutionTime = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $maxNestedFields = 0;
|
||||
|
||||
/**
|
||||
* @param \Nuwave\Lighthouse\Support\Contracts\CanStreamResponse $stream
|
||||
* @param \Nuwave\Lighthouse\GraphQL $graphQL
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(CanStreamResponse $stream, GraphQL $graphQL)
|
||||
public function __construct(CanStreamResponse $stream, GraphQL $graphQL, ConfigRepository $config)
|
||||
{
|
||||
$this->stream = $stream;
|
||||
$this->graphQL = $graphQL;
|
||||
$this->maxNestedFields = config('lighthouse.defer.max_nested_fields', 0);
|
||||
|
||||
$executionTime = $config->get('lighthouse.defer.max_execution_ms', 0);
|
||||
if ($executionTime > 0) {
|
||||
$this->maxExecutionTime = microtime(true) + $executionTime * 1000;
|
||||
}
|
||||
|
||||
$this->maxNestedFields = $config->get('lighthouse.defer.max_nested_fields', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tracing directive on all fields of the query to enable tracing them.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Events\ManipulateAST $manipulateAST
|
||||
* @return void
|
||||
*/
|
||||
public function handleManipulateAST(ManipulateAST $manipulateAST): void
|
||||
public function handleStartExecution(StartExecution $startExecution): void
|
||||
{
|
||||
ASTHelper::attachDirectiveToObjectTypeFields(
|
||||
$manipulateAST->documentAST,
|
||||
PartialParser::directive('@deferrable')
|
||||
);
|
||||
|
||||
$manipulateAST->documentAST->setDirectiveDefinition(
|
||||
PartialParser::directiveDefinition('
|
||||
"""
|
||||
Use this directive on expensive or slow fields to resolve them asynchronously.
|
||||
Must not be placed upon:
|
||||
- Non-Nullable fields
|
||||
- Mutation root fields
|
||||
"""
|
||||
directive @defer(if: Boolean = true) on FIELD
|
||||
')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isStreaming(): bool
|
||||
{
|
||||
return $this->isStreaming;
|
||||
$this->startExecution = $startExecution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register deferred field.
|
||||
*
|
||||
* @param \Closure $resolver
|
||||
* @param string $path
|
||||
* @return mixed
|
||||
* @param \Closure(): mixed $resolver
|
||||
* @return mixed The data if it is already available.
|
||||
*/
|
||||
public function defer(Closure $resolver, string $path)
|
||||
{
|
||||
if ($data = Arr::get($this->result, "data.{$path}")) {
|
||||
$data = $this->getData($path);
|
||||
if ($data !== null) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ($this->isDeferred($path) || ! $this->acceptFurtherDeferring) {
|
||||
// If we have been here before, now is the time to resolve this field
|
||||
$deferredResolver = $this->deferred[$path] ?? null;
|
||||
if ($deferredResolver) {
|
||||
return $this->resolve($deferredResolver, $path);
|
||||
}
|
||||
|
||||
if (! $this->shouldDeferFurther) {
|
||||
return $this->resolve($resolver, $path);
|
||||
}
|
||||
|
||||
$this->deferred[$path] = $resolver;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Closure $originalResolver
|
||||
* @param string $path
|
||||
* @return mixed
|
||||
* @return mixed The data at the path
|
||||
*/
|
||||
public function findOrResolve(Closure $originalResolver, string $path)
|
||||
protected function getData(string $path)
|
||||
{
|
||||
if (! $this->hasData($path)) {
|
||||
if (isset($this->deferred[$path])) {
|
||||
unset($this->deferred[$path]);
|
||||
}
|
||||
|
||||
return $this->resolve($originalResolver, $path);
|
||||
}
|
||||
|
||||
return Arr::get($this->result, "data.{$path}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve field with data or resolver.
|
||||
*
|
||||
* @param \Closure $originalResolver
|
||||
* @param string $path
|
||||
* @return mixed
|
||||
* @param \Closure(): mixed $resolver
|
||||
* @return mixed The loaded data
|
||||
*/
|
||||
public function resolve(Closure $originalResolver, string $path)
|
||||
protected function resolve(Closure $resolver, string $path)
|
||||
{
|
||||
$isDeferred = $this->isDeferred($path);
|
||||
$resolver = $isDeferred
|
||||
? $this->deferred[$path]
|
||||
: $originalResolver;
|
||||
|
||||
if ($isDeferred) {
|
||||
$this->resolved[] = $path;
|
||||
|
||||
unset($this->deferred[$path]);
|
||||
}
|
||||
unset($this->deferred[$path]);
|
||||
$this->resolved [] = $path;
|
||||
|
||||
return $resolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return bool
|
||||
* @param \Closure(): mixed $originalResolver
|
||||
* @return mixed The loaded data
|
||||
*/
|
||||
public function isDeferred(string $path): bool
|
||||
public function findOrResolve(Closure $originalResolver, string $path)
|
||||
{
|
||||
return isset($this->deferred[$path]);
|
||||
if ($this->hasData($path)) {
|
||||
return $this->getData($path);
|
||||
}
|
||||
|
||||
return $originalResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public function hasData(string $path): bool
|
||||
protected function hasData(string $path): bool
|
||||
{
|
||||
return Arr::has($this->result, "data.{$path}");
|
||||
}
|
||||
@ -187,30 +159,26 @@ directive @defer(if: Boolean = true) on FIELD
|
||||
/**
|
||||
* Return either a final response or a stream of responses.
|
||||
*
|
||||
* @param mixed[] $result
|
||||
* @param array<string, mixed> $result
|
||||
* @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\StreamedResponse
|
||||
*/
|
||||
public function createResponse(array $result): Response
|
||||
{
|
||||
if (empty($this->deferred)) {
|
||||
if (! $this->hasRemainingDeferred()) {
|
||||
return response($result);
|
||||
}
|
||||
|
||||
$this->result = $result;
|
||||
$this->isStreaming = true;
|
||||
|
||||
return response()->stream(
|
||||
function () use ($result): void {
|
||||
function (): void {
|
||||
$this->stream();
|
||||
|
||||
$nested = 1;
|
||||
$this->result = $result;
|
||||
$this->isStreaming = true;
|
||||
$this->stream->stream($result, [], empty($this->deferred));
|
||||
|
||||
if ($executionTime = config('lighthouse.defer.max_execution_ms', 0)) {
|
||||
$this->maxExecutionTime = microtime(true) + ($executionTime * 1000);
|
||||
}
|
||||
|
||||
// TODO: Allow nested_levels to be set in config to break out of loop early.
|
||||
while (
|
||||
count($this->deferred)
|
||||
&& ! $this->executionTimeExpired()
|
||||
$this->hasRemainingDeferred()
|
||||
&& ! $this->maxExecutionTimeReached()
|
||||
&& ! $this->maxNestedFieldsResolved($nested)
|
||||
) {
|
||||
$nested++;
|
||||
@ -218,48 +186,40 @@ directive @defer(if: Boolean = true) on FIELD
|
||||
}
|
||||
|
||||
// We've hit the max execution time or max nested levels of deferred fields.
|
||||
$this->shouldDeferFurther = false;
|
||||
|
||||
// We process remaining deferred fields, but are no longer allowing additional
|
||||
// fields to be deferred.
|
||||
if (count($this->deferred)) {
|
||||
$this->acceptFurtherDeferring = false;
|
||||
if ($this->hasRemainingDeferred()) {
|
||||
$this->executeDeferred();
|
||||
}
|
||||
},
|
||||
200,
|
||||
[
|
||||
// TODO: Allow headers to be set in config
|
||||
'X-Accel-Buffering' => 'no',
|
||||
'Content-Type' => 'multipart/mixed; boundary="-"',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $time
|
||||
* @return void
|
||||
*/
|
||||
public function setMaxExecutionTime(int $time): void
|
||||
protected function hasRemainingDeferred(): bool
|
||||
{
|
||||
$this->maxExecutionTime = $time;
|
||||
return count($this->deferred) > 0;
|
||||
}
|
||||
|
||||
protected function stream(): void
|
||||
{
|
||||
$this->stream->stream(
|
||||
$this->result,
|
||||
$this->resolved,
|
||||
! $this->hasRemainingDeferred()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override max nested fields.
|
||||
*
|
||||
* @param int $max
|
||||
* @return void
|
||||
* Check if we reached the maximum execution time.
|
||||
*/
|
||||
public function setMaxNestedFields(int $max): void
|
||||
{
|
||||
$this->maxNestedFields = $max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the maximum execution time has expired.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function executionTimeExpired(): bool
|
||||
protected function maxExecutionTimeReached(): bool
|
||||
{
|
||||
if ($this->maxExecutionTime === 0) {
|
||||
return false;
|
||||
@ -270,9 +230,6 @@ directive @defer(if: Boolean = true) on FIELD
|
||||
|
||||
/**
|
||||
* Check if the maximum number of nested field has been resolved.
|
||||
*
|
||||
* @param int $nested
|
||||
* @return bool
|
||||
*/
|
||||
protected function maxNestedFieldsResolved(int $nested): bool
|
||||
{
|
||||
@ -280,26 +237,32 @@ directive @defer(if: Boolean = true) on FIELD
|
||||
return false;
|
||||
}
|
||||
|
||||
return $nested >= $this->maxNestedFields;
|
||||
return $this->maxNestedFields <= $nested;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute deferred fields.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function executeDeferred(): void
|
||||
{
|
||||
$this->result = app()->call(
|
||||
[$this->graphQL, 'executeRequest']
|
||||
$executionResult = $this->graphQL->executeQuery(
|
||||
$this->startExecution->query,
|
||||
$this->startExecution->context,
|
||||
$this->startExecution->variables,
|
||||
null,
|
||||
$this->startExecution->operationName
|
||||
);
|
||||
|
||||
$this->stream->stream(
|
||||
$this->result,
|
||||
$this->resolved,
|
||||
empty($this->deferred)
|
||||
);
|
||||
$this->result = $this->graphQL->serializable($executionResult);
|
||||
$this->stream();
|
||||
|
||||
$this->resolved = [];
|
||||
}
|
||||
|
||||
public function setMaxExecutionTime(float $time): void
|
||||
{
|
||||
$this->maxExecutionTime = $time;
|
||||
}
|
||||
|
||||
public function setMaxNestedFields(int $max): void
|
||||
{
|
||||
$this->maxNestedFields = $max;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,43 +2,65 @@
|
||||
|
||||
namespace Nuwave\Lighthouse\Defer;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use GraphQL\Language\Parser;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Nuwave\Lighthouse\Events\ManipulateAST;
|
||||
use Nuwave\Lighthouse\Schema\Factories\DirectiveFactory;
|
||||
use Nuwave\Lighthouse\Events\RegisterDirectiveNamespaces;
|
||||
use Nuwave\Lighthouse\Events\StartExecution;
|
||||
use Nuwave\Lighthouse\Schema\AST\ASTHelper;
|
||||
use Nuwave\Lighthouse\Support\Contracts\CreatesResponse;
|
||||
|
||||
class DeferServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\Factories\DirectiveFactory $directiveFactory
|
||||
* @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
|
||||
* @return void
|
||||
*/
|
||||
public function boot(DirectiveFactory $directiveFactory, Dispatcher $dispatcher): void
|
||||
public function register(): void
|
||||
{
|
||||
$directiveFactory->addResolved(
|
||||
DeferrableDirective::NAME,
|
||||
DeferrableDirective::class
|
||||
$this->app->singleton(Defer::class);
|
||||
$this->app->singleton(CreatesResponse::class, Defer::class);
|
||||
}
|
||||
|
||||
public function boot(Dispatcher $dispatcher): void
|
||||
{
|
||||
$dispatcher->listen(
|
||||
RegisterDirectiveNamespaces::class,
|
||||
static function (): string {
|
||||
return __NAMESPACE__;
|
||||
}
|
||||
);
|
||||
|
||||
$dispatcher->listen(
|
||||
ManipulateAST::class,
|
||||
Defer::class.'@handleManipulateAST'
|
||||
function (ManipulateAST $manipulateAST): void {
|
||||
$this->handleManipulateAST($manipulateAST);
|
||||
}
|
||||
);
|
||||
|
||||
$dispatcher->listen(
|
||||
StartExecution::class,
|
||||
Defer::class.'@handleStartExecution'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
* Set the tracing directive on all fields of the query to enable tracing them.
|
||||
*/
|
||||
public function register(): void
|
||||
public function handleManipulateAST(ManipulateAST $manipulateAST): void
|
||||
{
|
||||
$this->app->singleton(Defer::class);
|
||||
ASTHelper::attachDirectiveToObjectTypeFields(
|
||||
$manipulateAST->documentAST,
|
||||
Parser::constDirective(/** @lang GraphQL */ '@deferrable')
|
||||
);
|
||||
|
||||
$this->app->singleton(CreatesResponse::class, Defer::class);
|
||||
$manipulateAST->documentAST->setDirectiveDefinition(
|
||||
Parser::directiveDefinition(/** @lang GraphQL */ '
|
||||
"""
|
||||
Use this directive on expensive or slow fields to resolve them asynchronously.
|
||||
Must not be placed upon:
|
||||
- Non-Nullable fields
|
||||
- Mutation root fields
|
||||
"""
|
||||
directive @defer(if: Boolean = true) on FIELD
|
||||
')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,31 +4,32 @@ namespace Nuwave\Lighthouse\Defer;
|
||||
|
||||
use Closure;
|
||||
use GraphQL\Error\Error;
|
||||
use GraphQL\Language\AST\TypeNode;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQL\Language\AST\NonNullTypeNode;
|
||||
use Nuwave\Lighthouse\Schema\AST\ASTHelper;
|
||||
use Nuwave\Lighthouse\Schema\Values\FieldValue;
|
||||
use GraphQL\Language\AST\TypeNode;
|
||||
use GraphQL\Type\Definition\Directive;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use Nuwave\Lighthouse\ClientDirectives\ClientDirective;
|
||||
use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
|
||||
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
|
||||
use Nuwave\Lighthouse\Schema\RootType;
|
||||
use Nuwave\Lighthouse\Schema\Values\FieldValue;
|
||||
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
|
||||
use Nuwave\Lighthouse\Support\Contracts\DefinedDirective;
|
||||
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
|
||||
|
||||
class DeferrableDirective extends BaseDirective implements DefinedDirective, FieldMiddleware
|
||||
class DeferrableDirective extends BaseDirective implements FieldMiddleware
|
||||
{
|
||||
const NAME = 'deferrable';
|
||||
const THE_DEFER_DIRECTIVE_CANNOT_BE_USED_ON_A_ROOT_MUTATION_FIELD = 'The @defer directive cannot be used on a root mutation field.';
|
||||
const THE_DEFER_DIRECTIVE_CANNOT_BE_USED_ON_A_NON_NULLABLE_FIELD = 'The @defer directive cannot be used on a Non-Nullable field.';
|
||||
public const THE_DEFER_DIRECTIVE_CANNOT_BE_USED_ON_A_ROOT_MUTATION_FIELD = 'The @defer directive cannot be used on a root mutation field.';
|
||||
public const THE_DEFER_DIRECTIVE_CANNOT_BE_USED_ON_A_NON_NULLABLE_FIELD = 'The @defer directive cannot be used on a Non-Nullable field.';
|
||||
public const DEFER_DIRECTIVE_NAME = 'defer';
|
||||
|
||||
public static function definition(): string
|
||||
{
|
||||
return /* @lang GraphQL */ <<<'SDL'
|
||||
return /** @lang GraphQL */ <<<'GRAPHQL'
|
||||
"""
|
||||
Do not use this directive directly, it is automatically added to the schema
|
||||
when using the defer extension.
|
||||
"""
|
||||
directive @deferrable on FIELD_DEFINITION
|
||||
SDL;
|
||||
GRAPHQL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,32 +37,11 @@ SDL;
|
||||
*/
|
||||
protected $defer;
|
||||
|
||||
/**
|
||||
* @param \Nuwave\Lighthouse\Defer\Defer $defer
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Defer $defer)
|
||||
{
|
||||
$this->defer = $defer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of the directive.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return self::NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the field directive.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\Values\FieldValue $fieldValue
|
||||
* @param \Closure $next
|
||||
* @return \Nuwave\Lighthouse\Schema\Values\FieldValue
|
||||
*/
|
||||
public function handleField(FieldValue $fieldValue, Closure $next): FieldValue
|
||||
{
|
||||
$previousResolver = $fieldValue->getResolver();
|
||||
@ -78,9 +58,7 @@ SDL;
|
||||
return $this->defer->defer($wrappedResolver, $path);
|
||||
}
|
||||
|
||||
return $this->defer->isStreaming()
|
||||
? $this->defer->findOrResolve($wrappedResolver, $path)
|
||||
: $previousResolver($root, $args, $context, $resolveInfo);
|
||||
return $this->defer->findOrResolve($wrappedResolver, $path);
|
||||
}
|
||||
);
|
||||
|
||||
@ -90,49 +68,56 @@ SDL;
|
||||
/**
|
||||
* Determine if field should be deferred.
|
||||
*
|
||||
* @param \GraphQL\Language\AST\TypeNode $fieldType
|
||||
* @param \GraphQL\Type\Definition\ResolveInfo $resolveInfo
|
||||
* @return bool
|
||||
*
|
||||
* @throws \GraphQL\Error\Error
|
||||
*/
|
||||
protected function shouldDefer(TypeNode $fieldType, ResolveInfo $resolveInfo): bool
|
||||
{
|
||||
foreach ($resolveInfo->fieldNodes as $fieldNode) {
|
||||
$deferDirective = ASTHelper::directiveDefinition($fieldNode, 'defer');
|
||||
if (! $deferDirective) {
|
||||
return false;
|
||||
}
|
||||
$defers = (new ClientDirective(self::DEFER_DIRECTIVE_NAME))->forField($resolveInfo);
|
||||
|
||||
if ($resolveInfo->parentType->name === 'Mutation') {
|
||||
if ($this->anyFieldHasDefer($defers)) {
|
||||
if ($resolveInfo->parentType->name === RootType::MUTATION) {
|
||||
throw new Error(self::THE_DEFER_DIRECTIVE_CANNOT_BE_USED_ON_A_ROOT_MUTATION_FIELD);
|
||||
}
|
||||
|
||||
if (! ASTHelper::directiveArgValue($deferDirective, 'if', true)) {
|
||||
return false;
|
||||
if ($fieldType instanceof NonNullTypeNode) {
|
||||
throw new Error(self::THE_DEFER_DIRECTIVE_CANNOT_BE_USED_ON_A_NON_NULLABLE_FIELD);
|
||||
}
|
||||
}
|
||||
|
||||
$skipDirective = ASTHelper::directiveDefinition($fieldNode, 'skip');
|
||||
if (
|
||||
$skipDirective
|
||||
&& ASTHelper::directiveArgValue($skipDirective, 'if') === true
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$includeDirective = ASTHelper::directiveDefinition($fieldNode, 'include');
|
||||
if (
|
||||
$includeDirective
|
||||
&& ASTHelper::directiveArgValue($includeDirective, 'if') === false
|
||||
) {
|
||||
// Following the semantics of Apollo:
|
||||
// All declarations of a field have to contain @defer for the field to be deferred
|
||||
foreach ($defers as $defer) {
|
||||
if ($defer === null || $defer === [Directive::IF_ARGUMENT_NAME => false]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fieldType instanceof NonNullTypeNode) {
|
||||
throw new Error(self::THE_DEFER_DIRECTIVE_CANNOT_BE_USED_ON_A_NON_NULLABLE_FIELD);
|
||||
$skips = (new ClientDirective(Directive::SKIP_NAME))->forField($resolveInfo);
|
||||
foreach ($skips as $skip) {
|
||||
if ($skip === [Directive::IF_ARGUMENT_NAME => true]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
$includes = (new ClientDirective(Directive::INCLUDE_NAME))->forField($resolveInfo);
|
||||
|
||||
return ! in_array(
|
||||
[Directive::IF_ARGUMENT_NAME => false],
|
||||
$includes,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array<string, mixed>|null> $defers
|
||||
*/
|
||||
protected function anyFieldHasDefer(array $defers): bool
|
||||
{
|
||||
foreach ($defers as $defer) {
|
||||
if ($defer !== null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,9 +5,8 @@ namespace Nuwave\Lighthouse\Events;
|
||||
/**
|
||||
* Fires after a query was resolved.
|
||||
*
|
||||
* Listeners of this event may return an instance of
|
||||
* @see \Nuwave\Lighthouse\Execution\ExtensionsResponse
|
||||
* that is then added to the response.
|
||||
* Listeners may return a @see \Nuwave\Lighthouse\Execution\ExtensionsResponse
|
||||
* to include in the response.
|
||||
*/
|
||||
class BuildExtensionsResponse
|
||||
{
|
||||
|
||||
@ -19,12 +19,6 @@ class BuildSchemaString
|
||||
*/
|
||||
public $userSchema;
|
||||
|
||||
/**
|
||||
* BuildSchemaString constructor.
|
||||
*
|
||||
* @param string $userSchema
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $userSchema)
|
||||
{
|
||||
$this->userSchema = $userSchema;
|
||||
|
||||
32
vendor/nuwave/lighthouse/src/Events/EndExecution.php
vendored
Normal file
32
vendor/nuwave/lighthouse/src/Events/EndExecution.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Events;
|
||||
|
||||
use GraphQL\Executor\ExecutionResult;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* Fires after resolving a single operation.
|
||||
*/
|
||||
class EndExecution
|
||||
{
|
||||
/**
|
||||
* The result of resolving a single operation.
|
||||
*
|
||||
* @var \GraphQL\Executor\ExecutionResult
|
||||
*/
|
||||
public $result;
|
||||
|
||||
/**
|
||||
* The point in time when the result was ready.
|
||||
*
|
||||
* @var \Illuminate\Support\Carbon
|
||||
*/
|
||||
public $moment;
|
||||
|
||||
public function __construct(ExecutionResult $result)
|
||||
{
|
||||
$this->result = $result;
|
||||
$this->moment = Carbon::now();
|
||||
}
|
||||
}
|
||||
24
vendor/nuwave/lighthouse/src/Events/EndOperationOrOperations.php
vendored
Normal file
24
vendor/nuwave/lighthouse/src/Events/EndOperationOrOperations.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Events;
|
||||
|
||||
/**
|
||||
* Fires after resolving all operations.
|
||||
*/
|
||||
class EndOperationOrOperations
|
||||
{
|
||||
/**
|
||||
* The result of either a single or multiple operations.
|
||||
*
|
||||
* @var array<string, mixed>|array<int, array<string, mixed>>
|
||||
*/
|
||||
public $resultOrResults;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed>|array<int, array<string, mixed>> $resultOrResults
|
||||
*/
|
||||
public function __construct(array $resultOrResults)
|
||||
{
|
||||
$this->resultOrResults = $resultOrResults;
|
||||
}
|
||||
}
|
||||
37
vendor/nuwave/lighthouse/src/Events/EndRequest.php
vendored
Normal file
37
vendor/nuwave/lighthouse/src/Events/EndRequest.php
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Events;
|
||||
|
||||
use Illuminate\Support\Carbon;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Fires right after building the HTTP response in the GraphQLController.
|
||||
*
|
||||
* Can be used for logging or for measuring and monitoring
|
||||
* the time a request takes to resolve.
|
||||
*
|
||||
* @see \Nuwave\Lighthouse\Support\Http\Controllers\GraphQLController
|
||||
*/
|
||||
class EndRequest
|
||||
{
|
||||
/**
|
||||
* The response that is about to be sent to the client.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public $response;
|
||||
|
||||
/**
|
||||
* The point in time when the response was ready.
|
||||
*
|
||||
* @var \Illuminate\Support\Carbon
|
||||
*/
|
||||
public $moment;
|
||||
|
||||
public function __construct(Response $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
$this->moment = Carbon::now();
|
||||
}
|
||||
}
|
||||
@ -21,12 +21,6 @@ class ManipulateAST
|
||||
*/
|
||||
public $documentAST;
|
||||
|
||||
/**
|
||||
* BuildSchemaString constructor.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\AST\DocumentAST $documentAST
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(DocumentAST &$documentAST)
|
||||
{
|
||||
$this->documentAST = $documentAST;
|
||||
|
||||
@ -19,12 +19,6 @@ class ManipulateResult
|
||||
*/
|
||||
public $result;
|
||||
|
||||
/**
|
||||
* ManipulateResult constructor.
|
||||
*
|
||||
* @param \GraphQL\Executor\ExecutionResult $result
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(ExecutionResult &$result)
|
||||
{
|
||||
$this->result = $result;
|
||||
|
||||
@ -8,7 +8,7 @@ namespace Nuwave\Lighthouse\Events;
|
||||
* Listeners may return one or more strings that are used as the base
|
||||
* namespace for locating directives.
|
||||
*
|
||||
* @see \Nuwave\Lighthouse\Schema\Factories\DirectiveFactory
|
||||
* @see \Nuwave\Lighthouse\Schema\DirectiveLocator::namespaces()
|
||||
*/
|
||||
class RegisterDirectiveNamespaces
|
||||
{
|
||||
|
||||
@ -2,30 +2,61 @@
|
||||
|
||||
namespace Nuwave\Lighthouse\Events;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use GraphQL\Language\AST\DocumentNode;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
|
||||
|
||||
/**
|
||||
* Fires right before resolving an individual query.
|
||||
* Fires right before resolving a single operation.
|
||||
*
|
||||
* Might happen multiple times in a single request if
|
||||
* query batching is used.
|
||||
* Might happen multiple times in a single request if batching is used.
|
||||
*/
|
||||
class StartExecution
|
||||
{
|
||||
/**
|
||||
* The client given parsed query string.
|
||||
*
|
||||
* @var \GraphQL\Language\AST\DocumentNode
|
||||
*/
|
||||
public $query;
|
||||
|
||||
/**
|
||||
* The client given variables, neither validated nor transformed.
|
||||
*
|
||||
* @var array<string, mixed>|null
|
||||
*/
|
||||
public $variables;
|
||||
|
||||
/**
|
||||
* The client given operation name.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $operationName;
|
||||
|
||||
/**
|
||||
* The context for the operation.
|
||||
*
|
||||
* @var \Nuwave\Lighthouse\Support\Contracts\GraphQLContext
|
||||
*/
|
||||
public $context;
|
||||
|
||||
/**
|
||||
* The point in time when the query execution started.
|
||||
*
|
||||
* @var \Carbon\Carbon
|
||||
* @var \Illuminate\Support\Carbon
|
||||
*/
|
||||
public $moment;
|
||||
|
||||
/**
|
||||
* StartRequest constructor.
|
||||
*
|
||||
* @return void
|
||||
* @param array<string, mixed>|null $variables
|
||||
*/
|
||||
public function __construct()
|
||||
public function __construct(DocumentNode $query, ?array $variables, ?string $operationName, GraphQLContext $context)
|
||||
{
|
||||
$this->query = $query;
|
||||
$this->variables = $variables;
|
||||
$this->operationName = $operationName;
|
||||
$this->context = $context;
|
||||
$this->moment = Carbon::now();
|
||||
}
|
||||
}
|
||||
|
||||
24
vendor/nuwave/lighthouse/src/Events/StartOperationOrOperations.php
vendored
Normal file
24
vendor/nuwave/lighthouse/src/Events/StartOperationOrOperations.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Events;
|
||||
|
||||
/**
|
||||
* Fires after receiving the parsed operation (single query) or operations (batched query).
|
||||
*/
|
||||
class StartOperationOrOperations
|
||||
{
|
||||
/**
|
||||
* One or multiple parsed GraphQL operations.
|
||||
*
|
||||
* @var \GraphQL\Server\OperationParams|array<int, \GraphQL\Server\OperationParams>
|
||||
*/
|
||||
public $operationOrOperations;
|
||||
|
||||
/**
|
||||
* @param \GraphQL\Server\OperationParams|array<int, \GraphQL\Server\OperationParams> $operationOrOperations
|
||||
*/
|
||||
public function __construct($operationOrOperations)
|
||||
{
|
||||
$this->operationOrOperations = $operationOrOperations;
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
namespace Nuwave\Lighthouse\Events;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Nuwave\Lighthouse\Execution\GraphQLRequest;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* Fires right after a request reaches the GraphQLController.
|
||||
@ -16,26 +16,20 @@ use Nuwave\Lighthouse\Execution\GraphQLRequest;
|
||||
class StartRequest
|
||||
{
|
||||
/**
|
||||
* GraphQL request instance.
|
||||
* The request sent from the client.
|
||||
*
|
||||
* @var \Nuwave\Lighthouse\Execution\GraphQLRequest
|
||||
* @var \Illuminate\Http\Request
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* The point in time when the request started.
|
||||
*
|
||||
* @var \Carbon\Carbon
|
||||
* @var \Illuminate\Support\Carbon
|
||||
*/
|
||||
public $moment;
|
||||
|
||||
/**
|
||||
* StartRequest constructor.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Execution\GraphQLRequest $request
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(GraphQLRequest $request)
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->moment = Carbon::now();
|
||||
|
||||
25
vendor/nuwave/lighthouse/src/Events/ValidateSchema.php
vendored
Normal file
25
vendor/nuwave/lighthouse/src/Events/ValidateSchema.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Events;
|
||||
|
||||
use GraphQL\Type\Schema;
|
||||
|
||||
/**
|
||||
* Dispatched when php artisan lighthouse:validate-schema is called.
|
||||
*
|
||||
* Listeners should throw a descriptive error if the schema is wrong.
|
||||
*/
|
||||
class ValidateSchema
|
||||
{
|
||||
/**
|
||||
* The final schema to validate.
|
||||
*
|
||||
* @var \GraphQL\Type\Schema
|
||||
*/
|
||||
public $schema;
|
||||
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
$this->schema = $schema;
|
||||
}
|
||||
}
|
||||
@ -6,35 +6,21 @@ use Illuminate\Auth\AuthenticationException as IlluminateAuthenticationException
|
||||
|
||||
class AuthenticationException extends IlluminateAuthenticationException implements RendersErrorsExtensions
|
||||
{
|
||||
/**
|
||||
* Returns true when exception message is safe to be displayed to a client.
|
||||
*
|
||||
* @api
|
||||
* @return bool
|
||||
*/
|
||||
public const MESSAGE = 'Unauthenticated.';
|
||||
public const CATEGORY = 'authentication';
|
||||
|
||||
public function isClientSafe(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string describing a category of the error.
|
||||
*
|
||||
* Value "graphql" is reserved for errors produced by query parsing or validation, do not use it.
|
||||
*
|
||||
* @api
|
||||
* @return string
|
||||
*/
|
||||
public function getCategory(): string
|
||||
{
|
||||
return 'authentication';
|
||||
return self::CATEGORY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the content that is put in the "extensions" part
|
||||
* of the returned error.
|
||||
*
|
||||
* @return array
|
||||
* @return array<string, array<string>>
|
||||
*/
|
||||
public function extensionsContent(): array
|
||||
{
|
||||
|
||||
@ -7,30 +7,13 @@ use Illuminate\Auth\Access\AuthorizationException as IlluminateAuthorizationExce
|
||||
|
||||
class AuthorizationException extends IlluminateAuthorizationException implements ClientAware
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const CATEGORY = 'authorization';
|
||||
public const CATEGORY = 'authorization';
|
||||
|
||||
/**
|
||||
* Returns true when exception message is safe to be displayed to a client.
|
||||
*
|
||||
* @api
|
||||
* @return bool
|
||||
*/
|
||||
public function isClientSafe(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string describing a category of the error.
|
||||
*
|
||||
* Value "graphql" is reserved for errors produced by query parsing or validation, do not use it.
|
||||
*
|
||||
* @api
|
||||
* @return string
|
||||
*/
|
||||
public function getCategory(): string
|
||||
{
|
||||
return self::CATEGORY;
|
||||
|
||||
@ -5,27 +5,18 @@ namespace Nuwave\Lighthouse\Exceptions;
|
||||
use Exception;
|
||||
use GraphQL\Error\ClientAware;
|
||||
|
||||
/**
|
||||
* Thrown when the schema definition or related code is wrong.
|
||||
*
|
||||
* This signals a developer error, so we do not show this exception to the user.
|
||||
*/
|
||||
class DefinitionException extends Exception implements ClientAware
|
||||
{
|
||||
/**
|
||||
* Returns true when exception message is safe to be displayed to a client.
|
||||
*
|
||||
* @api
|
||||
* @return bool
|
||||
*/
|
||||
public function isClientSafe(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string describing a category of the error.
|
||||
*
|
||||
* Value "graphql" is reserved for errors produced by query parsing or validation, do not use it.
|
||||
*
|
||||
* @api
|
||||
* @return string
|
||||
*/
|
||||
public function getCategory(): string
|
||||
{
|
||||
return 'schema';
|
||||
|
||||
@ -7,25 +7,11 @@ use GraphQL\Error\ClientAware;
|
||||
|
||||
class DirectiveException extends Exception implements ClientAware
|
||||
{
|
||||
/**
|
||||
* Returns true when exception message is safe to be displayed to a client.
|
||||
*
|
||||
* @api
|
||||
* @return bool
|
||||
*/
|
||||
public function isClientSafe(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string describing a category of the error.
|
||||
*
|
||||
* Value "graphql" is reserved for errors produced by query parsing or validation, do not use it.
|
||||
*
|
||||
* @api
|
||||
* @return string
|
||||
*/
|
||||
public function getCategory(): string
|
||||
{
|
||||
return 'schema';
|
||||
|
||||
10
vendor/nuwave/lighthouse/src/Exceptions/FederationException.php
vendored
Normal file
10
vendor/nuwave/lighthouse/src/Exceptions/FederationException.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class FederationException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
@ -6,30 +6,20 @@ use GraphQL\Error\Error;
|
||||
|
||||
class GenericException extends Error
|
||||
{
|
||||
/**
|
||||
* The category.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $category = 'generic';
|
||||
|
||||
/**
|
||||
* Set the contents that will be rendered under the "extensions" key of the error response.
|
||||
*
|
||||
* @param mixed $extensions
|
||||
* @param array<string, mixed> $extensions
|
||||
* @return $this
|
||||
*/
|
||||
public function setExtensions($extensions): self
|
||||
public function setExtensions(array $extensions): self
|
||||
{
|
||||
$this->extensions = (array) $extensions;
|
||||
$this->extensions = $extensions;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the category that will be rendered under the "extensions" key of the error response.
|
||||
*
|
||||
* @param string $category
|
||||
* @return $this
|
||||
*/
|
||||
public function setCategory(string $category): self
|
||||
|
||||
@ -4,28 +4,31 @@ namespace Nuwave\Lighthouse\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use GraphQL\Error\ClientAware;
|
||||
use GraphQL\Error\SyntaxError;
|
||||
use GraphQL\Language\Source;
|
||||
|
||||
class ParseException extends Exception implements ClientAware
|
||||
{
|
||||
/**
|
||||
* Returns true when exception message is safe to be displayed to a client.
|
||||
*
|
||||
* @api
|
||||
* @return bool
|
||||
*/
|
||||
public function __construct(SyntaxError $error)
|
||||
{
|
||||
$message = $error->getMessage();
|
||||
|
||||
$source = $error->getSource();
|
||||
$positions = $error->getPositions();
|
||||
if ($source instanceof Source && count($positions) > 0) {
|
||||
$position = $positions[0];
|
||||
|
||||
$message .= ', near: '.\Safe\substr($source->body, max(0, $position - 50), 100);
|
||||
}
|
||||
|
||||
parent::__construct($message);
|
||||
}
|
||||
|
||||
public function isClientSafe(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string describing a category of the error.
|
||||
*
|
||||
* Value "graphql" is reserved for errors produced by query parsing or validation, do not use it.
|
||||
*
|
||||
* @api
|
||||
* @return string
|
||||
*/
|
||||
public function getCategory(): string
|
||||
{
|
||||
return 'schema';
|
||||
|
||||
30
vendor/nuwave/lighthouse/src/Exceptions/RateLimitException.php
vendored
Normal file
30
vendor/nuwave/lighthouse/src/Exceptions/RateLimitException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Exceptions;
|
||||
|
||||
use GraphQL\Error\ClientAware;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Thrown when the user has reached the rate limit for a field.
|
||||
*/
|
||||
class RateLimitException extends RuntimeException implements ClientAware
|
||||
{
|
||||
public const MESSAGE = 'Rate limit exceeded. Please try later.';
|
||||
public const CATEGORY = 'rate-limit';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(self::MESSAGE);
|
||||
}
|
||||
|
||||
public function isClientSafe(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getCategory(): string
|
||||
{
|
||||
return self::CATEGORY;
|
||||
}
|
||||
}
|
||||
@ -16,7 +16,7 @@ interface RendersErrorsExtensions extends ClientAware
|
||||
* Return the content that is put in the "extensions" part
|
||||
* of the returned error.
|
||||
*
|
||||
* @return array
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function extensionsContent(): array;
|
||||
}
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Exceptions;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use GraphQL\Error\ClientAware;
|
||||
|
||||
class SubscriptionException extends InvalidArgumentException implements ClientAware
|
||||
{
|
||||
/**
|
||||
* Returns true when exception message is safe to be displayed to a client.
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClientSafe(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string describing a category of the error.
|
||||
*
|
||||
* Value "graphql" is reserved for errors produced by query parsing or validation, do not use it.
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCategory(): string
|
||||
{
|
||||
return 'subscription';
|
||||
}
|
||||
}
|
||||
16
vendor/nuwave/lighthouse/src/Exceptions/UnknownCacheVersionException.php
vendored
Normal file
16
vendor/nuwave/lighthouse/src/Exceptions/UnknownCacheVersionException.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class UnknownCacheVersionException extends Exception
|
||||
{
|
||||
/**
|
||||
* @param mixed $version Should be int, but could be something else
|
||||
*/
|
||||
public function __construct($version)
|
||||
{
|
||||
parent::__construct("Expected lighthouse.cache.version to be 1 or 2, got: {$version}.");
|
||||
}
|
||||
}
|
||||
@ -2,36 +2,62 @@
|
||||
|
||||
namespace Nuwave\Lighthouse\Exceptions;
|
||||
|
||||
class ValidationException extends \Illuminate\Validation\ValidationException implements RendersErrorsExtensions
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Validation\ValidationException as LaravelValidationException;
|
||||
|
||||
class ValidationException extends Exception implements RendersErrorsExtensions
|
||||
{
|
||||
const CATEGORY = 'validation';
|
||||
|
||||
/**
|
||||
* Returns true when exception message is safe to be displayed to a client.
|
||||
*
|
||||
* @return bool
|
||||
* @var \Illuminate\Contracts\Validation\Validator
|
||||
*/
|
||||
public function isClientSafe()
|
||||
protected $validator;
|
||||
|
||||
public function __construct(string $message, Validator $validator)
|
||||
{
|
||||
parent::__construct($message);
|
||||
|
||||
$this->validator = $validator;
|
||||
}
|
||||
|
||||
public static function fromLaravel(LaravelValidationException $laravelException): self
|
||||
{
|
||||
return new static(
|
||||
$laravelException->getMessage(),
|
||||
$laravelException->validator
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate from a plain array of messages.
|
||||
*
|
||||
* @see \Illuminate\Validation\ValidationException::withMessages()
|
||||
*
|
||||
* @param array<string, string|array<string>> $messages
|
||||
*/
|
||||
public static function withMessages(array $messages): self
|
||||
{
|
||||
return static::fromLaravel(
|
||||
LaravelValidationException::withMessages($messages)
|
||||
);
|
||||
}
|
||||
|
||||
public function isClientSafe(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string describing a category of the error.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCategory()
|
||||
public function getCategory(): string
|
||||
{
|
||||
return 'validation';
|
||||
return self::CATEGORY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the content that is put in the "extensions" part
|
||||
* of the returned error.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function extensionsContent(): array
|
||||
{
|
||||
return ['validation' => $this->errors()];
|
||||
return [
|
||||
self::CATEGORY => $this->validator->errors()->messages(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
209
vendor/nuwave/lighthouse/src/Execution/Arguments/ArgPartitioner.php
vendored
Normal file
209
vendor/nuwave/lighthouse/src/Execution/Arguments/ArgPartitioner.php
vendored
Normal file
@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Execution\Arguments;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphOne;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||
use Nuwave\Lighthouse\Exceptions\DefinitionException;
|
||||
use Nuwave\Lighthouse\Support\Contracts\ArgResolver;
|
||||
use Nuwave\Lighthouse\Support\Utils;
|
||||
use ReflectionClass;
|
||||
use ReflectionNamedType;
|
||||
|
||||
class ArgPartitioner
|
||||
{
|
||||
/**
|
||||
* Partition the arguments into nested and regular.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Execution\Arguments\ArgumentSet $argumentSet
|
||||
* @return array<\Nuwave\Lighthouse\Execution\Arguments\ArgumentSet>
|
||||
*/
|
||||
public static function nestedArgResolvers(ArgumentSet $argumentSet, $root): array
|
||||
{
|
||||
$model = $root instanceof Model
|
||||
? new \ReflectionClass($root)
|
||||
: null;
|
||||
|
||||
foreach ($argumentSet->arguments as $name => $argument) {
|
||||
static::attachNestedArgResolver($name, $argument, $model);
|
||||
}
|
||||
|
||||
return static::partition(
|
||||
$argumentSet,
|
||||
static function (string $name, Argument $argument): bool {
|
||||
return $argument->resolver !== null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all the arguments that correspond to a relation of a certain type on the model.
|
||||
*
|
||||
* For example, if the args input looks like this:
|
||||
*
|
||||
* [
|
||||
* 'name' => 'Ralf',
|
||||
* 'comments' =>
|
||||
* ['foo' => 'Bar'],
|
||||
* ]
|
||||
*
|
||||
* and the model has a method "comments" that returns a HasMany relationship,
|
||||
* the result will be:
|
||||
* [
|
||||
* [
|
||||
* 'comments' =>
|
||||
* ['foo' => 'Bar'],
|
||||
* ],
|
||||
* [
|
||||
* 'name' => 'Ralf',
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Execution\Arguments\ArgumentSet $argumentSet
|
||||
* @return array{0: \Nuwave\Lighthouse\Execution\Arguments\ArgumentSet, 1: \Nuwave\Lighthouse\Execution\Arguments\ArgumentSet}
|
||||
*/
|
||||
public static function relationMethods(
|
||||
ArgumentSet $argumentSet,
|
||||
Model $model,
|
||||
string $relationClass
|
||||
): array {
|
||||
$modelReflection = new ReflectionClass($model);
|
||||
|
||||
[$relations, $remaining] = static::partition(
|
||||
$argumentSet,
|
||||
static function (string $name) use ($modelReflection, $relationClass): bool {
|
||||
return static::methodReturnsRelation($modelReflection, $name, $relationClass);
|
||||
}
|
||||
);
|
||||
|
||||
$nonNullRelations = new ArgumentSet();
|
||||
$nonNullRelations->arguments = array_filter(
|
||||
$relations->arguments,
|
||||
static function (Argument $argument): bool {
|
||||
return null !== $argument->value;
|
||||
}
|
||||
);
|
||||
|
||||
return [$nonNullRelations, $remaining];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a nested argument resolver to an argument.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Execution\Arguments\Argument $argument
|
||||
*/
|
||||
protected static function attachNestedArgResolver(string $name, Argument &$argument, ?ReflectionClass $model): void
|
||||
{
|
||||
$resolverDirective = $argument->directives->first(
|
||||
Utils::instanceofMatcher(ArgResolver::class)
|
||||
);
|
||||
|
||||
if ($resolverDirective) {
|
||||
$argument->resolver = $resolverDirective;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($model)) {
|
||||
$isRelation = static function (string $relationClass) use ($model, $name): bool {
|
||||
return static::methodReturnsRelation($model, $name, $relationClass);
|
||||
};
|
||||
|
||||
if (
|
||||
$isRelation(HasOne::class)
|
||||
|| $isRelation(MorphOne::class)
|
||||
) {
|
||||
$argument->resolver = new ResolveNested(new NestedOneToOne($name));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
$isRelation(HasMany::class)
|
||||
|| $isRelation(MorphMany::class)
|
||||
) {
|
||||
$argument->resolver = new ResolveNested(new NestedOneToMany($name));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
$isRelation(BelongsToMany::class)
|
||||
|| $isRelation(MorphToMany::class)
|
||||
) {
|
||||
$argument->resolver = new ResolveNested(new NestedManyToMany($name));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Partition arguments based on a predicate.
|
||||
*
|
||||
* The predicate will be called for each argument within the ArgumentSet
|
||||
* with the following parameters:
|
||||
* 1. The name of the argument
|
||||
* 2. The argument itself
|
||||
*
|
||||
* Returns an array of two new ArgumentSet instances:
|
||||
* - the first one contains all arguments for which the predicate matched
|
||||
* - the second one contains all arguments for which the predicate did not match
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Execution\Arguments\ArgumentSet $argumentSet
|
||||
* @return array{0: \Nuwave\Lighthouse\Execution\Arguments\ArgumentSet, 1: \Nuwave\Lighthouse\Execution\Arguments\ArgumentSet}
|
||||
*/
|
||||
public static function partition(ArgumentSet $argumentSet, \Closure $predicate): array
|
||||
{
|
||||
$matched = new ArgumentSet();
|
||||
$notMatched = new ArgumentSet();
|
||||
|
||||
foreach ($argumentSet->arguments as $name => $argument) {
|
||||
if ($predicate($name, $argument)) {
|
||||
$matched->arguments[$name] = $argument;
|
||||
} else {
|
||||
$notMatched->arguments[$name] = $argument;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
$matched,
|
||||
$notMatched,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a method on the model return a relation of the given class?
|
||||
*/
|
||||
public static function methodReturnsRelation(
|
||||
ReflectionClass $modelReflection,
|
||||
string $name,
|
||||
string $relationClass
|
||||
): bool {
|
||||
if (! $modelReflection->hasMethod($name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$relationMethodCandidate = $modelReflection->getMethod($name);
|
||||
|
||||
$returnType = $relationMethodCandidate->getReturnType();
|
||||
if ($returnType === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $returnType instanceof ReflectionNamedType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! class_exists($returnType->getName())) {
|
||||
throw new DefinitionException('Class '.$returnType->getName().' does not exist, did you forget to import the Eloquent relation class?');
|
||||
}
|
||||
|
||||
return is_a($returnType->getName(), $relationClass, true);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user