From 5ae58ecc939b4961aee285c57227302a9d68b3a0 Mon Sep 17 00:00:00 2001 From: Wilhelm Behncke Date: Mon, 22 Apr 2024 17:13:23 +0200 Subject: [PATCH] TASK: Ensure that all Publish-related command handlers throw `ConflictsOccurred` --- .../PublishChangesInDocumentCommand.php | 7 ++- ...PublishChangesInDocumentCommandHandler.php | 23 ++++++++++ .../PublishChangesInSiteCommand.php | 7 ++- .../PublishChangesInSiteCommandHandler.php | 45 ++++++++++++++----- .../Controller/BackendServiceController.php | 4 +- .../src/Endpoints/index.ts | 8 ++-- packages/neos-ui-sagas/src/Publish/index.ts | 5 ++- 7 files changed, 79 insertions(+), 20 deletions(-) diff --git a/Classes/Application/PublishChangesInDocument/PublishChangesInDocumentCommand.php b/Classes/Application/PublishChangesInDocument/PublishChangesInDocumentCommand.php index 87d9762511..f5c06a6616 100644 --- a/Classes/Application/PublishChangesInDocument/PublishChangesInDocumentCommand.php +++ b/Classes/Application/PublishChangesInDocument/PublishChangesInDocumentCommand.php @@ -14,6 +14,7 @@ namespace Neos\Neos\Ui\Application\PublishChangesInDocument; +use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -32,11 +33,12 @@ public function __construct( public ContentRepositoryId $contentRepositoryId, public WorkspaceName $workspaceName, public NodeAggregateId $documentId, + public ?DimensionSpacePoint $preferredDimensionSpacePoint, ) { } /** - * @param array $values + * @param array{contentRepositoryId:string,workspaceName:string,documentId:string,preferredDimensionSpacePoint?:array} $values */ public static function fromArray(array $values): self { @@ -44,6 +46,9 @@ public static function fromArray(array $values): self ContentRepositoryId::fromString($values['contentRepositoryId']), WorkspaceName::fromString($values['workspaceName']), NodeAggregateId::fromString($values['documentId']), + isset($values['preferredDimensionSpacePoint']) && !empty($values['preferredDimensionSpacePoint']) + ? DimensionSpacePoint::fromLegacyDimensionArray($values['preferredDimensionSpacePoint']) + : null, ); } } diff --git a/Classes/Application/PublishChangesInDocument/PublishChangesInDocumentCommandHandler.php b/Classes/Application/PublishChangesInDocument/PublishChangesInDocumentCommandHandler.php index 3b7393c692..12950e2592 100644 --- a/Classes/Application/PublishChangesInDocument/PublishChangesInDocumentCommandHandler.php +++ b/Classes/Application/PublishChangesInDocument/PublishChangesInDocumentCommandHandler.php @@ -14,9 +14,13 @@ namespace Neos\Neos\Ui\Application\PublishChangesInDocument; +use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\WorkspaceRebaseFailed; use Neos\ContentRepository\Core\SharedModel\Exception\NodeAggregateCurrentlyDoesNotExist; +use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; use Neos\Neos\Domain\Workspace\WorkspaceProvider; +use Neos\Neos\Ui\Application\Shared\Conflicts; +use Neos\Neos\Ui\Application\Shared\ConflictsOccurred; use Neos\Neos\Ui\Application\Shared\PublishSucceeded; /** @@ -28,6 +32,9 @@ #[Flow\Scope("singleton")] final class PublishChangesInDocumentCommandHandler { + #[Flow\Inject] + protected ContentRepositoryRegistry $contentRepositoryRegistry; + #[Flow\Inject] protected WorkspaceProvider $workspaceProvider; @@ -52,6 +59,22 @@ public function handle(PublishChangesInDocumentCommand $command): PublishSucceed 'Node could not be published, probably because of a missing parentNode. Please check that the parentNode has been published.', 1682762156 ); + } catch (WorkspaceRebaseFailed $e) { + $conflictsBuilder = Conflicts::builder( + contentRepository: $this->contentRepositoryRegistry + ->get($command->contentRepositoryId), + workspaceName: $command->workspaceName, + preferredDimensionSpacePoint: $command->preferredDimensionSpacePoint + ); + + foreach ($e->commandsThatFailedDuringRebase as $commandThatFailedDuringRebase) { + $conflictsBuilder->addCommandThatFailedDuringRebase($commandThatFailedDuringRebase); + } + + throw new ConflictsOccurred( + $conflictsBuilder->build(), + 1712832228 + ); } } } diff --git a/Classes/Application/PublishChangesInSite/PublishChangesInSiteCommand.php b/Classes/Application/PublishChangesInSite/PublishChangesInSiteCommand.php index 0f5b82abde..f177482c41 100644 --- a/Classes/Application/PublishChangesInSite/PublishChangesInSiteCommand.php +++ b/Classes/Application/PublishChangesInSite/PublishChangesInSiteCommand.php @@ -14,6 +14,7 @@ namespace Neos\Neos\Ui\Application\PublishChangesInSite; +use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -32,11 +33,12 @@ public function __construct( public ContentRepositoryId $contentRepositoryId, public WorkspaceName $workspaceName, public NodeAggregateId $siteId, + public ?DimensionSpacePoint $preferredDimensionSpacePoint, ) { } /** - * @param array $values + * @param array{contentRepositoryId:string,workspaceName:string,siteId:string,preferredDimensionSpacePoint?:array} $values */ public static function fromArray(array $values): self { @@ -44,6 +46,9 @@ public static function fromArray(array $values): self ContentRepositoryId::fromString($values['contentRepositoryId']), WorkspaceName::fromString($values['workspaceName']), NodeAggregateId::fromString($values['siteId']), + isset($values['preferredDimensionSpacePoint']) && !empty($values['preferredDimensionSpacePoint']) + ? DimensionSpacePoint::fromLegacyDimensionArray($values['preferredDimensionSpacePoint']) + : null, ); } } diff --git a/Classes/Application/PublishChangesInSite/PublishChangesInSiteCommandHandler.php b/Classes/Application/PublishChangesInSite/PublishChangesInSiteCommandHandler.php index 63710ca355..ad83969e8c 100644 --- a/Classes/Application/PublishChangesInSite/PublishChangesInSiteCommandHandler.php +++ b/Classes/Application/PublishChangesInSite/PublishChangesInSiteCommandHandler.php @@ -14,8 +14,12 @@ namespace Neos\Neos\Ui\Application\PublishChangesInSite; +use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\WorkspaceRebaseFailed; +use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; use Neos\Neos\Domain\Workspace\WorkspaceProvider; +use Neos\Neos\Ui\Application\Shared\Conflicts; +use Neos\Neos\Ui\Application\Shared\ConflictsOccurred; use Neos\Neos\Ui\Application\Shared\PublishSucceeded; /** @@ -27,20 +31,41 @@ #[Flow\Scope("singleton")] final class PublishChangesInSiteCommandHandler { + #[Flow\Inject] + protected ContentRepositoryRegistry $contentRepositoryRegistry; + #[Flow\Inject] protected WorkspaceProvider $workspaceProvider; public function handle(PublishChangesInSiteCommand $command): PublishSucceeded { - $workspace = $this->workspaceProvider->provideForWorkspaceName( - $command->contentRepositoryId, - $command->workspaceName - ); - $publishingResult = $workspace->publishChangesInSite($command->siteId); - - return new PublishSucceeded( - numberOfAffectedChanges: $publishingResult->numberOfPublishedChanges, - baseWorkspaceName: $workspace->getCurrentBaseWorkspaceName()?->value - ); + try { + $workspace = $this->workspaceProvider->provideForWorkspaceName( + $command->contentRepositoryId, + $command->workspaceName + ); + $publishingResult = $workspace->publishChangesInSite($command->siteId); + + return new PublishSucceeded( + numberOfAffectedChanges: $publishingResult->numberOfPublishedChanges, + baseWorkspaceName: $workspace->getCurrentBaseWorkspaceName()?->value + ); + } catch (WorkspaceRebaseFailed $e) { + $conflictsBuilder = Conflicts::builder( + contentRepository: $this->contentRepositoryRegistry + ->get($command->contentRepositoryId), + workspaceName: $command->workspaceName, + preferredDimensionSpacePoint: $command->preferredDimensionSpacePoint + ); + + foreach ($e->commandsThatFailedDuringRebase as $commandThatFailedDuringRebase) { + $conflictsBuilder->addCommandThatFailedDuringRebase($commandThatFailedDuringRebase); + } + + throw new ConflictsOccurred( + $conflictsBuilder->build(), + 1712832228 + ); + } } } diff --git a/Classes/Controller/BackendServiceController.php b/Classes/Controller/BackendServiceController.php index a00da6f5c0..b093d1f514 100644 --- a/Classes/Controller/BackendServiceController.php +++ b/Classes/Controller/BackendServiceController.php @@ -203,7 +203,7 @@ public function changeAction(array $changes): void /** * Publish all changes in the current site * - * @phpstan-param array $command + * @phpstan-param array{workspaceName:string,siteId:string,preferredDimensionSpacePoint?:array} $command */ public function publishChangesInSiteAction(array $command): void { @@ -236,7 +236,7 @@ public function publishChangesInSiteAction(array $command): void /** * Publish all changes in the current document * - * @phpstan-param array $command + * @phpstan-param array{workspaceName:string,documentId:string,preferredDimensionSpacePoint?:array} $command */ public function publishChangesInDocumentAction(array $command): void { diff --git a/packages/neos-ui-backend-connector/src/Endpoints/index.ts b/packages/neos-ui-backend-connector/src/Endpoints/index.ts index 1bcfed2c08..aa2130b77d 100644 --- a/packages/neos-ui-backend-connector/src/Endpoints/index.ts +++ b/packages/neos-ui-backend-connector/src/Endpoints/index.ts @@ -69,7 +69,7 @@ export default (routes: Routes) => { })).then(response => fetchWithErrorHandling.parseJson(response)) .catch(reason => fetchWithErrorHandling.generalErrorHandler(reason)); - const publishChangesInSite = (siteId: NodeContextPath, workspaceName: WorkspaceName) => fetchWithErrorHandling.withCsrfToken(csrfToken => ({ + const publishChangesInSite = (siteId: NodeContextPath, workspaceName: WorkspaceName, preferredDimensionSpacePoint: null|DimensionCombination) => fetchWithErrorHandling.withCsrfToken(csrfToken => ({ url: routes.ui.service.publishChangesInSite, method: 'POST', credentials: 'include', @@ -78,12 +78,12 @@ export default (routes: Routes) => { 'Content-Type': 'application/json' }, body: JSON.stringify({ - command: {siteId, workspaceName} + command: {siteId, workspaceName, preferredDimensionSpacePoint} }) })).then(response => fetchWithErrorHandling.parseJson(response)) .catch(reason => fetchWithErrorHandling.generalErrorHandler(reason)); - const publishChangesInDocument = (documentId: NodeContextPath, workspaceName: WorkspaceName) => fetchWithErrorHandling.withCsrfToken(csrfToken => ({ + const publishChangesInDocument = (documentId: NodeContextPath, workspaceName: WorkspaceName, preferredDimensionSpacePoint: null|DimensionCombination) => fetchWithErrorHandling.withCsrfToken(csrfToken => ({ url: routes.ui.service.publishChangesInDocument, method: 'POST', credentials: 'include', @@ -92,7 +92,7 @@ export default (routes: Routes) => { 'Content-Type': 'application/json' }, body: JSON.stringify({ - command: {documentId, workspaceName} + command: {documentId, workspaceName, preferredDimensionSpacePoint} }) })).then(response => fetchWithErrorHandling.parseJson(response)) .catch(reason => fetchWithErrorHandling.generalErrorHandler(reason)); diff --git a/packages/neos-ui-sagas/src/Publish/index.ts b/packages/neos-ui-sagas/src/Publish/index.ts index efce72eed8..e3c29fdaba 100644 --- a/packages/neos-ui-sagas/src/Publish/index.ts +++ b/packages/neos-ui-sagas/src/Publish/index.ts @@ -10,7 +10,7 @@ import {put, call, select, takeEvery, take, race, all} from 'redux-saga/effects'; import {AnyError} from '@neos-project/neos-ui-error'; -import {NodeContextPath, WorkspaceName} from '@neos-project/neos-ts-interfaces'; +import {DimensionCombination, NodeContextPath, WorkspaceName} from '@neos-project/neos-ts-interfaces'; import {actionTypes, actions, selectors} from '@neos-project/neos-ui-redux-store'; import {GlobalState} from '@neos-project/neos-ui-redux-store/src/System'; import {FeedbackEnvelope} from '@neos-project/neos-ui-redux-store/src/ServerFeedback'; @@ -83,6 +83,7 @@ export function * watchPublishing({routes}: {routes: Routes}) { const {ancestorIdSelector} = SELECTORS_BY_SCOPE[scope]; const workspaceName: WorkspaceName = yield select(selectors.CR.Workspaces.personalWorkspaceNameSelector); + const dimensionSpacePoint: null|DimensionCombination = yield select(selectors.CR.ContentDimensions.active); const ancestorId: NodeContextPath = ancestorIdSelector ? yield select(ancestorIdSelector) : null; @@ -92,7 +93,7 @@ export function * watchPublishing({routes}: {routes: Routes}) { window.addEventListener('beforeunload', handleWindowBeforeUnload); const result: PublishingResponse = scope === PublishingScope.ALL ? yield call(endpoint as any, workspaceName) - : yield call(endpoint, ancestorId, workspaceName); + : yield call(endpoint, ancestorId, workspaceName, dimensionSpacePoint); if ('success' in result) { yield put(actions.CR.Publishing.succeed(result.success.numberOfAffectedChanges));