Skip to content

Commit 88b623e

Browse files
committed
FEATURE: Neos9 support for flow nodeTemplate:validate
Introduced via #58
1 parent 383d614 commit 88b623e

5 files changed

Lines changed: 152 additions & 64 deletions

File tree

Classes/Application/Command/NodeTemplateCommandController.php

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,18 @@
88
use Flowpack\NodeTemplates\Domain\NodeCreation\NodeCreationService;
99
use Flowpack\NodeTemplates\Domain\NodeTemplateDumper\NodeTemplateDumper;
1010
use Flowpack\NodeTemplates\Domain\TemplateConfiguration\TemplateConfigurationProcessor;
11+
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
12+
use Neos\ContentRepository\Core\Projection\ContentGraph\AbsoluteNodePath;
13+
use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath;
14+
use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints;
15+
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
16+
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
17+
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
1118
use Neos\Flow\Annotations as Flow;
1219
use Neos\Flow\Cli\CommandController;
13-
use Neos\Neos\Domain\Service\ContentContext;
20+
use Neos\Neos\Domain\Repository\SiteRepository;
21+
use Neos\Neos\Domain\Service\NodeTypeNameFactory;
22+
use Neos\Neos\Ui\Domain\NodeCreation\NodeCreationCommands;
1423

1524
class NodeTemplateCommandController extends CommandController
1625
{
@@ -32,6 +41,17 @@ class NodeTemplateCommandController extends CommandController
3241
*/
3342
protected $templateConfigurationProcessor;
3443

44+
/**
45+
* @var SiteRepository
46+
* @Flow\Inject
47+
*/
48+
protected $siteRepository;
49+
50+
/**
51+
* @var ContentRepositoryRegistry
52+
* @Flow\Inject
53+
*/
54+
protected $contentRepositoryRegistry;
3555

3656
/**
3757
* Dump the node tree structure into a NodeTemplate YAML structure.
@@ -43,8 +63,8 @@ class NodeTemplateCommandController extends CommandController
4363
*/
4464
public function createFromNodeSubtreeCommand(string $startingNodeId, string $workspaceName = 'live'): void
4565
{
46-
// TODO re-enable
4766
throw new \BadMethodCallException('Not implemented.');
67+
/*
4868
$subgraph = $this->contextFactory->create([
4969
'workspaceName' => $workspaceName
5070
]);
@@ -53,6 +73,7 @@ public function createFromNodeSubtreeCommand(string $startingNodeId, string $wor
5373
throw new \InvalidArgumentException("Node $startingNodeId doesnt exist in workspace $workspaceName.");
5474
}
5575
echo $this->nodeTemplateDumper->createNodeTemplateYamlDumpFromSubtree($node);
76+
*/
5677
}
5778

5879
/**
@@ -62,26 +83,51 @@ public function createFromNodeSubtreeCommand(string $startingNodeId, string $wor
6283
* We process and build all configured NodeType templates. No nodes will be created in the Content Repository.
6384
*
6485
*/
65-
public function validateCommand(): void
86+
public function validateCommand(?string $site = null): void
6687
{
67-
// TODO re-enable
68-
throw new \BadMethodCallException('Not implemented.');
88+
$siteInstance = $site
89+
? $this->siteRepository->findOneByNodeName($site)
90+
: $this->siteRepository->findDefault();
91+
92+
if (!$siteInstance) {
93+
$this->outputLine(sprintf('<error>Site "%s" does not exist.</error>', $site));
94+
$this->quit(2);
95+
}
96+
97+
$siteConfiguration = $siteInstance->getConfiguration();
98+
99+
$contentRepository = $this->contentRepositoryRegistry->get($siteConfiguration->contentRepositoryId);
100+
69101
$templatesChecked = 0;
70102
/**
71103
* nodeTypeNames as index
72104
* @var array<string, array{processingErrors: ProcessingErrors, dataWasAccessed: bool}> $faultyNodeTypeTemplates
73105
*/
74106
$faultyNodeTypeTemplates = [];
75107

76-
foreach ($this->nodeTypeManager->getNodeTypes(false) as $nodeType) {
108+
// default context? https://github.com/neos/neos-development-collection/issues/5113
109+
$subgraph = $contentRepository->getContentGraph(WorkspaceName::forLive())->getSubgraph(
110+
$siteConfiguration->defaultDimensionSpacePoint,
111+
VisibilityConstraints::frontend()
112+
);
113+
114+
$siteNode = $subgraph->findNodeByAbsolutePath(AbsoluteNodePath::fromRootNodeTypeNameAndRelativePath(
115+
NodeTypeNameFactory::forSites(),
116+
NodePath::fromNodeNames($siteInstance->getNodeName()->toNodeName())
117+
));
118+
119+
if (!$siteNode) {
120+
$this->outputLine(sprintf('<error>Could not resolve site node for site "%s".</error>', $siteInstance->getNodeName()->value));
121+
$this->quit(3);
122+
}
123+
124+
foreach ($contentRepository->getNodeTypeManager()->getNodeTypes(false) as $nodeType) {
77125
$templateConfiguration = $nodeType->getOptions()['template'] ?? null;
78126
if (!$templateConfiguration) {
79127
continue;
80128
}
81129
$processingErrors = ProcessingErrors::create();
82130

83-
/** @var ContentContext $subgraph */
84-
$subgraph = $this->contextFactory->create();
85131

86132
$observableEmptyData = new class ([]) extends \ArrayObject
87133
{
@@ -93,27 +139,37 @@ public function offsetExists($key): bool
93139
}
94140
};
95141

96-
$siteNode = $subgraph->getCurrentSiteNode();
97-
98142
$template = $this->templateConfigurationProcessor->processTemplateConfiguration(
99143
$templateConfiguration,
100144
[
101145
'data' => $observableEmptyData,
102-
'triggeringNode' => $siteNode, // @deprecated
103146
'site' => $siteNode,
104147
'parentNode' => $siteNode,
105148
],
106149
$processingErrors
107150
);
108151

109-
$this->nodeCreationService->createMutatorsForRootTemplate($template, $nodeType, $this->nodeTypeManager, $subgraph, $processingErrors);
152+
$fakeNodeCreationCommands = NodeCreationCommands::fromFirstCommand(
153+
CreateNodeAggregateWithNode::create(
154+
$siteNode->workspaceName,
155+
NodeAggregateId::create(),
156+
$nodeType->name,
157+
$siteNode->originDimensionSpacePoint,
158+
$siteNode->aggregateId
159+
),
160+
$contentRepository->getNodeTypeManager()
161+
);
162+
163+
$this->nodeCreationService->apply($template, $fakeNodeCreationCommands, $contentRepository->getNodeTypeManager(), $subgraph, $nodeType, $processingErrors);
110164

111165
if ($processingErrors->hasError()) {
112-
$faultyNodeTypeTemplates[$nodeType->getName()] = ['processingErrors' => $processingErrors, 'dataWasAccessed' => $observableEmptyData->dataWasAccessed];
166+
$faultyNodeTypeTemplates[$nodeType->name->value] = ['processingErrors' => $processingErrors, 'dataWasAccessed' => $observableEmptyData->dataWasAccessed];
113167
}
114168
$templatesChecked++;
115169
}
116170

171+
$this->output(sprintf('<comment>Content repository "%s": </comment>', $contentRepository->id->value));
172+
117173
if ($templatesChecked === 0) {
118174
$this->outputLine('<comment>No NodeType templates found.</comment>');
119175
return;

Configuration/Testing/Settings.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,12 @@ Neos:
88
factoryObjectName: Neos\ContentRepository\BehavioralTests\TestSuite\Behavior\FakeUserIdProviderFactory
99
clock:
1010
factoryObjectName: Neos\ContentRepository\BehavioralTests\TestSuite\Behavior\FakeClockFactory
11+
12+
Neos:
13+
sites:
14+
'node-templates-site':
15+
uriPathSuffix: '.html'
16+
contentRepository: node_templates
17+
contentDimensions:
18+
resolver:
19+
factoryClassName: Neos\Neos\FrontendRouting\DimensionResolution\Resolver\AutoUriPathResolverFactory

Tests/Functional/Features/StandaloneValidationCommand/Snapshots/NodeTemplateValidateOutput.log

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
10 of 15 NodeType template validated. 5 could not be build standalone.
1+
Content repository "node_templates": 10 of 15 NodeType template validated. 5 could not be build standalone.
22

33
Flowpack.NodeTemplates:Content.DisallowedChildNodes
44
NodeConstraintException(Node type "Flowpack.NodeTemplates:Content.Text" is not allowed below tethered child nodes "content" of nodes of type "Flowpack.NodeTemplates:Content.DisallowedChildNodes", 1687541480146)

Tests/Functional/Features/StandaloneValidationCommand/StandaloneValidationCommandTest.php

Lines changed: 73 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,53 +5,56 @@
55
namespace Flowpack\NodeTemplates\Tests\Functional\Features\StandaloneValidationCommand;
66

77
use Flowpack\NodeTemplates\Application\Command\NodeTemplateCommandController;
8-
use Flowpack\NodeTemplates\Domain\NodeTemplateDumper\NodeTemplateDumper;
9-
use Flowpack\NodeTemplates\Domain\Template\RootTemplate;
8+
use Flowpack\NodeTemplates\Tests\Functional\ContentRepositoryTestTrait;
109
use Flowpack\NodeTemplates\Tests\Functional\FakeNodeTypeManagerTrait;
1110
use Flowpack\NodeTemplates\Tests\Functional\SnapshotTrait;
12-
use Neos\ContentRepository\Domain\Model\NodeInterface;
13-
use Neos\ContentRepository\Domain\Model\Workspace;
14-
use Neos\ContentRepository\Domain\Repository\ContentDimensionRepository;
15-
use Neos\ContentRepository\Domain\Repository\WorkspaceRepository;
16-
use Neos\ContentRepository\Domain\Service\ContextFactoryInterface;
17-
use Neos\ContentRepository\Domain\Service\NodeTypeManager;
11+
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
12+
use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint;
13+
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
14+
use Neos\ContentRepository\Core\Feature\RootNodeCreation\Command\CreateRootNodeAggregateWithNode;
15+
use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Command\CreateRootWorkspace;
16+
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
17+
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
18+
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
19+
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
20+
use Neos\ContentRepository\Core\SharedModel\Node\NodeName;
21+
use Neos\ContentRepository\Core\SharedModel\User\UserId;
22+
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
23+
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceDescription;
24+
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
25+
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle;
26+
use Neos\ContentRepository\TestSuite\Behavior\Features\Bootstrap\Helpers\FakeUserIdProvider;
1827
use Neos\Flow\Cli\Exception\StopCommandException;
1928
use Neos\Flow\Cli\Response;
20-
use Neos\Flow\Tests\FunctionalTestCase;
29+
use Neos\Flow\Core\Bootstrap;
30+
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
2131
use Neos\Neos\Domain\Model\Site;
2232
use Neos\Neos\Domain\Repository\SiteRepository;
2333
use Neos\Neos\Ui\Domain\Model\FeedbackCollection;
2434
use Neos\Utility\ObjectAccess;
35+
use PHPUnit\Framework\TestCase;
2536
use Symfony\Component\Console\Output\BufferedOutput;
2637

27-
final class StandaloneValidationCommandTest extends FunctionalTestCase
38+
final class StandaloneValidationCommandTest extends TestCase // we don't use Flows functional test case as it would reset the database afterwards
2839
{
2940
use SnapshotTrait;
41+
use ContentRepositoryTestTrait;
3042
use FakeNodeTypeManagerTrait;
3143

32-
protected static $testablePersistenceEnabled = true;
33-
34-
private ContextFactoryInterface $contextFactory;
35-
36-
protected NodeInterface $homePageNode;
37-
38-
protected NodeInterface $homePageMainContentCollectionNode;
39-
40-
private NodeTemplateDumper $nodeTemplateDumper;
41-
42-
private RootTemplate $lastCreatedRootTemplate;
44+
/**
45+
* Matching configuration in Neos.Neos.sites.node-templates-site
46+
*/
47+
private const TEST_SITE_NAME = 'node-templates-site';
4348

4449
private NodeTypeManager $nodeTypeManager;
4550

4651
private string $fixturesDir;
4752

53+
protected ObjectManagerInterface $objectManager;
54+
4855
public function setUp(): void
4956
{
50-
parent::setUp();
51-
52-
$this->nodeTypeManager = $this->objectManager->get(NodeTypeManager::class);
53-
54-
$this->loadFakeNodeTypes();
57+
$this->objectManager = Bootstrap::$staticObjectManager;
5558

5659
$this->setupContentRepository();
5760

@@ -61,51 +64,72 @@ public function setUp(): void
6164

6265
public function tearDown(): void
6366
{
64-
parent::tearDown();
65-
$this->inject($this->contextFactory, 'contextInstances', []);
6667
$this->objectManager->get(FeedbackCollection::class)->reset();
67-
$this->objectManager->forgetInstance(ContentDimensionRepository::class);
68-
$this->objectManager->forgetInstance(NodeTypeManager::class);
6968
}
7069

7170
private function setupContentRepository(): void
7271
{
73-
// Create an environment to create nodes.
74-
$this->objectManager->get(ContentDimensionRepository::class)->setDimensionsConfiguration([]);
72+
$this->initCleanContentRepository(ContentRepositoryId::fromString('node_templates'));
73+
74+
$this->nodeTypeManager = $this->contentRepository->getNodeTypeManager();
75+
$this->loadFakeNodeTypes();
7576

76-
$liveWorkspace = new Workspace('live');
77-
$workspaceRepository = $this->objectManager->get(WorkspaceRepository::class);
78-
$workspaceRepository->add($liveWorkspace);
77+
$liveWorkspaceCommand = CreateRootWorkspace::create(
78+
$workspaceName = WorkspaceName::fromString('live'),
79+
new WorkspaceTitle('Live'),
80+
new WorkspaceDescription('The live workspace'),
81+
ContentStreamId::fromString('cs-identifier')
82+
);
7983

80-
$testSite = new Site('test-site');
81-
$testSite->setSiteResourcesPackageKey('Test.Site');
82-
$siteRepository = $this->objectManager->get(SiteRepository::class);
83-
$siteRepository->add($testSite);
84+
$this->contentRepository->handle($liveWorkspaceCommand);
8485

85-
$this->persistenceManager->persistAll();
86-
$this->contextFactory = $this->objectManager->get(ContextFactoryInterface::class);
87-
$subgraph = $this->contextFactory->create(['workspaceName' => 'live']);
86+
FakeUserIdProvider::setUserId(UserId::fromString('initiating-user-identifier'));
8887

89-
$rootNode = $subgraph->getRootNode();
88+
$rootNodeCommand = CreateRootNodeAggregateWithNode::create(
89+
$workspaceName,
90+
$sitesId = NodeAggregateId::fromString('sites'),
91+
NodeTypeName::fromString('Neos.Neos:Sites')
92+
);
9093

91-
$sitesRootNode = $rootNode->createNode('sites');
92-
$testSiteNode = $sitesRootNode->createNode('test-site');
93-
$this->homePageNode = $testSiteNode->createNode(
94-
'homepage',
95-
$this->nodeTypeManager->getNodeType('Flowpack.NodeTemplates:Document.HomePage')
94+
$this->contentRepository->handle($rootNodeCommand);
95+
96+
$siteNodeCommand = CreateNodeAggregateWithNode::create(
97+
$workspaceName,
98+
NodeAggregateId::fromString('test-site'),
99+
NodeTypeName::fromString('Flowpack.NodeTemplates:Document.HomePage'),
100+
OriginDimensionSpacePoint::fromDimensionSpacePoint(
101+
DimensionSpacePoint::fromArray([])
102+
),
103+
$sitesId,
104+
nodeName: NodeName::fromString(self::TEST_SITE_NAME)
96105
);
106+
107+
$this->contentRepository->handle($siteNodeCommand);
97108
}
98109

99110
/** @test */
100111
public function itMatchesSnapshot()
101112
{
102113
$commandController = $this->objectManager->get(NodeTemplateCommandController::class);
103114

115+
$testSite = new Site(self::TEST_SITE_NAME);
116+
$testSite->setSiteResourcesPackageKey('Test.Site');
117+
118+
$siteRepositoryMock = $this->getMockBuilder(SiteRepository::class)->disableOriginalConstructor()->getMock();
119+
$siteRepositoryMock->expects(self::once())->method('findOneByNodeName')->willReturnCallback(function (string $nodeName) use ($testSite) {
120+
return $nodeName === $testSite->getNodeName()->value
121+
? $testSite
122+
: null;
123+
});
124+
125+
ObjectAccess::setProperty($commandController, 'siteRepository', $siteRepositoryMock, true);
126+
127+
104128
ObjectAccess::setProperty($commandController, 'response', $cliResponse = new Response(), true);
105129
ObjectAccess::getProperty($commandController, 'output', true)->setOutput($bufferedOutput = new BufferedOutput());
106130

107131
try {
108-
$commandController->validateCommand();
132+
$commandController->validateCommand(self::TEST_SITE_NAME);
109133
} catch (StopCommandException $e) {
110134
}
111135

phpstan.neon

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@ parameters:
33
paths:
44
- Classes
55
excludePaths:
6-
- Classes/Application/Command/NodeTemplateCommandController.php
76
- Classes/Domain/NodeTemplateDumper/NodeTemplateDumper.php

0 commit comments

Comments
 (0)