Skip to content

Commit

Permalink
Upmerge nodetemplates version 2 changes into version 3-dev
Browse files Browse the repository at this point in the history
This upmerge includes changes until 2.0.1 aswell as the WIP features #58 and #67
  • Loading branch information
mhsdesign committed Jun 24, 2023
2 parents cc9a6ed + 330fa20 commit b6672ec
Show file tree
Hide file tree
Showing 76 changed files with 2,261 additions and 832 deletions.
68 changes: 68 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Tests

on:
push:
branches: [ main, '[0-9]+.[0-9]' ]
pull_request:
branches: [ main, '[0-9]+.[0-9]' ]
workflow_dispatch: # Allow manual triggering on any branch via `gh workflow run tests.yml -r branch-name`

jobs:
build:
env:
FLOW_CONTEXT: Testing
FLOW_PATH_ROOT: ../neos-base-distribution

runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
include:
- php-version: 7.4
neos-version: 7.3
- php-version: 8.1
neos-version: 8.3

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: mbstring, xml, json, zlib, iconv, intl, pdo_sqlite, mysql

- id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
shell: bash

- uses: actions/cache@v2
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-composer-

- name: Prepare Neos distribution
run: |
git clone --depth 1 --branch ${{ matrix.neos-version }} https://github.com/neos/neos-base-distribution.git ${FLOW_PATH_ROOT}
cd ${FLOW_PATH_ROOT}
composer config --no-plugins allow-plugins.neos/composer-plugin true
composer config repositories.tested-package path ../Flowpack.NodeTemplates
composer require --no-update --no-interaction flowpack/nodetemplates:@dev
- name: Install dependencies
run: |
cd ${FLOW_PATH_ROOT}
composer install --no-interaction --no-progress --prefer-dist
- name: Run Unit tests
run: |
cd ${FLOW_PATH_ROOT}
bin/phpunit --colors -c Build/BuildEssentials/PhpUnit/UnitTests.xml Packages/Application/Flowpack.NodeTemplates/Tests/Unit
- name: Run Functional tests
run: |
cd ${FLOW_PATH_ROOT}
bin/phpunit --colors -c Build/BuildEssentials/PhpUnit/FunctionalTests.xml Packages/Application/Flowpack.NodeTemplates/Tests/Functional
91 changes: 90 additions & 1 deletion Classes/Application/Command/NodeTemplateCommandController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

namespace Flowpack\NodeTemplates\Application\Command;

use Flowpack\NodeTemplates\Domain\ExceptionHandling\CaughtExceptions;
use Flowpack\NodeTemplates\Domain\NodeCreation\ToBeCreatedNode;
use Flowpack\NodeTemplates\Domain\NodeTemplateDumper\NodeTemplateDumper;
use Flowpack\NodeTemplates\Domain\TemplateConfiguration\TemplateConfigurationProcessor;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Cli\CommandController;

Expand All @@ -16,6 +19,13 @@ class NodeTemplateCommandController extends CommandController
*/
protected $nodeTemplateDumper;

/**
* @Flow\Inject
* @var TemplateConfigurationProcessor
*/
protected $templateConfigurationProcessor;


