From 7b158d57b5b43f115bb93dd37b9c1672bbab9357 Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Sat, 24 May 2025 22:09:48 +0200 Subject: [PATCH] API improvements (#5494) * hide logo in docs UI * always return a user in api base controller * added example for duration fields --- phpstan.neon | 24 ------------------- src/API/BaseApiController.php | 13 +++++++--- src/Form/Type/DurationType.php | 5 ++++ src/Form/Type/UserType.php | 15 +++++++++--- .../Stoplight/index.html.twig | 10 ++++++++ 5 files changed, 37 insertions(+), 30 deletions(-) create mode 100644 templates/bundles/NelmioApiDocBundle/Stoplight/index.html.twig diff --git a/phpstan.neon b/phpstan.neon index fd43dfba..832db819 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -42,10 +42,6 @@ parameters: containerXmlPath: %rootDir%/../../../var/cache/dev/App_KernelDevDebugContainer.xml ignoreErrors: - '#^Method .*\(\) has parameter \$builder with generic interface Symfony\\Component\\Form\\FormBuilderInterface but does not specify its types\: TData$#' - - - message: "#^Parameter \\#1 \\$user of class App\\\\Event\\\\PageActionsEvent constructor expects App\\\\Entity\\\\User, App\\\\Entity\\\\User\\|null given\\.$#" - count: 4 - path: src/API/ActionsController.php - message: "#^Parameter \\#1 \\$name of method App\\\\Entity\\\\Activity\\:\\:getMetaField\\(\\) expects string, mixed given\\.$#" @@ -62,11 +58,6 @@ parameters: count: 1 path: src/API/Authentication/TokenAuthenticator.php - - - message: "#^Parameter \\#1 \\$user of static method App\\\\Timesheet\\\\DateTimeFactory\\:\\:createByUser\\(\\) expects App\\\\Entity\\\\User, App\\\\Entity\\\\User\\|null given\\.$#" - count: 1 - path: src/API/BaseApiController.php - - message: "#^Parameter \\#1 \\$name of method App\\\\Entity\\\\Customer\\:\\:getMetaField\\(\\) expects string, mixed given\\.$#" count: 1 @@ -137,21 +128,6 @@ parameters: count: 3 path: src/API/TimesheetController.php - - - message: "#^Parameter \\#1 \\$user of class App\\\\Event\\\\RecentActivityEvent constructor expects App\\\\Entity\\\\User, App\\\\Entity\\\\User\\|null given\\.$#" - count: 1 - path: src/API/TimesheetController.php - - - - message: "#^Parameter \\#1 \\$user of method App\\\\Repository\\\\TimesheetRepository\\:\\:getRecentActivities\\(\\) expects App\\\\Entity\\\\User, App\\\\Entity\\\\User\\|null given\\.$#" - count: 1 - path: src/API/TimesheetController.php - - - - message: "#^Cannot call method getId\\(\\) on App\\\\Entity\\\\User\\|null\\.$#" - count: 1 - path: src/API/UserController.php - - message: "#^Parameter \\#2 \\$statistic of class App\\\\Event\\\\ActivityStatisticEvent constructor expects App\\\\Model\\\\ActivityStatistic, App\\\\Model\\\\ActivityStatistic\\|null given\\.$#" count: 1 diff --git a/src/API/BaseApiController.php b/src/API/BaseApiController.php index ab57f2b0..4a9a6b16 100644 --- a/src/API/BaseApiController.php +++ b/src/API/BaseApiController.php @@ -20,15 +20,22 @@ use Symfony\Component\Form\Extension\Core\Type\DateTimeType; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormTypeInterface; -/** - * @method null|User getUser() - */ abstract class BaseApiController extends AbstractController { public const DATE_ONLY_FORMAT = 'yyyy-MM-dd'; public const DATE_FORMAT = DateTimeType::HTML5_FORMAT; public const DATE_FORMAT_PHP = 'Y-m-d\TH:i:s'; + protected function getUser(): User + { + $user = parent::getUser(); + if (!$user instanceof User) { + throw $this->createAccessDeniedException('Need a user for API access'); + } + + return $user; + } + /** * @template TFormType of FormTypeInterface * @template TData of BaseQuery diff --git a/src/Form/Type/DurationType.php b/src/Form/Type/DurationType.php index 41789c75..5b869c2e 100644 --- a/src/Form/Type/DurationType.php +++ b/src/Form/Type/DurationType.php @@ -33,6 +33,11 @@ final class DurationType extends AbstractType 'toggle' => false, 'max_hours' => 24, 'icon' => 'clock', + 'documentation' => [ + 'type' => 'string', + 'description' => 'Duration - supports various formats: https://www.kimai.org/documentation/duration-format.html', + 'example' => '01:30', + ] ]); $resolver->setAllowedTypes('max_hours', 'int'); } diff --git a/src/Form/Type/UserType.php b/src/Form/Type/UserType.php index 0549550d..8d1c018c 100644 --- a/src/Form/Type/UserType.php +++ b/src/Form/Type/UserType.php @@ -70,11 +70,20 @@ final class UserType extends AbstractType // includes the current user if it is a system-account, which is especially useful for forms pages, // which have a user switcher and display the logged-in user by default 'include_current_user_if_system_account' => false, - 'documentation' => [ + ]); + + $resolver->setDefault('documentation', function (Options $options) { + $example = 0; + if ($options['user'] instanceof User) { + $example = $options['user']->getId(); + } + + return [ 'type' => 'integer', 'description' => 'User ID', - ], - ]); + 'example' => $example + ]; + }); $resolver->setDefault('choices', function (Options $options) { $query = new UserFormTypeQuery(); diff --git a/templates/bundles/NelmioApiDocBundle/Stoplight/index.html.twig b/templates/bundles/NelmioApiDocBundle/Stoplight/index.html.twig new file mode 100644 index 00000000..5bd786d7 --- /dev/null +++ b/templates/bundles/NelmioApiDocBundle/Stoplight/index.html.twig @@ -0,0 +1,10 @@ +{% extends '@!NelmioApiDoc/Stoplight/index.html.twig' %} + +{% block stylesheets %} + {{ parent() }} + +{% endblock stylesheets %} \ No newline at end of file