Compare commits

..

No commits in common. "1e9b99c7a635cddf4c0561a5bf85ecc426d7927a" and "e7d7d18f77680a6cdbe97359024b4ac261d7a189" have entirely different histories.

25 changed files with 630 additions and 61913 deletions

1
.gitignore vendored
View File

@ -1 +0,0 @@
node_modules

View File

@ -1 +0,0 @@
vendor

View File

@ -1,11 +0,0 @@
{
"printWidth": 120,
"tabWidth": 2,
"singleQuote": true,
"semi": true,
"trailingComma": "es5",
"arrowParens": "always",
"phpVersion": "7.1"
}

17
.vscode/settings.json vendored
View File

@ -1,17 +0,0 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[php]":
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
},
"search.exclude": {
"**/vendor": true,
"**/node_modules": true,
"yarn.lock": true,
"package.json": true,
"tsconfig.json": true,
".*": true,
},
}

View File

@ -17,15 +17,15 @@ class Plugin extends PluginBase
{ {
return [ return [
'settings' => [ 'settings' => [
'label' => 'germanairlinesva.graphql::lang.settings.label', 'label' => 'germanairlinesva.graphql::lang.settings.label',
'description' => 'germanairlinesva.graphql::lang.settings.description', 'description' => 'germanairlinesva.graphql::lang.settings.description',
'category' => 'system::lang.system.categories.cms', 'category' => 'system::lang.system.categories.cms',
'icon' => 'icon-compress', 'icon' => 'icon-compress',
'class' => 'GermanAirlinesVa\Graphql\Models\Settings', 'class' => 'GermanAirlinesVa\Graphql\Models\Settings',
'order' => 1000, 'order' => 1000,
'keywords' => 'graphql', 'keywords' => 'graphql',
'permissions' => ['germanairlinesva.graphql.schemas'], 'permissions' => ['germanairlinesva.graphql.schemas']
], ]
]; ];
} }
@ -48,22 +48,28 @@ class Plugin extends PluginBase
$packages = Config::get($pluginNamespace . '::packages'); $packages = Config::get($pluginNamespace . '::packages');
// Boot each package // Boot each package
foreach ($packages as $name => $options) { foreach ($packages as $name => $options)
{
// Setup the configuration for the package, pulling from this plugin's config // Setup the configuration for the package, pulling from this plugin's config
if (!empty($options['config']) && !empty($options['config_namespace'])) { if (!empty($options['config']) && !empty($options['config_namespace']))
{
Config::set($options['config_namespace'], $options['config']); Config::set($options['config_namespace'], $options['config']);
} }
// Register any Service Providers for the package // Register any Service Providers for the package
if (!empty($options['providers'])) { if (!empty($options['providers']))
foreach ($options['providers'] as $provider) { {
foreach ($options['providers'] as $provider)
{
App::register($provider); App::register($provider);
} }
} }
// Register any Aliases for the package // Register any Aliases for the package
if (!empty($options['aliases'])) { if (!empty($options['aliases']))
foreach ($options['aliases'] as $alias => $path) { {
foreach ($options['aliases'] as $alias => $path)
{
$aliasLoader->alias($alias, $path); $aliasLoader->alias($alias, $path);
} }
} }
@ -72,3 +78,4 @@ class Plugin extends PluginBase
return $packages; return $packages;
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -6,17 +6,19 @@ use Illuminate\Http\Request;
use Nuwave\Lighthouse\Support\Contracts\CreatesContext as LighthouseCreatesContext; use Nuwave\Lighthouse\Support\Contracts\CreatesContext as LighthouseCreatesContext;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext; use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
class CreatesContext implements LighthouseCreatesContext class CreatesContext implements LighthouseCreatesContext
{ {
/**
* Generate GraphQL context. /**
* * Generate GraphQL context.
* @param Request $request *
* * @param Request $request
* @return GraphQLContext *
*/ * @return GraphQLContext
public function generate(Request $request): GraphQLContext */
{ public function generate(Request $request): GraphQLContext
return new SchemaContext($request); {
} return new SchemaContext($request);
}
} }

View File

@ -5,34 +5,42 @@ namespace GermanAirlinesVa\Graphql\Classes;
use Lang; use Lang;
use Cms\Classes\Page; use Cms\Classes\Page;
class Graph extends Page class Graph extends Page
{ {
/**
* @var string The container name associated with the model.
*/
protected $dirName = 'graphs';
/** /**
* @var array The attributes that are mass assignable. * @var string The container name associated with the model.
*/ */
protected $fillable = ['title', 'description', 'markup', 'settings', 'code']; protected $dirName = 'graphs';
/** /**
* @var array The rules to be applied to the data. * @var array The attributes that are mass assignable.
*/ */
public $rules = [ protected $fillable = [
'title' => 'required', 'title',
]; 'description',
'markup',
'settings',
'code'
];
public function getCodeClassParent(): string /**
{ * @var array The rules to be applied to the data.
return GraphCode::class; */
} public $rules = [
'title' => 'required'
];
public function runComponents() public function getCodeClassParent() : string {
{ return GraphCode::class;
foreach ($this->components as $component) {
$component->onRun();
} }
}
public function runComponents()
{
foreach ($this->components as $component) {
$component->onRun();
}
}
} }

View File

@ -8,46 +8,48 @@ use October\Rain\Extension\Extendable;
*/ */
class GraphCode extends CodeBase class GraphCode extends CodeBase
{ {
public $graph;
/** public $graph;
* Creates the object instance.
* @param \GermanAirlinesVa\Graphql\Classes\Graph $graph Specifies the Headstart graph.
* @param null
* @param \GermanAirlinesVa\Graphql\Classes\GraphController $controller Specifies the Graph controller.
*/
public function __construct($graph, $layout, $controller)
{
$this->graph = $graph;
$this->controller = $controller;
Extendable::__construct(); /**
} * Creates the object instance.
* @param \GermanAirlinesVa\Graphql\Classes\Graph $graph Specifies the Headstart graph.
* @param null
* @param \GermanAirlinesVa\Graphql\Classes\GraphController $controller Specifies the Graph controller.
*/
public function __construct($graph, $layout, $controller)
{
$this->graph = $graph;
$this->controller = $controller;
public function __get($name) Extendable::__construct();
{
if (isset($this->graph->components[$name]) || isset($this->layout->components[$name])) {
return $this[$name];
} }
if (($value = $this->graph->{$name}) !== null) { public function __get($name)
return $value; {
if (isset($this->graph->components[$name]) || isset($this->layout->components[$name])) {
return $this[$name];
}
if (($value = $this->graph->{$name}) !== null) {
return $value;
}
if (array_key_exists($name, $this->controller->vars)) {
return $this[$name];
}
return null;
} }
if (array_key_exists($name, $this->controller->vars)) { public function __set($name, $value)
return $this[$name]; {
return $this->graph->{$name} = $value;
} }
return null; public function __isset($name)
} {
return isset($this->graph->{$name});
}
public function __set($name, $value)
{
return $this->graph->{$name} = $value;
}
public function __isset($name)
{
return isset($this->graph->{$name});
}
} }

View File

@ -8,109 +8,111 @@ use Cms\Classes\Controller;
use Cms\Classes\CmsException; use Cms\Classes\CmsException;
use Cms\Classes\ComponentManager; use Cms\Classes\ComponentManager;
class GraphController // extends Controller
class GraphController// extends Controller
{ {
protected $graph;
/** protected $graph;
* @var self Cache of self
*/
protected static $instance;
/** /**
* Creates the controller. * @var self Cache of self
*/ */
public function __construct($graph, $args) protected static $instance;
{
$this->graph = $graph;
$this->router = new GraphRouter($args);
self::$instance = $this;
}
/** /**
* Returns an existing instance of the controller. * Creates the controller.
* If the controller doesn't exists, returns null. */
* @return mixed Returns the controller object or null. public function __construct($graph, $args)
*/ {
public static function getController() $this->graph = $graph;
{ $this->router = new GraphRouter($args);
return self::$instance;
}
public function component($alias) self::$instance = $this;
{
if (isset($this->graph->components[$alias])) {
return $this->graph->components[$alias];
} }
foreach ($this->graph->settings['components'] as $component => $properties) { /**
[$name, $component_alias] = strpos($component, ' ') ? explode(' ', $component) : [$component, $component]; * Returns an existing instance of the controller.
* If the controller doesn't exists, returns null.
* @return mixed Returns the controller object or null.
*/
public static function getController()
{
return self::$instance;
}
if ($component_alias == $alias) { public function component($alias) {
// make component if (isset($this->graph->components[$alias])) {
$manager = ComponentManager::instance(); return $this->graph->components[$alias];
if (!($componentObj = $manager->makeComponent($name, null, $properties))) {
throw new CmsException(
Lang::get('cms::lang.component.not_found', [
'name' => $name,
])
);
} }
$this->setComponentPropertiesFromParams($componentObj, $this->router->getParameters()); foreach ($this->graph->settings['components'] as $component => $properties) {
$componentObj->init(); list($name, $component_alias) = strpos($component, ' ')
? explode(' ', $component)
: [$component, $component];
$componentObj->alias = $alias; if ($component_alias == $alias) {
$this->graph->components[$alias] = $componentObj; // make component
$manager = ComponentManager::instance();
return $componentObj; if (!$componentObj = $manager->makeComponent($name, null, $properties)) {
} throw new CmsException(Lang::get('cms::lang.component.not_found', ['name' => $name]));
} }
}
/** $this->setComponentPropertiesFromParams($componentObj, $this->router->getParameters());
* Sets component property values from partial parameters. $componentObj->init();
* The property values should be defined as {{ param }}.
* @param ComponentBase $component The component object.
* @param array $parameters Specifies the partial parameters.
*/
protected function setComponentPropertiesFromParams($component, $parameters = [])
{
$properties = $component->getProperties();
$routerParameters = $this->router->getParameters();
foreach ($properties as $propertyName => $propertyValue) { $componentObj->alias = $alias;
if (is_array($propertyValue)) { $this->graph->components[$alias] = $componentObj;
continue;
}
$matches = []; return $componentObj;
if (preg_match('/^\{\{([^\}]+)\}\}$/', $propertyValue, $matches)) { }
$paramName = trim($matches[1]);
if (substr($paramName, 0, 1) == ':') {
$routeParamName = substr($paramName, 1);
$newPropertyValue = $routerParameters[$routeParamName] ?? null;
} else {
$newPropertyValue = $parameters[$paramName] ?? null;
} }
$component->setProperty($propertyName, $newPropertyValue);
$component->setExternalPropertyName($propertyName, $paramName);
}
} }
}
/** /**
* Returns a routing parameter. * Sets component property values from partial parameters.
* @param string $name Routing parameter name. * The property values should be defined as {{ param }}.
* @param string $default Default to use if none is found. * @param ComponentBase $component The component object.
* @return string * @param array $parameters Specifies the partial parameters.
*/ */
public function param($name, $default = null) protected function setComponentPropertiesFromParams($component, $parameters = [])
{ {
return $this->router->getParameter($name, $default); $properties = $component->getProperties();
} $routerParameters = $this->router->getParameters();
foreach ($properties as $propertyName => $propertyValue) {
if (is_array($propertyValue)) {
continue;
}
$matches = [];
if (preg_match('/^\{\{([^\}]+)\}\}$/', $propertyValue, $matches)) {
$paramName = trim($matches[1]);
if (substr($paramName, 0, 1) == ':') {
$routeParamName = substr($paramName, 1);
$newPropertyValue = $routerParameters[$routeParamName] ?? null;
}
else {
$newPropertyValue = $parameters[$paramName] ?? null;
}
$component->setProperty($propertyName, $newPropertyValue);
$component->setExternalPropertyName($propertyName, $paramName);
}
}
}
/**
* Returns a routing parameter.
* @param string $name Routing parameter name.
* @param string $default Default to use if none is found.
* @return string
*/
public function param($name, $default = null)
{
return $this->router->getParameter($name, $default);
}
} }

View File

@ -7,35 +7,38 @@ use Cms\Classes\Router;
class GraphRouter //extends Router class GraphRouter //extends Router
{ {
/**
* @var array A list of parameters names and values extracted from the URL pattern and URL string.
*/
protected $parameters = [];
public function __construct($parameters) /**
{ * @var array A list of parameters names and values extracted from the URL pattern and URL string.
$this->parameters = $parameters; */
} protected $parameters = [];
/** public function __construct($parameters)
* Returns the current routing parameters. {
* @return array $this->parameters = $parameters;
*/ }
public function getParameters()
{ /**
return $this->parameters; * Returns the current routing parameters.
} * @return array
*/
/** public function getParameters()
* Returns a routing parameter. {
* @return array return $this->parameters;
*/ }
public function getParameter($name, $default = null)
{
if (isset($this->parameters[$name]) && !empty($this->parameters[$name])) { /**
return $this->parameters[$name]; * Returns a routing parameter.
* @return array
*/
public function getParameter($name, $default = null)
{
if (isset($this->parameters[$name]) && !empty($this->parameters[$name])) {
return $this->parameters[$name];
}
return $default;
} }
return $default;
}
} }

View File

@ -9,12 +9,14 @@ use GermanAirlinesVa\Graphql\Classes\CreatesContext as GraphqlCreatesContext;
use October\Rain\Support\ServiceProvider; use October\Rain\Support\ServiceProvider;
class GraphqlServiceProvider extends ServiceProvider
{ class GraphqlServiceProvider extends ServiceProvider {
public function register()
{ public function register()
$this->app->bind(ProvidesResolver::class, GraphqlProvidesResolver::class); {
$this->app->singleton(CreatesContext::class, GraphqlCreatesContext::class); $this->app->bind(ProvidesResolver::class, GraphqlProvidesResolver::class);
$this->app->singleton(SchemaSourceProvider::class, GraphqlSchemaSourceProvider::class); $this->app->singleton(CreatesContext::class, GraphqlCreatesContext::class);
} $this->app->singleton(SchemaSourceProvider::class, GraphqlSchemaSourceProvider::class);
}
} }

View File

@ -6,32 +6,34 @@ use GraphQL\Type\Definition\ResolveInfo;
use Cms\Classes\CodeParser; use Cms\Classes\CodeParser;
use Nuwave\Lighthouse\Schema\ResolverProvider as LighthouseResolverProvider; use Nuwave\Lighthouse\Schema\ResolverProvider as LighthouseResolverProvider;
class ResolverProvider extends LighthouseResolverProvider
{
/**
* Provide a field resolver in case no resolver directive is defined for a field.
*
* @param \Nuwave\Lighthouse\Schema\Values\FieldValue $fieldValue
* @return \Closure
*/
public function provideResolver(FieldValue $fieldValue): Closure
{
return function ($root, array $args, $context, ResolveInfo $resolveInfo) use ($fieldValue) {
$fieldName = $fieldValue->getFieldName();
// use local graph resolver in code section if existent
if ($graphObj = $context->source->findGraph($fieldName)) {
/* @var $graphObj \GermanAirlinesVa\Graphql\Classes\Graph */
$parser = new CodeParser($graphObj);
$codeObj = $parser->source($graphObj, null, new GraphController($graphObj, $args));
$resolveMethod = 'resolve' . studly_case($fieldName);
if (method_exists($codeObj, $resolveMethod)) { class ResolverProvider extends LighthouseResolverProvider {
return $codeObj->$resolveMethod($root, $args, $context, $resolveInfo);
} /**
} * Provide a field resolver in case no resolver directive is defined for a field.
*
* @param \Nuwave\Lighthouse\Schema\Values\FieldValue $fieldValue
* @return \Closure
*/
public function provideResolver(FieldValue $fieldValue): Closure
{
return function ($root, array $args, $context, ResolveInfo $resolveInfo) use ($fieldValue) {
$fieldName = $fieldValue->getFieldName();
// use local graph resolver in code section if existent
if ($graphObj = $context->source->findGraph($fieldName)) {
/* @var $graphObj \GermanAirlinesVa\Graphql\Classes\Graph */
$parser = new CodeParser($graphObj);
$codeObj = $parser->source($graphObj, null, new GraphController($graphObj, $args));
$resolveMethod = 'resolve' . studly_case($fieldName);
if (method_exists($codeObj, $resolveMethod)) {
return $codeObj->$resolveMethod($root, $args, $context, $resolveInfo);
}
}
// default resolver
return parent::provideResolver($fieldValue)($root, $args, $context, $resolveInfo);
};
}
// default resolver
return parent::provideResolver($fieldValue)($root, $args, $context, $resolveInfo);
};
}
} }

View File

@ -6,37 +6,39 @@ use Cms\Classes\Theme;
class Schema extends Theme class Schema extends Theme
{ {
/**
* Returns the absolute theme path. /**
* @param string $dirName Optional theme directory. Defaults to $this->getDirName() * Returns the absolute theme path.
* @return string * @param string $dirName Optional theme directory. Defaults to $this->getDirName()
*/ * @return string
public function getPath(?string $dirName = null): string */
{ public function getPath(?string $dirName = null): string
if (!$dirName) { {
$dirName = $this->getDirName(); if (!$dirName) {
$dirName = $this->getDirName();
}
return base_path() . '/' . $dirName;
} }
return base_path() . '/' . $dirName;
}
/** /**
* Returns a list of graphs in the template. * Returns a list of graphs in the template.
* @param boolean $skipCache Indicates if the pages should be reloaded from the disk bypassing the cache. * @param boolean $skipCache Indicates if the pages should be reloaded from the disk bypassing the cache.
* @return array Returns an array of Nocio\Headstart\Classes\Graph objects. * @return array Returns an array of Nocio\Headstart\Classes\Graph objects.
*/ */
public function listGraphs($skipCache = false) public function listGraphs($skipCache = false)
{ {
return Graph::listInTheme($this, $skipCache); return Graph::listInTheme($this, $skipCache);
} }
/**
* Returns the active theme code.
*
* @return string
* If the theme doesn't exist, returns null.
*/
public static function getActiveThemeCode(): ?string
{
return 'graphql';
}
/**
* Returns the active theme code.
*
* @return string
* If the theme doesn't exist, returns null.
*/
public static function getActiveThemeCode(): ?string
{
return 'graphql';
}
} }

View File

@ -7,20 +7,24 @@ use Nuwave\Lighthouse\Schema\Context;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Nuwave\Lighthouse\Schema\Source\SchemaSourceProvider; use Nuwave\Lighthouse\Schema\Source\SchemaSourceProvider;
class SchemaContext extends Context class SchemaContext extends Context
{ {
public $source;
/** public $source;
* Create new context.
* /**
* @param Request $request * Create new context.
* @param $source *
*/ * @param Request $request
public function __construct(Request $request) * @param $source
{ */
parent::__construct($request); public function __construct(Request $request)
{
parent::__construct($request);
$this->source = App::make(SchemaSourceProvider::class);
}
$this->source = App::make(SchemaSourceProvider::class);
}
} }

View File

@ -11,64 +11,66 @@ use Cms\Classes\ComponentPartial;
class SchemaSourceProvider implements LighthouseSchemaSourceProvider class SchemaSourceProvider implements LighthouseSchemaSourceProvider
{ {
protected $fieldGraphMapCacheKey = 'graphql-schema-field-graph-map';
/** protected $fieldGraphMapCacheKey = 'graphql-schema-field-graph-map';
* @var Schema
*/
public $template;
/** /**
* @var string * @var Schema
*/ */
protected $rootSchemaPath; public $template;
/** /**
* @var array * @var string
* Collects graphs and their parsed schema */
*/ protected $rootSchemaPath;
protected $graphMap;
/** /**
* @var array * @var array
* Maps field names to graphs * Collects graphs and their parsed schema
*/ */
protected $fieldGraphMap; protected $graphMap;
/** /**
* SchemaSource constructor. * @var array
* * Maps field names to graphs
* @param string|null $template */
*/ protected $fieldGraphMap;
public function __construct($template = null)
{
$this->template = Schema::load(is_string($template) ? $template : 'graphql');
$this->rootSchemaPath = $this->template->getPath();
}
/**
* Set schema root path.
*
* @param string $path
*
* @return SchemaSourceProvider
*/
public function setRootPath(string $path): self
{
$this->rootSchemaPath = $path;
return $this; /**
} * SchemaSource constructor.
*
* @param string|null $template
*/
public function __construct($template = null)
{
$this->template = Schema::load(is_string($template) ? $template : 'graphql');
$this->rootSchemaPath = $this->template->getPath();
}
/** /**
* Stitch together schema documents and return the result as a string. * Set schema root path.
* *
* @return string * @param string $path
*/ *
public function getSchemaString(): string * @return SchemaSourceProvider
{ */
// root types public function setRootPath(string $path): self
$schema = ' {
$this->rootSchemaPath = $path;
return $this;
}
/**
* Stitch together schema documents and return the result as a string.
*
* @return string
*/
public function getSchemaString(): string
{
// root types
$schema = '
type Query { type Query {
dummy: Boolean dummy: Boolean
} }
@ -77,141 +79,143 @@ class SchemaSourceProvider implements LighthouseSchemaSourceProvider
dummy: Boolean dummy: Boolean
} }
'; ';
// schema // schema
$schema .= collect($this->getGraphMap())->implode('schema', ''); $schema .= collect($this->getGraphMap())->implode('schema', '');
return $schema; return $schema;
}
public function findGraph($fieldName, $findOrFail = false)
{
if (!isset($this->getFieldGraphMap()[$fieldName])) {
if ($findOrFail) {
throw new Error("Could not find graph of field '{$fieldName}'.");
}
return null;
} }
$name = $this->getFieldGraphMap()[$fieldName]; public function findGraph($fieldName, $findOrFail=false) {
if (!isset($this->getFieldGraphMap()[$fieldName])) {
if (isset($this->graphMap[$name])) { if ($findOrFail) {
// if available, use already instantiated graph objects in memory throw new Error("Could not find graph of field '{$fieldName}'.");
return $this->graphMap[$name]['graph'];
}
return Graph::loadCached($this->template, $name);
}
public function getFieldGraphMap()
{
if (!is_null($this->fieldGraphMap)) {
return $this->fieldGraphMap;
}
// restore from cache
$cacheable = true;
if ($cacheable) {
$map = Cache::get($this->fieldGraphMapCacheKey, false);
if ($map && ($map = @unserialize(@base64_decode($map))) && is_array($map)) {
$this->fieldGraphMap = $map;
return $this->fieldGraphMap;
}
}
// rebuild mapping
$this->fieldGraphMap = [];
foreach ($this->getGraphMap() as $key => $element) {
if (empty(trim($element['schema']))) {
continue;
}
// parse the AST to collect fields mapping
$ast = Parser::parse($element['schema'], ['noLocation' => true]);
$fields = collect($ast->definitions)
->filter(function ($node) {
// we only consider the root types since their fields are unique
return $node->name->value == 'Query' || $node->name->value == 'Mutation';
})
->flatMap(function ($node) {
// collect field names
return collect($node->fields)->map(function ($field) {
return $field->name->value;
});
});
// map as [$field_name => $graph_filename]
$this->fieldGraphMap = array_merge($this->fieldGraphMap, array_fill_keys($fields->toArray(), $key));
}
// cache mapping
if ($cacheable) {
Cache::put($this->fieldGraphMapCacheKey, base64_encode(serialize($this->fieldGraphMap)), 10);
}
return $this->fieldGraphMap;
}
public function getGraphMap()
{
if (!is_null($this->graphMap)) {
return $this->graphMap;
}
$this->graphMap = [];
$component_re = '/{%\s*component\s+["|\'](.*)["|\']\s*%}/m';
$component_str = [$this, 'getComponentSchemaString'];
foreach ($this->template->listGraphs() as $graph) {
/* @var $graph \GermanAirlinesVa\Graphql\Classes\Graph */
$markup = $graph->markup;
$schema = preg_replace_callback(
$component_re,
function (array $matches) use ($graph, $component_str) {
$matched_alias = $matches[1];
foreach ($graph->settings['components'] as $component => $properties) {
// find component by alias
[$name, $alias] = strpos($component, ' ') ? explode(' ', $component) : [$component, $component];
if ($alias == $matched_alias) {
// resolve component schema
return $component_str($name, $alias);
} }
}
// if not found, remove
return '';
},
$markup
);
$this->graphMap[$graph->getFileName()] = [ return null;
'graph' => $graph, }
'schema' => $schema,
]; $name = $this->getFieldGraphMap()[$fieldName];
if (isset($this->graphMap[$name])) {
// if available, use already instantiated graph objects in memory
return $this->graphMap[$name]['graph'];
}
return Graph::loadCached($this->template, $name);
} }
return $this->graphMap; public function getFieldGraphMap() {
} if (! is_null($this->fieldGraphMap)) {
return $this->fieldGraphMap;
}
public function getComponentSchemaString($componentName, $alias) // restore from cache
{
$manager = ComponentManager::instance();
$componentObj = $manager->makeComponent($componentName);
if ($partial = ComponentPartial::load($componentObj, 'schema.graphqls')) {
$content = $partial->getContent();
$content = str_replace('__SELF__', $alias, $content);
return $content; $cacheable = true;
} else {
return "# {$componentName} does not provide a default schema definition"; if ($cacheable) {
$map = Cache::get($this->fieldGraphMapCacheKey, false);
if (
$map &&
($map = @unserialize(@base64_decode($map))) &&
is_array($map)
) {
$this->fieldGraphMap = $map;
return $this->fieldGraphMap;
}
}
// rebuild mapping
$this->fieldGraphMap = [];
foreach ($this->getGraphMap() as $key => $element) {
if (empty(trim($element['schema']))) {
continue;
}
// parse the AST to collect fields mapping
$ast = Parser::parse($element['schema'], ['noLocation' => true]);
$fields = collect($ast->definitions)
->filter(function ($node) {
// we only consider the root types since their fields are unique
return $node->name->value == 'Query' || $node->name->value == 'Mutation';
})
->flatMap(function ($node) {
// collect field names
return collect($node->fields)->map(function($field) {
return $field->name->value;
});
});
// map as [$field_name => $graph_filename]
$this->fieldGraphMap = array_merge($this->fieldGraphMap, array_fill_keys($fields->toArray(), $key));
}
// cache mapping
if ($cacheable) {
Cache::put(
$this->fieldGraphMapCacheKey,
base64_encode(serialize($this->fieldGraphMap)),
10
);
}
return $this->fieldGraphMap;
}
public function getGraphMap() {
if (!is_null($this->graphMap)) {
return $this->graphMap;
}
$this->graphMap = [];
$component_re = '/{%\s*component\s+["|\'](.*)["|\']\s*%}/m';
$component_str = [$this, 'getComponentSchemaString'];
foreach ($this->template->listGraphs() as $graph) {
/* @var $graph \GermanAirlinesVa\Graphql\Classes\Graph */
$markup = $graph->markup;
$schema = preg_replace_callback($component_re, function(array $matches) use ($graph, $component_str) {
$matched_alias = $matches[1];
foreach ($graph->settings['components'] as $component => $properties) {
// find component by alias
list($name, $alias) = strpos($component, ' ')
? explode(' ', $component)
: [$component, $component];
if ($alias == $matched_alias) {
// resolve component schema
return $component_str($name, $alias);
}
}
// if not found, remove
return '';
}, $markup);
$this->graphMap[$graph->getFileName()] = [
'graph' => $graph,
'schema' => $schema
];
}
return $this->graphMap;
}
public function getComponentSchemaString($componentName, $alias) {
$manager = ComponentManager::instance();
$componentObj = $manager->makeComponent($componentName);
if ($partial = ComponentPartial::load($componentObj, 'schema.graphqls')) {
$content = $partial->getContent();
$content = str_replace('__SELF__', $alias, $content);
return $content;
} else {
return "# {$componentName} does not provide a default schema definition";
}
}
public function clearCache() {
Cache::forget($this->fieldGraphMapCacheKey);
} }
}
public function clearCache()
{
Cache::forget($this->fieldGraphMapCacheKey);
}
} }

