Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Improve mock data handling #27

Open
wants to merge 23 commits into
base: pimcore-x-webonyx-features
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b5f0b32
chore: Improve mock data handling
das-peter Jul 12, 2024
10c4abd
chore: Forward element instance to the element resolver to allow furt…
das-peter Jul 19, 2024
945842d
chore: Generalize the element loading / sharing by introducing a trait.
das-peter Jul 19, 2024
0ef5076
chore: Add support for mockup objects in relations.
das-peter Aug 13, 2024
6e5d57e
Apply php-cs-fixer changes
das-peter Aug 13, 2024
f1682e6
fix: Type for checkPermission() has to be string.
das-peter Aug 14, 2024
1b3ca7d
fix: Fix call signature in ElementLoaderTrait.php
das-peter Aug 14, 2024
0848190
chore: More adjustments to work with DefaultMockup.
das-peter Aug 15, 2024
544ec20
Apply php-cs-fixer changes
das-peter Aug 15, 2024
180c459
chore: Completely overhauled Mockup handling - introduced custom Elem…
das-peter Aug 16, 2024
60854eb
Apply php-cs-fixer changes
das-peter Aug 16, 2024
4f99a28
chore: Fix type of getContainerClassDefinition() AbstractModel works …
das-peter Aug 16, 2024
dc102f4
chore: Added context data handling for ElementMockupInterface. This w…
das-peter Aug 16, 2024
f5fd99b
Apply php-cs-fixer changes
das-peter Aug 16, 2024
364a247
chore: More work on ElementMockup handling. Alias has to be used to r…
das-peter Aug 16, 2024
b81b486
Apply php-cs-fixer changes
das-peter Aug 16, 2024
58cc728
chore: Add mockup support for asset srcset
das-peter Aug 19, 2024
bc82cc5
chore: Make mockup element support configurable.
das-peter Aug 19, 2024
d15216f
fix: Remove development artifact.
das-peter Aug 19, 2024
fba3737
fix: Remove development code.
das-peter Aug 19, 2024
d5787b3
chore: Add static cache to WorkspaceHelper::checkPermission. There do…
das-peter Aug 20, 2024
1737eda
Apply php-cs-fixer changes
das-peter Aug 20, 2024
d0dcde1
chore: FilterTotalCount should be resolver is now deferred to optimiz…
das-peter Aug 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions doc/10_GraphQL/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,65 @@ Open the settings and change `request.credentials` to `include`. Otherwise the
`XDEBUG_SESSION` cookie header will get removed by default.

![Settings](../img/graphql/debugging.png)

## Mockup Elements

**!Beware!** It is a quite complex undertaking to implement this correctly, but worth it if you need a high-performance integration of product indexes.

DataHub supports the use of Mockup Objects / Elements as used in Pimcores Ecommerce Framework Bundle IndexService.
In order to use a Mockup Class it has to implement DataHubs own interface: \Pimcore\Bundle\DataHubBundle\Model\ElementMockupInterface

Mockup elements will be resolved differently, operators and or sub-elements are
only resolved if the requested data structure isn't already available in the mockup itself.
As such Mockup Objects have to ensure to return the same output as a non-mocked
query would.

One particular way to do this is to implement a callback in the mockup and use
the passed in GraphQl Execution context to resolve the data,

Example of returning Image Thumbnail Path:

```php
class AssetMockup implements ElementMockupInterface
{
use ElementMockupTrait;

/**
* @return array
*/
public function getFullpath(): ?string
{
// Check if this is a graphQL callback.
$suffix = null;
if ($this->isGraphqlCallback(__FUNCTION__)) {
if ($thumbnail = $this->getGraphQLArguments()['thumbnail'] ?? null) {
$suffix = '_' . $thumbnail;
}
}
return $this->getParam('fullpath' . $suffix) ?? $this->getParam('fullpath') ?? null;
}
}
```

Will use the returned value, or if null is returned the original Asset is used to resolve the value.

The mockup support can be enabled and configured with a configuration entry like this in your `config.yml` file:
```yml
#### DATAHUB MOCKUP ELEMENTS
pimcore_data_hub:
graphql:
mockup_element_support_enabled: true
```

## Permission Check Cache

By default only a single permission check per request is executed.
This is different from previous versions in which the event
`pimcore.datahub.graphql.permission.preCheck` was fired multiple times during the execution.

