Skip to content

Changed how exceptions are handled in InstantBook #260

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

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from

Conversation

tuj
Copy link
Contributor

@tuj tuj commented Aug 6, 2025

Link to issue

#257

Link to ticket

https://leantime.itkdev.dk/#/tickets/showTicket/4992

Description

Exceptions are not handled correctly for the InstantBook feature. This results in 500 errors.
This PR aims to handle the exceptions in a better way.
If an error is encountered when requesting booking options, an empty array of options is returned instead of an exception.

Checklist

  • My code is covered by test cases.
  • My code passes our test (all our tests).
  • My code passes our static analysis suite.
  • My code passes our continuous integration process.

@tuj tuj self-assigned this Aug 6, 2025
@tuj tuj added the bug Something isn't working label Aug 6, 2025
@turegjorup
Copy link
Contributor

Take a look at using Exception to status

The framework also allows you to configure the HTTP status code sent to the clients when custom exceptions are thrown on an API Platform resource operation.

This can potentially save some code in the controller.

@tuj tuj marked this pull request as ready for review August 20, 2025 07:08
@tuj tuj requested a review from turegjorup August 20, 2025 08:07
try {
$interactionRequest = $this->interactiveSlideService->parseRequestBody($requestBody);
} catch (InteractiveSlideException $e) {
throw new HttpException($e->getCode(), $e->getMessage());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use API Platforms Exception to status. That way we have one line of config instead of having to catch the domain exceptions multiple places.

try {
$actionResult = $this->interactiveSlideService->performAction($user, $slide, $interactionRequest);
} catch (InteractiveSlideException $e) {
throw new HttpException($e->getCode(), $e->getMessage());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use "Exception to status" as above


$user = $this->security->getUser();

if (!($user instanceof User || $user instanceof ScreenUser)) {
throw new NotFoundException('User not found');
throw new NotFoundHttpException('User not found');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Questions:

  • We only have these two user types, so how is this even relevant?
  • If it is relevant it seems "Access denied" is the proper exception to throw
  • Should this only be allowed for ScreenUsers or are there legitimate use cases for normal users or service accounts?

@@ -90,7 +88,7 @@ public function performAction(UserInterface $user, Slide $slide, InteractionSlid
return match ($interactionRequest->action) {
self::ACTION_GET_QUICK_BOOK_OPTIONS => $this->getQuickBookOptions($slide, $interactionRequest),
self::ACTION_QUICK_BOOK => $this->quickBook($slide, $interactionRequest),
default => throw new BadRequestHttpException('Action not allowed'),
default => throw new InteractiveSlideException('Action not supported', 400),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
default => throw new InteractiveSlideException('Action not supported', 400),
default => throw new InteractiveSlideException('Action not supported'),

I don't think domain services should ever implement HTTP specifics. So it's good that we switch from BadRequestHttpException to a custom domain exception. But bad that we retain 400 as the error code given that it's HTTP specific. So without the HTTP context it's just a magic number.

At a minimum use Response::HTTP_BAD_REQUEST constant to indicate context.

Better to not use code and just let the domain exception bubble up, then handle it in config with "exception to status" as described in comment on controller

@@ -105,7 +103,7 @@ private function authenticate(array $configuration): array
$password = $this->keyValueService->getValue($configuration['password']);

if (4 !== count(array_filter([$tenantId, $clientId, $username, $password]))) {
throw new BadRequestHttpException('tenantId, clientId, username, password must all be set.');
throw new InteractiveSlideException('tenantId, clientId, username, password must all be set.', 400);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw new InteractiveSlideException('tenantId, clientId, username, password must all be set.', 400);
throw new InteractiveSlideException('tenantId, clientId, username, password must all be set.');

private function getValueFromInterval(string $key, InteractionSlideRequest $interactionRequest): string|int
{
$interval = $interactionRequest->data['interval'] ?? null;

if (null === $interval) {
throw new BadRequestHttpException('interval not set.');
throw new InteractiveSlideException('interval not set.', 400);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw new InteractiveSlideException('interval not set.', 400);
throw new InteractiveSlideException('interval not set.');

}

$value = $interval[$key] ?? null;

if (null === $value) {
throw new BadRequestHttpException("interval.'.$key.' not set.");
throw new InteractiveSlideException("interval.'.$key.' not set.", 400);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw new InteractiveSlideException("interval.'.$key.' not set.", 400);
throw new InteractiveSlideException("interval.'.$key.' not set.");

@@ -468,13 +501,13 @@ private function getAllowedResources(InteractiveSlide $interactive): array
$key = $configuration['resourceEndpoint'] ?? null;

if (null === $key) {
throw new \Exception('resourceEndpoint not set');
throw new InteractiveSlideException('resourceEndpoint not set', 400);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw new InteractiveSlideException('resourceEndpoint not set', 400);
throw new InteractiveSlideException('resourceEndpoint not set');

}

$resourceEndpoint = $this->keyValueService->getValue($key);

if (null === $resourceEndpoint) {
throw new \Exception('resourceEndpoint value not set');
throw new InteractiveSlideException('resourceEndpoint value not set', 400);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw new InteractiveSlideException('resourceEndpoint value not set', 400);
throw new InteractiveSlideException('resourceEndpoint value not set');

@@ -453,7 +486,7 @@ private function checkPermission(InteractiveSlide $interactive, string $resource
$allowedResources = $this->getAllowedResources($interactive);

if (!in_array($resource, $allowedResources)) {
throw new \Exception('Not allowed');
throw new InteractiveSlideException('Not allowed', 403);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw new InteractiveSlideException('Not allowed', 403);
throw new InteractiveSlidePermissionException('Not allowed');

Then create InteractiveSlidePermissionException and map it to 403 Forbidden

@tuj tuj requested a review from turegjorup August 21, 2025 09:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants