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

TASK: Adjust to Flow Controller changes #4738

Merged
merged 14 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ jobs:
git -C ../${{ env.NEOS_FOLDER }} checkout -b build
composer config repositories.neos '{ "type": "path", "url": "../${{ env.NEOS_FOLDER }}", "options": { "symlink": false } }'
composer require --no-update neos/neos-development-collection:"dev-build as ${{ env.NEOS_BRANCH_ALIAS }}"
# enable this line to require a specific flow version for testing
# composer require --no-update neos/flow-development-collection:"dev-your-flow-branch-name as ${{ env.NEOS_BRANCH_ALIAS }}"

- name: Cache Composer packages
id: composer-cache
Expand Down
3 changes: 2 additions & 1 deletion Neos.Fusion/Classes/Core/Runtime.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Mvc\ActionResponse;
use Neos\Flow\Mvc\Exception\StopActionException;
use Neos\Flow\Mvc\Exception\ForwardException;
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
use Neos\Flow\Security\Exception as SecurityException;
use Neos\Fusion\Core\Cache\RuntimeContentCache;
Expand Down Expand Up @@ -478,7 +479,7 @@ public function evaluate(string $fusionPath, $contextObject = null, string $beha
// fast path for expression or value
try {
return $this->evaluateExpressionOrValueInternal($fusionPath, $fusionConfiguration, $contextObject);
} catch (StopActionException | SecurityException | RuntimeException $exception) {
} catch (StopActionException | ForwardException | SecurityException | RuntimeException $exception) {
throw $exception;
} catch (\Exception $exception) {
return $this->handleRenderingException($fusionPath, $exception, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* source code.
*/

use GuzzleHttp\Psr7\Response;
use Neos\Flow\Exception;
use Neos\Flow\Mvc\Exception\StopActionException;
use Neos\Flow\Tests\UnitTestCase;
Expand Down Expand Up @@ -99,7 +100,7 @@ public function unpackRuntimeException()
public function neverHandleStopActionException()
{
$this->expectException(StopActionException::class);
$this->handler->handleRenderingException('path', new StopActionException());
$this->handler->handleRenderingException('path', StopActionException::createForResponse(new Response(), ''));
}


Expand Down
19 changes: 9 additions & 10 deletions Neos.Neos/Classes/Controller/Backend/ModuleController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
namespace Neos\Neos\Controller\Backend;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\ActionResponse;
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\Mvc\Dispatcher;
use Neos\Flow\Security\Account;
Expand Down Expand Up @@ -109,19 +108,19 @@ public function indexAction(array $module)

$moduleRequest->setArgument('__moduleConfiguration', $moduleConfiguration);

$moduleResponse = new ActionResponse();
$moduleResponse = $this->dispatcher->dispatch($moduleRequest);

$this->dispatcher->dispatch($moduleRequest, $moduleResponse);
$moduleResponse->mergeIntoParentResponse($this->response);

if ($moduleResponse->getRedirectUri() !== null) {
$this->redirectToUri($moduleResponse->getRedirectUri(), 0, $moduleResponse->getStatusCode());
if ($moduleResponse->hasHeader('Location')) {
// Preserve redirects see b57d72aeeaa2e6da4d9c0a80363025fefd63d813
return $moduleResponse;
} elseif ($moduleRequest->getFormat() !== 'html') {
// Allow ajax request with json or similar dd7e5c99924bf1b8618775bec08cc4f2cb1a6d2a
// todo just return $moduleResponse and trust its content-type instead of inferring the requested content-type
$mediaType = MediaTypes::getMediaTypeFromFilename('file.' . $moduleRequest->getFormat());
if ($mediaType !== 'application/octet-stream') {
$this->controllerContext->getResponse()->setContentType($mediaType);
$moduleResponse = $moduleResponse->withHeader('Content-Type', $mediaType);
}
return $moduleResponse->getContent();
return $moduleResponse;
} else {
/** @var ?Account $authenticatedAccount */
$authenticatedAccount = $this->securityContext->getAccount();
Expand All @@ -131,7 +130,7 @@ public function indexAction(array $module)

$this->view->assignMultiple([
'moduleClass' => implode('-', $modules),
'moduleContents' => $moduleResponse->getContent(),
'moduleContents' => $moduleResponse->getBody()->getContents(),
'title' => $moduleRequest->hasArgument('title')
? $moduleRequest->getArgument('title')
: $moduleConfiguration['label'],
Expand Down
16 changes: 11 additions & 5 deletions Neos.Neos/Classes/Fusion/PluginImplementation.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,21 @@ public function evaluate(): string
$this->node = $currentContext['node'];
$this->documentNode = $currentContext['documentNode'];
$parentResponse = $this->runtime->getControllerContext()->getResponse();
$pluginResponse = new ActionResponse();
$this->dispatcher->dispatch($this->buildPluginRequest(), $pluginResponse);
$pluginResponse = $this->dispatcher->dispatch($this->buildPluginRequest());

// We need to make sure to not merge content up into the parent ActionResponse
// because that would break the Fusion HttpResponse.
$content = $pluginResponse->getContent();
$pluginResponse->setContent('');
$content = $pluginResponse->getBody()->getContents();

$pluginResponse->mergeIntoParentResponse($parentResponse);
// hacky, but part of the deal. We have to manipulate the global response to redirect for example.
// transfer possible headers that have been set dynamically
foreach ($pluginResponse->getHeaders() as $name => $values) {
$parentResponse->setHttpHeader($name, $values);
}
// if the status code is 200 we assume it's the default and will not overrule it
if ($pluginResponse->getStatusCode() !== 200) {
$parentResponse->setStatusCode($pluginResponse->getStatusCode());
}

return $content;
}
Expand Down
35 changes: 18 additions & 17 deletions Neos.Neos/Classes/Service/Controller/AbstractServiceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,19 @@

namespace Neos\Neos\Service\Controller;

use GuzzleHttp\Psr7\Response;
use Neos\ContentRepository\Core\SharedModel\User\UserId;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Exception as FlowException;
use Neos\Flow\Log\ThrowableStorageInterface;
use Neos\Flow\Log\Utility\LogEnvironment;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Mvc\ActionResponse;
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\Mvc\Exception\ForwardException;
use Neos\Flow\Mvc\Exception\StopActionException;
use Neos\Neos\Controller\BackendUserTranslationTrait;
use Neos\Neos\Domain\Service\UserService;
use Psr\Http\Message\ResponseInterface;

/**
* Abstract Service Controller
Expand Down Expand Up @@ -84,36 +86,35 @@ protected function errorAction(): never
/**
* Catch exceptions while processing an exception and respond to JSON format
* TODO: This is an explicit exception handling that will be replaced by format-enabled exception handlers.
*
* @param ActionRequest $request The request object
* @param ActionResponse $response The response, modified by this handler
* @return void
* @throws StopActionException
* @throws \Exception
*/
public function processRequest(ActionRequest $request, ActionResponse $response)
public function processRequest(ActionRequest $request): ResponseInterface
{
try {
parent::processRequest($request, $response);
} catch (StopActionException $exception) {
$response = parent::processRequest($request);
} catch (StopActionException | ForwardException $exception) {
throw $exception;
} catch (\Exception $exception) {
if ($this->request->getFormat() !== 'json') {
throw $exception;
}
$exceptionData = $this->convertException($exception);
$response->setContentType('application/json');
if ($exception instanceof FlowException) {
$response->setStatusCode($exception->getStatusCode());
} else {
$response->setStatusCode(500);
}
$response->setContent(json_encode(['error' => $exceptionData], JSON_THROW_ON_ERROR));
$body = json_encode(['error' => $exceptionData], JSON_THROW_ON_ERROR);
$response = new Response(
status: $exception instanceof FlowException
? $exception->getStatusCode()
: 500,
headers: [
'Content-Type' => 'application/json'
],
body: $body
);
$this->logger->error(
$this->throwableStorage2->logThrowable($exception),
LogEnvironment::fromMethodName(__METHOD__)
);
}

return $response;
}

/**
Expand Down
8 changes: 4 additions & 4 deletions Neos.Neos/Tests/Unit/Fusion/PluginImplementationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
* source code.
*/

use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Uri;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Mvc\ActionResponse;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Neos\Flow\Mvc\Dispatcher;
use Neos\Flow\Mvc\RequestInterface;
use Neos\Flow\Tests\UnitTestCase;
use Neos\Fusion\Core\Runtime;
use Neos\Neos\Fusion\PluginImplementation;
Expand Down Expand Up @@ -54,7 +54,7 @@ class PluginImplementationTest extends UnitTestCase
protected $mockHttpRequest;

/**
* @var MockObject|RequestInterface
* @var MockObject|ActionRequest
*/
protected $mockActionRequest;

Expand Down Expand Up @@ -132,8 +132,8 @@ public function evaluateSetHeaderIntoParent(string $message, array $input, array
$this->_setHeadersIntoResponse($parentResponse, $input['parent']);
$this->mockControllerContext->method('getResponse')->willReturn($parentResponse);

$this->mockDispatcher->method('dispatch')->willReturnCallback(function (ActionRequest $request, ActionResponse $response) use ($input) {
$this->_setHeadersIntoResponse($response, $input['plugin']);
$this->mockDispatcher->method('dispatch')->willReturnCallback(function (ActionRequest $request) use ($input) {
return new Response(headers: $input['plugin']);
});

$this->mockRuntime->expects($this->any())->method('getCurrentContext')->willReturn(['node' => null, 'documentNode' => null]);
Expand Down
Loading