The new approach limits the performance impact of the permission handling - but can be disabled using following setting:
```yml
pimcore_data_hub:
graphql:
disable_permission_check_cache: true
```
2 changes: 2 additions & 0 deletions src/Controller/WebserviceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ public function webonyxAction(
if (isset($datahubConfig['graphql']) && isset($datahubConfig['graphql']['not_allowed_policy'])) {
PimcoreDataHubBundle::setNotAllowedPolicy($datahubConfig['graphql']['not_allowed_policy']);
}
$context['disable_permission_check_cache'] = !empty($datahubConfig['graphql']['disable_permission_check_cache']);
$context['mockup_element_support_enabled'] = !empty($datahubConfig['graphql']['mockup_element_support_enabled']);

$validators = null;
if ($request->get('novalidate')) {
Expand Down
2 changes: 2 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public function getConfigTreeBuilder()
->scalarNode('not_allowed_policy')->info('throw exception = 1, return null = 2')->defaultValue(2)->end()
->booleanNode('output_cache_enabled')->info('enables output cache for graphql responses. It is disabled by default')->defaultValue(false)->end()
->integerNode('output_cache_lifetime')->info('output cache in seconds. Default is 30 seconds')->defaultValue(30)->end()
->booleanNode('mockup_element_support_enabled')->info('Enable support of mockup elements as used by Ecommerce Framework Bundle IndexService. You have to use your own Mockup class(es) that implement DataHubs ElementMockupInterface')->defaultValue(false)->end()
->booleanNode('disable_permission_check_cache')->info('Disables the permission check static cache. Use only if you have very specific cases in which the permission can change _within_ the same request!')->defaultValue(false)->end()
->booleanNode('allow_introspection')->info('enables introspection for graphql. It is enabled by default')->defaultValue(true)->end()
->booleanNode('run_subrequest_per_query')->info('If enabled a Symfony Sub-Requet is triggered for each query in a multi-query request.')->defaultValue(false)->end()
->end()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function getGraphQlFieldConfig($attribute, Data $fieldDefinition, $class
'name' => $fieldDefinition->getName(),
'type' => $this->getFieldType($fieldDefinition, $class, $container),
'resolve' => function ($value, $args, $context = [], ResolveInfo $resolveInfo = null) use ($fieldDefinition, $attribute) {
$result = Service::resolveValue($value, $fieldDefinition, $attribute, $args);
$result = Service::resolveValue($value, $fieldDefinition, $attribute, $args, !empty($context['mockup_element_support_enabled']));

// The table has no specific definition of columns, so we cannot have a ObjectType in schema for it.
// Just return the data JSON encoded
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public function __construct(Service $graphQlService, $attribute, $fieldDefinitio
*/
public function resolve($value = null, $args = [], $context = [], ResolveInfo $resolveInfo = null)
{
$asset = Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args = []);
$asset = Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args = [], !empty($context['mockup_element_support_enabled']));

if (!$asset) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public function __construct(Service $graphQlService, $attribute, $fieldDefinitio
*/
public function resolve($value = null, $args = [], $context = [], ResolveInfo $resolveInfo = null)
{
$result = Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args);
$result = Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args, !empty($context['mockup_element_support_enabled']));

return $result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public function resolve($value = null, $args = [], $context = [], ResolveInfo $r
return Service::resolveCachedValue($value, $resolveInfo);
}
/** @var $container Hotspotimage */
$container = Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args);
$container = Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args, !empty($context['mockup_element_support_enabled']));
if ($container instanceof \Pimcore\Model\DataObject\Data\Hotspotimage) {
$image = $container->getImage();
if ($image instanceof Asset) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Pimcore\Bundle\DataHubBundle\GraphQL\ElementDescriptor;
use Pimcore\Bundle\DataHubBundle\GraphQL\Service;
use Pimcore\Bundle\DataHubBundle\GraphQL\Traits\ServiceTrait;
use Pimcore\Bundle\DataHubBundle\Model\ElementMockupInterface;
use Pimcore\Bundle\DataHubBundle\WorkspaceHelper;
use Pimcore\Model\DataObject\ClassDefinition;
use Pimcore\Model\DataObject\ClassDefinition\Data;
Expand Down Expand Up @@ -72,9 +73,9 @@ public function __construct(Service $graphQlService, $attribute, $fieldDefinitio
public function resolve($value = null, $args = [], $context = [], ResolveInfo $resolveInfo = null)
{
if ($value instanceof BaseDescriptor) {
$relation = \Pimcore\Bundle\DataHubBundle\GraphQL\Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args);
$relation = \Pimcore\Bundle\DataHubBundle\GraphQL\Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args, !empty($context['mockup_element_support_enabled']));

if ($relation instanceof ElementInterface) {
if ($relation instanceof ElementInterface || $relation instanceof ElementMockupInterface) {
if (!WorkspaceHelper::checkPermission($relation, 'read')) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Pimcore\Bundle\DataHubBundle\GraphQL\ElementDescriptor;
use Pimcore\Bundle\DataHubBundle\GraphQL\Service;
use Pimcore\Bundle\DataHubBundle\GraphQL\Traits\ServiceTrait;
use Pimcore\Bundle\DataHubBundle\Model\ElementMockupInterface;
use Pimcore\Bundle\DataHubBundle\WorkspaceHelper;
use Pimcore\Model\Asset;
use Pimcore\Model\DataObject\ClassDefinition;
Expand Down Expand Up @@ -72,9 +73,9 @@ public function __construct(\Pimcore\Bundle\DataHubBundle\GraphQL\Service $graph
public function resolve($value = null, $args = [], $context = [], ResolveInfo $resolveInfo = null)
{
if ($value instanceof BaseDescriptor) {
$relation = Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args);
$relation = Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args, !empty($context['mockup_element_support_enabled']));

if ($relation instanceof Asset) {
if ($relation instanceof Asset || $relation instanceof ElementMockupInterface) {
if (!WorkspaceHelper::checkPermission($relation, 'read')) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Pimcore\Bundle\DataHubBundle\GraphQL\ElementDescriptor;
use Pimcore\Bundle\DataHubBundle\GraphQL\Service as GraphQlService;
use Pimcore\Bundle\DataHubBundle\GraphQL\Traits\ServiceTrait;
use Pimcore\Bundle\DataHubBundle\Model\ElementMockupInterface;
use Pimcore\Bundle\DataHubBundle\WorkspaceHelper;
use Pimcore\Model\Asset;
use Pimcore\Model\DataObject\ClassDefinition;
Expand Down Expand Up @@ -83,7 +84,7 @@ public function __construct(
public function resolve($value = null, $args = [], $context = [], ResolveInfo $resolveInfo = null)
{
$result = [];
$relations = GraphQlService::resolveValue($value, $this->fieldDefinition, $this->attribute, $args);
$relations = GraphQlService::resolveValue($value, $this->fieldDefinition, $this->attribute, $args, !empty($context['mockup_element_support_enabled']));
if ($relations) {
foreach ($relations as $relation) {
if ($relation instanceof Hotspotimage) {
Expand All @@ -92,7 +93,7 @@ public function resolve($value = null, $args = [], $context = [], ResolveInfo $r
continue;
}

if ($image instanceof Asset) {
if ($image instanceof Asset || $relation instanceof ElementMockupInterface) {
if (!WorkspaceHelper::checkPermission($image, 'read')) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ public function __construct(Service $graphQlService, $attribute, $fieldDefinitio
public function resolve($value = null, $args = [], $context = [], ResolveInfo $resolveInfo = null)
{
$result = [];
$relations = \Pimcore\Bundle\DataHubBundle\GraphQL\Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args);
$relations = \Pimcore\Bundle\DataHubBundle\GraphQL\Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args, !empty($context['mockup_element_support_enabled']));
if ($relations) {
/** @var AbstractElement $relation */
foreach ($relations as $relation) {
if (!WorkspaceHelper::checkPermission($relation, 'read')) {
if (!WorkspaceHelper::checkPermission($relation, 'read', $type)) {
continue;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public function __construct(Service $graphQlService, $attribute, $fieldDefinitio
*/
public function resolve($value = null, $args = [], $context = [], ResolveInfo $resolveInfo = null)
{
$relations = \Pimcore\Bundle\DataHubBundle\GraphQL\Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args);
$relations = \Pimcore\Bundle\DataHubBundle\GraphQL\Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args, !empty($context['mockup_element_support_enabled']));
if ($relations) {
$result = [];
/** @var ElementMetadata $relation */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ public function __construct(Service $graphQlService, $attribute, $fieldDefinitio
public function resolve($value = null, $args = [], $context = [], ResolveInfo $resolveInfo = null)
{
if ($value instanceof BaseDescriptor) {
$relations = Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args);
$relations = Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args, !empty($context['mockup_element_support_enabled']));
if ($relations) {
$result = [];
/** @var $relation AbstractElement */
foreach ($relations as $relation) {
/** @var $relation AbstractElement */
if (!WorkspaceHelper::checkPermission($relation, 'read')) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public function __construct(Service $graphQlService, $attribute, $fieldDefinitio
public function resolve($value = null, $args = [], $context = [], ResolveInfo $resolveInfo = null)
{
$result = [];
$relations = \Pimcore\Bundle\DataHubBundle\GraphQL\Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args);
$relations = \Pimcore\Bundle\DataHubBundle\GraphQL\Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args, !empty($context['mockup_element_support_enabled']));
if ($relations) {
/** @var ObjectMetadata $relation */
foreach ($relations as $relation) {
Expand Down
2 changes: 1 addition & 1 deletion src/GraphQL/DataObjectType/BlockEntryType.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ protected function prepareField(Data $fieldDef, bool $localized = false)
return $resolve($value, $args, $context, $resolveInfo);
}

return $this->graphQlService::resolveValue($value, $this->fieldDefinition, $this->fieldDefinition->getName(), $args);
return $this->graphQlService::resolveValue($value, $this->fieldDefinition, $this->fieldDefinition->getName(), $args, !empty($context['mockup_element_support_enabled']));
};

return $field;
Expand Down
8 changes: 6 additions & 2 deletions src/GraphQL/ElementDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

namespace Pimcore\Bundle\DataHubBundle\GraphQL;

use Pimcore\Bundle\DataHubBundle\Model\ElementMockupInterface;
use Pimcore\Model\Asset;
use Pimcore\Model\DataObject\Concrete;
use Pimcore\Model\Document;
Expand All @@ -25,15 +26,18 @@ class ElementDescriptor extends BaseDescriptor
/**
* @param ElementInterface|null $element
*/
public function __construct(ElementInterface $element = null)
public function __construct(ElementInterface | ElementMockupInterface $element = null)
{
parent::__construct();
if ($element) {
$this->offsetSet('id', $element->getId());
$this->offsetSet('__elementType', \Pimcore\Model\Element\Service::getElementType($element));
$this->offsetSet('__elementSubtype', $element instanceof Concrete ? $element->getClass()->getName() : $element->getType());

if ($element instanceof Concrete) {
if ($element instanceof ElementMockupInterface) {
$this->offsetSet('__elementType', $element->getElementType());
$this->offsetSet('__elementSubtype', $element->getElementType() === 'object' ? $element->getClass()->getName() : $element->getType());
} elseif ($element instanceof Concrete) {
$subtype = $element->getClass()->getName();

$this->offsetSet('__elementType', 'object');
Expand Down
8 changes: 5 additions & 3 deletions src/GraphQL/FieldHelper/AbstractFieldHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
use GraphQL\Language\AST\NodeList;
use GraphQL\Language\AST\SelectionSetNode;
use GraphQL\Type\Definition\ResolveInfo;
use Pimcore\Bundle\DataHubBundle\GraphQL\Traits\ElementLoaderTrait;
use Pimcore\Bundle\DataHubBundle\GraphQL\Traits\ServiceTrait;
use Pimcore\Bundle\DataHubBundle\Model\ElementMockupInterface;
use Pimcore\Model\Element\ElementInterface;

abstract class AbstractFieldHelper
{
use ServiceTrait;
use ServiceTrait, ElementLoaderTrait;

public function __construct()
{
Expand Down Expand Up @@ -118,10 +120,10 @@ public function getArguments(FieldNode $ast)
*/
public function extractData(&$data, $container, $args, $context = [], ResolveInfo $resolveInfo = null)
{
if ($container instanceof ElementInterface) {
if ($container instanceof ElementInterface || $container instanceof ElementMockupInterface) {
// we have to at least add the ID and pass it around even if not requested because we need it internally
// to resolve fields of linked elements (such as asset image and so on)
$data['id'] = $container->getId();
$data = $this->setDataElement($data, $container);
}

$resolveInfoArray = (array)$resolveInfo;
Expand Down
19 changes: 2 additions & 17 deletions src/GraphQL/FieldHelper/AssetFieldHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

use GraphQL\Language\AST\FieldNode;
use GraphQL\Type\Definition\ResolveInfo;
use Pimcore\Bundle\DataHubBundle\GraphQL\Service;
use Pimcore\Model\Asset;
use Pimcore\Model\Asset\Image;
use Pimcore\Model\Asset\Video;
Expand Down Expand Up @@ -128,23 +129,7 @@ public function doExtractData(FieldNode $ast, &$data, $container, $args, $contex
}
}
} else {
if (method_exists($container, $getter)) {
if ($languageArgument) {
if ($ast->alias) {
// defer it
$data[$realName] = function ($source, $args, $context, ResolveInfo $info) use (
$container,
$getter
) {
return $container->$getter($args['language'] ?? null);
};
} else {
$data[$realName] = $container->$getter($languageArgument);
}
} else {
$data[$realName] = $container->$getter();
}
}
Service::resolveContainerGetterData($container, $data, $getter, $resolveInfo, $ast, $languageArgument);
}
}
}
Loading
Loading