/**
* Dump the node tree structure into a NodeTemplate YAML structure.
* References to Nodes and non-primitive property values are commented out in the YAML.
Expand All @@ -28,7 +38,7 @@ public function createFromNodeSubtreeCommand(string $startingNodeId, string $wor
{
// TODO re-enable
throw new \BadMethodCallException('Not implemented.');
$subgraph = $this->contentContextFactory->create([
$subgraph = $this->contextFactory->create([
'workspaceName' => $workspaceName
]);
$node = $subgraph->getNodeByIdentifier($startingNodeId);
Expand All @@ -37,4 +47,83 @@ public function createFromNodeSubtreeCommand(string $startingNodeId, string $wor
}
echo $this->nodeTemplateDumper->createNodeTemplateYamlDumpFromSubtree($node);
}

/**
* Checks if all configured NodeTemplates are valid. E.g no syntax errors in EEL expressions,
* that properties exist on the node type and their types match and other checks.
*
* We process and build all configured NodeType templates. No nodes will be created in the Content Repository.
*
*/
public function validateCommand(): void
{
// TODO re-enable
throw new \BadMethodCallException('Not implemented.');
$templatesChecked = 0;
/**
* nodeTypeNames as index
* @var array<string, array{caughtExceptions: CaughtExceptions, dataWasAccessed: bool}> $faultyNodeTypeTemplates
*/
$faultyNodeTypeTemplates = [];

foreach ($this->nodeTypeManager->getNodeTypes(false) as $nodeType) {
$templateConfiguration = $nodeType->getOptions()['template'] ?? null;
if (!$templateConfiguration) {
continue;
}
$caughtExceptions = CaughtExceptions::create();

$subgraph = $this->contextFactory->create();

$observableEmptyData = new class ([]) extends \ArrayObject
{
public bool $dataWasAccessed = false;
public function offsetExists($key)
{
$this->dataWasAccessed = true;
return false;
}
};

$template = $this->templateConfigurationProcessor->processTemplateConfiguration(
$templateConfiguration,
[
'data' => $observableEmptyData,
'triggeringNode' => $subgraph->getRootNode(),
],
$caughtExceptions
);

$nodeCreation = new NodeCreationService($subgraph);
$nodeCreation->createMutatorCollection($template, ToBeCreatedNode::fromRegular($nodeType), $caughtExceptions);

if ($caughtExceptions->hasExceptions()) {
$faultyNodeTypeTemplates[$nodeType->getName()] = ['caughtExceptions' => $caughtExceptions, 'dataWasAccessed' => $observableEmptyData->dataWasAccessed];
}
$templatesChecked++;
}

if (empty($faultyNodeTypeTemplates)) {
$this->outputLine(sprintf('<success>%d NodeType templates validated.</success>', $templatesChecked));
return;
}

$possiblyFaultyTemplates = count($faultyNodeTypeTemplates);
$this->outputLine(sprintf('<comment>%d of %d NodeType template validated. %d could not be build standalone.</comment>', $templatesChecked - $possiblyFaultyTemplates, $templatesChecked, $possiblyFaultyTemplates));

$this->outputLine();

foreach ($faultyNodeTypeTemplates as $nodeTypeName => ['caughtExceptions' => $caughtExceptions, 'dataWasAccessed' => $dataWasAccessed]) {
if ($dataWasAccessed) {
$this->outputLine(sprintf('<comment>%s</comment> <b>(depends on "data" context)</b>', $nodeTypeName));
} else {
$this->outputLine(sprintf('<error>%s</error>', $nodeTypeName));
}

foreach ($caughtExceptions as $caughtException) {
$this->outputLine(' ' . $caughtException->toMessage());
$this->outputLine();
}
}
}
}
9 changes: 9 additions & 0 deletions Classes/Domain/NodeCreation/InvalidReferenceException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Flowpack\NodeTemplates\Domain\NodeCreation;

class InvalidReferenceException extends \RuntimeException
{
}
7 changes: 7 additions & 0 deletions Classes/Domain/NodeCreation/NodeConstraintException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Flowpack\NodeTemplates\Domain\NodeCreation;

class NodeConstraintException extends \RuntimeException
{
}
43 changes: 27 additions & 16 deletions Classes/Domain/NodeCreation/NodeCreationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,27 @@
use Neos\ContentRepository\Core\SharedModel\Node\ReferenceName;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Property\PropertyMapper;
use Neos\Neos\Ui\NodeCreationHandler\NodeCreationCommands;
use Neos\Neos\Utility\NodeUriPathSegmentGenerator;

class NodeCreationService
{
/**
* @Flow\Inject
* @var NodeUriPathSegmentGenerator
*/
protected $nodeUriPathSegmentGenerator;
private readonly NodeTypeManager $nodeTypeManager;

private readonly NodeUriPathSegmentGenerator $nodeUriPathSegmentGenerator;

private PropertiesHandler $propertiesHandler;

public function __construct(
private readonly ContentSubgraphInterface $subgraph,
private readonly NodeTypeManager $nodeTypeManager
ContentSubgraphInterface $subgraph,
NodeTypeManager $nodeTypeManager,
PropertyMapper $propertyMapper,
NodeUriPathSegmentGenerator $nodeUriPathSegmentGenerator
) {
$this->nodeTypeManager = $nodeTypeManager;
$this->nodeUriPathSegmentGenerator = $nodeUriPathSegmentGenerator;
$this->propertiesHandler = new PropertiesHandler($subgraph, $propertyMapper);
}

/**
Expand Down Expand Up @@ -67,11 +73,11 @@ public function apply(RootTemplate $template, NodeCreationCommands $commands, Ca

return $this->applyTemplateRecursively(
$template->getChildNodes(),
new ToBeCreatedNode(
ToBeCreatedNode::fromRegular(
$commands->first->contentStreamId,
$commands->first->originDimensionSpacePoint,
$commands->first->nodeAggregateId,
$nodeType,
$nodeType
),
$commands->withInitialPropertyValues($initialProperties)->withAdditionalCommands(
...$this->createReferencesCommands(
Expand All @@ -87,16 +93,19 @@ public function apply(RootTemplate $template, NodeCreationCommands $commands, Ca

private function applyTemplateRecursively(Templates $templates, ToBeCreatedNode $parentNode, NodeCreationCommands $commands, CaughtExceptions $caughtExceptions): NodeCreationCommands
{
// `hasAutoCreatedChildNode` actually has a bug; it looks up the NodeName parameter against the raw configuration instead of the transliterated NodeName
// https://github.com/neos/neos-ui/issues/3527
$parentNodesAutoCreatedChildNodes = $parentNode->getNodeType()->getAutoCreatedChildNodes();
foreach ($templates as $template) {
if ($template->getName() && $parentNode->nodeType->hasAutoCreatedChildNode($template->getName())) {
if ($template->getName() && isset($parentNodesAutoCreatedChildNodes[$template->getName()->value])) {
if ($template->getType() !== null) {
$caughtExceptions->add(
CaughtException::fromException(new \RuntimeException(sprintf('Template cant mutate type of auto created child nodes. Got: "%s"', $template->getType()->value), 1685999829307))
);
// we continue processing the node
}

$nodeType = $parentNode->nodeType->getTypeOfAutoCreatedChildNode($template->getName());
$nodeType = $parentNodesAutoCreatedChildNodes[$template->getName()->value];
$propertiesAndReferences = PropertiesAndReferences::createFromArrayAndTypeDeclarations($template->getProperties(), $nodeType);

$commands = $commands->withAdditionalCommands(
Expand Down Expand Up @@ -151,15 +160,15 @@ private function applyTemplateRecursively(Templates $templates, ToBeCreatedNode
continue;
}

if (!$parentNode->nodeType->allowsChildNodeType($nodeType)) {
try {
$parentNode->requireConstraintsImposedByAncestorsAreMet($nodeType);
} catch (NodeConstraintException $nodeConstraintException) {
$caughtExceptions->add(
CaughtException::fromException(new \RuntimeException(sprintf('Node type "%s" is not allowed for child nodes of type %s', $template->getType()->value, $parentNode->nodeType->name->value), 1686417627173))
CaughtException::fromException($nodeConstraintException)
);
continue;
}

// todo maybe check also allowsGrandchildNodeType

$propertiesAndReferences = PropertiesAndReferences::createFromArrayAndTypeDeclarations($template->getProperties(), $nodeType);

$nodeName = $template->getName() ?? NodeName::fromString(uniqid('node-', false));
Expand Down Expand Up @@ -195,7 +204,7 @@ private function applyTemplateRecursively(Templates $templates, ToBeCreatedNode

$commands = $this->applyTemplateRecursively(
$template->getChildNodes(),
$parentNode->withNodeTypeAndNodeAggregateId(
$parentNode->forRegularChildNode(
$nodeType,
$nodeAggregateId
),
Expand Down Expand Up @@ -229,6 +238,8 @@ private function createReferencesCommands(ContentStreamId $contentStreamId, Node
/**
* All document node types get a uri path segmfent; if it is not explicitly set in the properties,
* it should be built based on the title property
*
* @param Template|RootTemplate $template
*/
private function ensureNodeHasUriPathSegment(
NodeType $nodeType,
Expand Down
Loading

0 comments on commit b6672ec

Please sign in to comment.