View File

@ -4,13 +4,17 @@ use Cms\Classes\Theme;
use GermanAirlinesVa\Graphql\Models\Settings; use GermanAirlinesVa\Graphql\Models\Settings;
return [ return [
'packages' => [ 'packages' => [
'nuwave/lighthouse' => [ 'nuwave/lighthouse' => [
'config_namespace' => 'lighthouse', 'config_namespace' => 'lighthouse',
'providers' => ['Nuwave\Lighthouse\LighthouseServiceProvider'], 'providers' => [
'aliases' => [], 'Nuwave\Lighthouse\LighthouseServiceProvider'
'config' => [ ],
/* 'aliases' => [
],
'config' => [
/*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Route Configuration | Route Configuration
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -21,27 +25,27 @@ return [
| |
*/ */
'route' => [ 'route' => [
/* /*
* The URI the endpoint responds to, e.g. mydomain.com/graphql. * The URI the endpoint responds to, e.g. mydomain.com/graphql.
*/ */
'uri' => 'graphql', 'uri' => 'graphql',
/* /*
* Lighthouse creates a named route for convenient URL generation and redirects. * Lighthouse creates a named route for convenient URL generation and redirects.
*/ */
'name' => 'graphql', 'name' => 'graphql',
/* /*
* *
* Beware that middleware defined here runs before the GraphQL execution phase, * Beware that middleware defined here runs before the GraphQL execution phase,
* so you have to take extra care to return spec-compliant error responses. * so you have to take extra care to return spec-compliant error responses.
* To apply middleware on a field level, use the @middleware directive. * To apply middleware on a field level, use the @middleware directive.
*/ */
'middleware' => [\Nuwave\Lighthouse\Support\Http\Middleware\AcceptJson::class], 'middleware' => [\Nuwave\Lighthouse\Support\Http\Middleware\AcceptJson::class]
], ],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Schema Declaration | Schema Declaration
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -52,11 +56,11 @@ return [
| |
*/ */
'schema' => [ 'schema' => [
'register' => '', 'register' => '',
], ],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Schema Cache | Schema Cache
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -67,13 +71,13 @@ return [
| |
*/ */
'cache' => [ 'cache' => [
'enable' => Settings::get('enable_cache', false), 'enable' => Settings::get('enable_cache', false),
'key' => env('GRAPHQL_CACHE_KEY', 'graphql-schema'), 'key' => env('GRAPHQL_CACHE_KEY', 'graphql-schema'),
'ttl' => env('GRAPHQL_CACHE_TTL', null), 'ttl' => env('GRAPHQL_CACHE_TTL', null),
], ],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Namespaces | Namespaces
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -84,18 +88,18 @@ return [
| |
*/ */
'namespaces' => [ 'namespaces' => [
'models' => 'Graphql\\Models', 'models' => 'Graphql\\Models',
'queries' => 'Graphql\\Queries', 'queries' => 'Graphql\\Queries',
'mutations' => 'Graphql\\Mutations', 'mutations' => 'Graphql\\Mutations',
'subscriptions' => 'Graphql\\Subscriptions', 'subscriptions' => 'Graphql\\Subscriptions',
'interfaces' => 'Graphql\\Interfaces', 'interfaces' => 'Graphql\\Interfaces',
'unions' => 'Graphql\\Unions', 'unions' => 'Graphql\\Unions',
'scalars' => 'Graphql\\Scalars', 'scalars' => 'Graphql\\Scalars',
'directives' => ['GermanAirlinesVa\\Graphql\\GraphQL\\Directives', 'Graphql\\Directives'], 'directives' => ['GermanAirlinesVa\\Graphql\\GraphQL\\Directives', 'Graphql\\Directives'],
], ],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Security | Security
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -105,13 +109,13 @@ return [
| |
*/ */
'security' => [ 'security' => [
'max_query_complexity' => \GraphQL\Validator\Rules\QueryComplexity::DISABLED, 'max_query_complexity' => \GraphQL\Validator\Rules\QueryComplexity::DISABLED,
'max_query_depth' => \GraphQL\Validator\Rules\QueryDepth::DISABLED, 'max_query_depth' => \GraphQL\Validator\Rules\QueryDepth::DISABLED,
'disable_introspection' => \GraphQL\Validator\Rules\DisableIntrospection::DISABLED, 'disable_introspection' => \GraphQL\Validator\Rules\DisableIntrospection::DISABLED,
], ],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Pagination | Pagination
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -122,9 +126,9 @@ return [
| |
*/ */
'paginate_max_count' => null, 'paginate_max_count' => null,
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Pagination Amount Argument | Pagination Amount Argument
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -135,9 +139,9 @@ return [
| |
*/ */
'pagination_amount_argument' => 'first', 'pagination_amount_argument' => 'first',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Debug | Debug
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -147,9 +151,9 @@ return [
| |
*/ */
'debug' => \GraphQL\Error\Debug::INCLUDE_DEBUG_MESSAGE | \GraphQL\Error\Debug::INCLUDE_TRACE, 'debug' => \GraphQL\Error\Debug::INCLUDE_DEBUG_MESSAGE | \GraphQL\Error\Debug::INCLUDE_TRACE,
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Error Handlers | Error Handlers
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -160,9 +164,11 @@ return [
| |
*/ */
'error_handlers' => [\Nuwave\Lighthouse\Execution\ExtensionErrorHandler::class], 'error_handlers' => [
\Nuwave\Lighthouse\Execution\ExtensionErrorHandler::class,
],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Global ID | Global ID
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -172,9 +178,9 @@ return [
| |
*/ */
'global_id_field' => 'id', 'global_id_field' => 'id',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Batched Queries | Batched Queries
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -184,9 +190,9 @@ return [
| |
*/ */
'batched_queries' => Settings::get('batched_queries', true), 'batched_queries' => Settings::get('batched_queries', true),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Transactional Mutations | Transactional Mutations
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -196,9 +202,9 @@ return [
| |
*/ */
'transactional_mutations' => true, 'transactional_mutations' => true,
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| GraphQL Subscriptions | GraphQL Subscriptions
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -208,39 +214,39 @@ return [
| |
*/ */
'subscriptions' => [ 'subscriptions' => [
/* /*
* Determines if broadcasts should be queued by default. * Determines if broadcasts should be queued by default.
*/ */
'queue_broadcasts' => env('LIGHTHOUSE_QUEUE_BROADCASTS', true), 'queue_broadcasts' => env('LIGHTHOUSE_QUEUE_BROADCASTS', true),
/* /*
* Default subscription storage. * Default subscription storage.
* *
* Any Laravel supported cache driver options are available here. * Any Laravel supported cache driver options are available here.
*/ */
'storage' => env('LIGHTHOUSE_SUBSCRIPTION_STORAGE', 'redis'), 'storage' => env('LIGHTHOUSE_SUBSCRIPTION_STORAGE', 'redis'),
/* /*
* Default subscription broadcaster. * Default subscription broadcaster.
*/ */
'broadcaster' => env('LIGHTHOUSE_BROADCASTER', 'pusher'), 'broadcaster' => env('LIGHTHOUSE_BROADCASTER', 'pusher'),
/* /*
* Subscription broadcasting drivers with config options. * Subscription broadcasting drivers with config options.
*/ */
'broadcasters' => [ 'broadcasters' => [
'log' => [ 'log' => [
'driver' => 'log', 'driver' => 'log',
], ],
'pusher' => [ 'pusher' => [
'driver' => 'pusher', 'driver' => 'pusher',
'routes' => \Nuwave\Lighthouse\Subscriptions\SubscriptionRouter::class . '@pusher', 'routes' => \Nuwave\Lighthouse\Subscriptions\SubscriptionRouter::class.'@pusher',
'connection' => 'pusher', 'connection' => 'pusher',
], ],
], ],
], ],
], ]
], ]
], ]
]; ];

View File

@ -1,18 +0,0 @@
<?php namespace GermanAirlinesVa\Graphql\Controllers;
use Backend\Classes\Controller;
use BackendMenu;
class Playground extends Controller
{
public function __construct()
{
parent::__construct();
BackendMenu::setContext('GermanAirlinesVa.Graphql', 'menu', 'playground');
}
public function index()
{
}
}

View File

@ -1,2 +0,0 @@
<div id="mount"></div>
<script type="text/javascript" src="/plugins/germanairlinesva/graphql/assets/js/graphiql.js"></script>

View File

@ -1,16 +1,16 @@
<?php <?php
if (!function_exists('join_paths')) { if(!function_exists("join_paths")) {
function join_paths() function join_paths()
{ {
$paths = []; $paths = array();
foreach (func_get_args() as $arg) { foreach (func_get_args() as $arg) {
if ($arg !== '') { if ($arg !== '') {
$paths[] = $arg; $paths[] = $arg;
} }
}
return preg_replace('#/+#', '/', join('/', $paths));
} }
return preg_replace('#/+#', '/', join('/', $paths));
}
} }

View File

@ -1,33 +1,31 @@
<?php return [ <?php return [
'plugin' => [ 'plugin' => [
'name' => 'GraphQL', 'name' => 'GraphQL',
'description' => '', 'description' => '',
],
'menu' => [
'playground' => 'GraphiQL',
],
'permission' => [
'tab' => [
'schemas' => 'Schemas',
], ],
'label' => [ 'menu' => [
'schemas' => 'Manage schemas', 'main' => 'GraphQL',
], ],
], 'permission' => [
'settings' => [ 'tab' => [
'label' => 'German Airlines VA - GraphQL', 'schemas' => 'Schemas',
'description' => 'Configure GrapQL', ],
'general' => 'General', 'label' => [
'engine' => 'Engine', 'schemas' => 'Manage schemas',
'enable_cache' => [ ],
'label' => 'Enable Schema Cache',
'comment' =>
'A large part of the Schema generation is parsing the various graph definition into an AST. These operations are pretty expensive so it is recommended to enable caching in production mode.',
], ],
'batched_queries' => [ 'settings' => [
'label' => 'Batched Queries', 'label' => 'German Airlines VA - GraphQL',
'comment' => 'description' => 'Configure GrapQL',
'GraphQL query batching means sending multiple queries to the server in one request. You may set this flag to process/deny batched queries.', 'general' => 'General',
], 'engine' => 'Engine',
], 'enable_cache' => [
'label' => 'Enable Schema Cache',
'comment' => 'A large part of the Schema generation is parsing the various graph definition into an AST. These operations are pretty expensive so it is recommended to enable caching in production mode.'
],
'batched_queries' => [
'label' => 'Batched Queries',
'comment' => 'GraphQL query batching means sending multiple queries to the server in one request. You may set this flag to process/deny batched queries.'
]
],
]; ];

View File

@ -3,11 +3,13 @@
use Cms\Classes\Theme; use Cms\Classes\Theme;
use Model; use Model;
class Settings extends Model class Settings extends Model
{ {
public $implement = ['System.Behaviors.SettingsModel']; public $implement = ['System.Behaviors.SettingsModel'];
public $settingsCode = 'germanairlinesva_graphql_settings'; public $settingsCode = 'germanairlinesva_graphql_settings';
public $settingsFields = 'fields.yaml';
public $settingsFields = 'fields.yaml';
} }

View File

@ -1,15 +0,0 @@
{
"name": "graphql",
"version": "1.0.0",
"main": "Plugin.php",
"repository": "https://git.hofmannnet.myhome-server.de/GermanAirlines/GermanAirlinesVA-GraphQL.git",
"author": "German Airlines VA",
"license": "MIT",
"devDependencies": {
"@prettier/plugin-php": "^0.16.3",
"prettier": "^2.3.0"
},
"scripts": {
"format": "prettier --write './**/*.{php,html}'"
}
}

View File

@ -8,8 +8,3 @@ permissions:
schemas: schemas:
tab: 'germanairlinesva.graphql::lang.permission.tab.schemas' tab: 'germanairlinesva.graphql::lang.permission.tab.schemas'
label: 'germanairlinesva.graphql::lang.permission.label.schemas' label: 'germanairlinesva.graphql::lang.permission.label.schemas'
navigation:
menu:
label: 'germanairlinesva.graphql::lang.menu.playground'
url: germanairlinesva/graphql/playground
icon: icon-database

View File

@ -1,52 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@prettier/plugin-php@^0.16.3":
version "0.16.3"
resolved "https://registry.yarnpkg.com/@prettier/plugin-php/-/plugin-php-0.16.3.tgz#74867210079ba3c0c3ae843029d76e25ff0aadf3"
integrity sha512-DNidzeGpP+/wmcCAZNSHxgoAnhEosYG+no4jJRqln19e1o3Okpuir/2JMxb07VCwdG50IWjtNgVwNPVl4uj0Hg==
dependencies:
linguist-languages "^7.5.1"
mem "^8.0.0"
php-parser "3.0.2"
linguist-languages@^7.5.1:
version "7.15.0"
resolved "https://registry.yarnpkg.com/linguist-languages/-/linguist-languages-7.15.0.tgz#a93bed6b93015d8133622cb05da6296890862bfa"
integrity sha512-qkSSNDjDDycZ2Wcw+GziNBB3nNo3ddYUInM/PL8Amgwbd9RQ/BKGj2/1d6mdxKgBFnUqZuaDbkIwkE4KUwwmtQ==
map-age-cleaner@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a"
integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==
dependencies:
p-defer "^1.0.0"
mem@^8.0.0:
version "8.1.1"
resolved "https://registry.yarnpkg.com/mem/-/mem-8.1.1.tgz#cf118b357c65ab7b7e0817bdf00c8062297c0122"
integrity sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==
dependencies:
map-age-cleaner "^0.1.3"
mimic-fn "^3.1.0"
mimic-fn@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74"
integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==
p-defer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=
php-parser@3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/php-parser/-/php-parser-3.0.2.tgz#a86dbbc110e57378cba71ab4cd9b0d18f3872ac3"
integrity sha512-a7y1+odEGsceLDLpu7oNyspZ0pK8FMWJOoim4/yd82AtnEZNLdCLZ67arnOQZ9K0lHJiSp4/7lVUpGELVxE14w==
prettier@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18"
integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==